Skip to content

ntpstats.holdover

Holdover: predicted time error after the reference is lost, and time to violation.

The question is operational: if GNSS or the upstream network goes away now, how long does this clock stay within 1.1 µs, 100 µs or 1 ms?

Input. The phase of the oscillator against a reference while the reference was available: a free-running measurement (TIC, a GNSSDO against a better reference), or the frequency correction a disciplining daemon logs (chrony's frequency column), integrated to phase.

Holdover model. At the loss time T the clock is aligned and then runs on the frequency (model="frequency"), or frequency and drift (model="drift"), fitted by least squares over the last window of data. The time error after t is::

TIE(t) = x(T+t) - x(T) - [fit(T+t) - fit(T)]

This is a linear function of the phase samples, so for power-law noise with coefficients h_α (fitted with :mod:ntpstats.noisefit, Kasdin–Walter model) its variance is computed exactly, including the error of the fitted frequency and drift. With model="frequency" a fitted drift gives the mean of TIE(t); with model="drift" the mean is zero. The envelope is mean ± z·σ, and the time to violation is when it first leaves a limit.

:func:backtest checks the prediction on the data itself: it cuts the reference out at many points of a long log and counts how often the real TIE stays inside the envelope (calibration).

HoldoverResult dataclass

time_to(limit)

First t where the envelope (and where the mean) leaves ±limit; None if not within the horizon.

predict(x, tau0, horizon, h=None, model='frequency', window=None, ci=0.95, limits=(), points=60, noise=None, drift_mean=None, uncertainty=40)

Holdover prediction from evenly sampled phase x (s, NaN = gap); the reference is lost after the last sample.

h (power-law coefficients) defaults to a fit of x. With model="frequency", drift_mean says whether the fitted drift shifts the mean TIE (default: when the noise fit finds a drift, or always when h is given). uncertainty=N mixes N bootstrap noise models into the envelope, so the uncertainty of the fitted model is included.

phase_from(series, source='offset', tau0=None, max_gap=3.0)

Uniform phase grid from a series: its offset (negated to local - reference) or its integrated frequency (ppm).

backtest(x, tau0, horizon, trials=20, train=None, h=None, model='frequency', window=None, ci=0.95, points=20, drift_mean=None)

Cut the reference at trials points of a long log, predict, and compare with what the data did.

The noise model is fitted once, on the first train seconds. Returns the fraction of (trial, horizon) points where the real TIE stayed inside the envelope, which should be close to ci.