Skip to content

Stable API

ntpstats.api is the public surface you can build on: scripts, notebooks, papers and other packages. Everything listed on this page keeps working across minor releases. If a name or a parameter has to change, it first goes through a deprecation period (below).

from ntpstats import api as nt

s = nt.load_one("measurements.log", peer="192.0.2.10")
for r in nt.series_stability(s, kinds=("oadev", "tdev"), ci=0.95):
    print(r.kind, r.taus, r.dev, r.lo, r.hi)
df = s.to_pandas()                        # optional: pip install "ntpstats[data]"

The objects are the same as in their home modules (nt.TimeSeries is ntpstats.series.TimeSeries), so existing imports such as from ntpstats.stability import compute keep working. The module pages in this reference document the details.

What "stable" means

  • A name listed here is not removed or renamed, its parameters keep their names and order, and optional parameters stay optional, unless at least one full minor release of NtpstatsDeprecationWarning came first. The warning says when the name goes away and what to use instead; the removal is listed under Removed in the changelog.
  • New optional parameters, new names, new result fields and new methods can arrive in any minor release.
  • Numbers can change when a bug is fixed or an estimator is made more accurate; such changes are listed under Fixed or Changed, with the validation that supports them.
  • NtpstatsDeprecationWarning is a FutureWarning, so it is shown by default in scripts and notebooks. To find deprecated calls in your own tests: pytest -W error::ntpstats.deprecation.NtpstatsDeprecationWarning.

The surface is frozen in tests/data/api_surface.json; CI fails on any incompatible change.

Provisional: everything that is not on this page. The protocol clients (ntpstats.sntp, ntpstats.nts, ntpstats.roughtime) follow IETF drafts that still change, and the web UI, exporters and per-format parser functions are internal. They work and are documented, but may change in a minor release (always noted in the changelog).

Data and input

Name What it is
TimeSeries offset series: t (POSIX s), offset (s, reference − local), extra, meta (series)
load, load_one read any supported log, capture or file; auto-detected (parsers)
load_large, iter_chunks read very large text logs in blocks with bounded memory (see below)
detect_format, all_formats format detection; every built-in and plugin format
ParseError raised for unreadable input
load_profile an instrument import profile (profiles)
to_pandas, from_pandas, read_parquet, write_parquet dataframes and Parquet (adapters)

Stability

Name What it is
KINDS the statistics: ADEV, MDEV, TDEV, HDEV, totals, Theo, MTIE, TIErms
compute, compute_many, series_stability deviations with confidence intervals and noise ID (stability)
dynamic sliding-window stability
StabilityResult, DynamicResult results (to_dataframe(), to_xarray())
edf, chi2_interval, identify_noise degrees of freedom, intervals, power-law noise identification (edf)

Analysis and network

Name What it is
summary, compare, detrend, remove_outliers, format_seconds (analysis)
delay_stats, wedge, floor_packet_percentage, min_delay_filter (network)

Metrology

Name What it is
series_spectrum, Spectrum phase/frequency PSD and L(f) (spectrum)
fit_noise, NoiseFit power-law noise model h₋₂…h₂ with intervals (ntpstats.noisefit.fit_series, noisefit)
hat_series, HatResult N-cornered hat and Groslambert covariance (hat)
holdover_series, HoldoverResult holdover prediction (holdover)

Time error, masks and assurance

Name What it is
time_error, TimeErrorResult, check_time_error max|TE|, cTE, dTE and limits (ntpstats.timeerror.check, timeerror)
Mask, load_mask, check_mask stability limit masks (ntpstats.masks.check, masks)
audit, AuditConfig UTC traceability bound and evidence (audit)
detect_events, Event steps, spikes, frequency and route changes (ntpstats.events.detect, events)
parse_bounds, validate_bounds clock-error bound validation (ntpstats.bounds.validate, bounds)

Estimators, simulation and the bench

Name What it is
Estimator, FunctionEstimator the estimator interface (estimators)
register_estimator, get_estimator, available_estimators, run_estimator registry (ntpstats.estimators.register, get, available, run)
kalman_series Kalman filter and RTS smoother (filters)
Scenario, ClockModel, PathModel, PathEvent, ServerSpec, simulate_ntp, simulate_multi simulator with ground truth (simulate)
load_scenarios, run_bench, score benchmark runner (bench)

Reports and extension points

Name What it is
dataset_report, bench_report self-contained HTML reports (report)
ParserPlugin, DetectorPlugin plugin types (plugins)
NtpstatsDeprecationWarning the deprecation warning

Large files

load reads text logs above 256 MB in blocks by itself. To control the block size or to process blocks one at a time:

for block in nt.iter_chunks("/var/log/ntpstats/peerstats.all", chunk_lines=1_000_000):
    for s in block:
        print(s.meta.get("peer"), len(s))

series = nt.load_large("/data/tracking.log.gz", chunk_lines=500_000)   # gzip is streamed too

Streaming works for line-oriented formats: ntpd/NTPsec stats files, chrony logs, linuxptp, CSV and the 2012 log. Header lines (CSV column names) are repeated for every block, so the result is the same as load. Peak memory is about the size of the arrays plus one block of text, against several times the file size for load.

Streaming reader for very large line-oriented logs.

:func:ntpstats.load reads the whole file into memory as text before parsing, which for a multi-gigabyte log costs several times the file size. :func:iter_chunks reads chunk_lines lines at a time and parses each block. :func:load_large joins the blocks into the same series load would return, so peak memory is the size of the arrays plus one block of text. load switches to it by itself for files larger than :data:STREAM_THRESHOLD bytes in a format listed in :data:STREAMABLE.

Header lines at the top of the file (a CSV header, comments) are repeated in front of every block, so column names and detection work the same in every block. Gzip-compressed logs are read as a stream too.

STREAMABLE = ('loopstats', 'peerstats', 'rawstats', 'chrony-tracking', 'chrony-measurements', 'chrony-statistics', 'chrony-refclocks', 'linuxptp', 'csv', 'gsoc2012') module-attribute

STREAM_THRESHOLD = 256 * 2 ** 20 module-attribute

iter_chunks(path, fmt='auto', chunk_lines=DEFAULT_CHUNK_LINES, tau0=None, name=None)

Parse a large log chunk_lines lines at a time; yields the series of each block.

Only formats in :data:STREAMABLE can be cut into blocks; others raise ValueError (use :func:ntpstats.load). Blocks without usable samples (for example only comments) are skipped.

load_large(path, fmt='auto', chunk_lines=DEFAULT_CHUNK_LINES, tau0=None, name=None)

Like :func:ntpstats.load for a large file, reading it chunk_lines lines at a time.

streamable(path, fmt='auto')

The detected format if path can be streamed, else None.

Deprecation helpers

For contributors: how a stable name is changed.

Deprecation helpers for the stable API (:mod:ntpstats.api).

Policy (also in CONTRIBUTING.md): a stable name or parameter that changes keeps working, with a :class:NtpstatsDeprecationWarning, for at least one full minor release. It is removed no earlier than the release named in the warning, and the removal is listed under Removed in the changelog.

The warning is a :class:FutureWarning subclass, so it is shown by default to the people who run the code (scripts, notebooks), not only under test runners. The test suite turns it into an error, so ntpstats never calls its own deprecated names.

NtpstatsDeprecationWarning

Bases: FutureWarning

A stable ntpstats name or parameter is deprecated and will be removed.

deprecated(since, remove_in, use=None)

Mark a function (or a class, on instantiation) as deprecated.

renamed_parameter(old, new, since, remove_in)

Accept old= as an alias of new= with a warning.

moved(module, aliases)

A module __getattr__ for names that moved.

aliases maps an old name to ("new.module:name", since, remove_in)::

__getattr__ = moved(__name__, {"old_name": ("ntpstats.x:new_name", "2.15", "2.17")})