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
NtpstatsDeprecationWarningcame 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.
NtpstatsDeprecationWarningis aFutureWarning, 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")})