Skip to content

ntpstats.stability

Frequency/time stability estimators for clock-offset (phase) data.

All estimators take phase data x in seconds (NTP offsets are phase data) sampled uniformly every tau0 seconds, and follow the definitions in IEEE Std 1139 / NIST SP 1065 (Riley, "Handbook of Frequency Stability Analysis"). tests/test_stability.py validates them against literal implementations of the textbook sums, analytic power-law slopes and a frozen table of values from an independent implementation. No third-party stability library is used or required.

NaN samples are allowed: they mark gaps (see :meth:ntpstats.series.TimeSeries.to_uniform). Every term that touches a NaN is dropped rather than bridged, so gaps never manufacture data.

Estimators

adev non-overlapping Allan deviation (what the 2012 tool computed) oadev overlapping Allan deviation (preferred ADEV estimator) mdev modified Allan deviation (separates white vs flicker PM) tdev time deviation, tau/sqrt(3) * MDEV (ITU-T G.810 / G.8260 metric) hdev overlapping Hadamard deviation (insensitive to linear frequency drift, useful for NTP-disciplined clocks with aging/thermal drift) totdev total deviation (reflection-extended ADEV, tighter at long tau) mtot modified total deviation (long-tau MDEV counterpart, bias-corrected) ttot time total deviation, tau/sqrt(3) * MTOT theo1 Theo1 (Howe), reaches tau = 0.75 * record length theobr bias-removed Theo1 (TheoBR) theoh TheoH (ADEV at short tau, TheoBR at long tau) mtie maximum time interval error (ITU-T G.810) tierms RMS time interval error (ITU-T G.810)

StabilityResult dataclass

to_dataframe()

pandas DataFrame, one row per tau (see :func:ntpstats.adapters.stability_to_dataframe).

DynamicResult dataclass

to_xarray()

xarray DataArray with dims (time, tau) (see :func:ntpstats.adapters.dynamic_to_xarray).

tau_multipliers(n, spacing='octave', max_m=None)

Averaging factors m (tau = m * tau0).

spacing: "octave" (1, 2, 4, ...), "decade" (1, 2, 5, 10, 20, 50, ...), "dense" (~10 log-spaced points per decade) or "all". A sequence of integers is used verbatim.

compute(x, tau0, kind='oadev', taus='octave', min_terms=2, ci=0.683, max_work=None, bias_correction=True)

Compute one stability statistic of phase data x (seconds).

taus is a spacing name or a list of averaging factors m. Points supported by fewer than min_terms terms are dropped. With ci (e.g. 0.683 or 0.95) the dominant noise type is identified at every tau (lag-1 autocorrelation) and a chi-squared confidence interval is attached using the equivalent degrees of freedom.

MTOT and Theo1/TheoBR/TheoH cost O(N·m) per tau (TheoBR's bias ratio even more). max_work bounds the element operations per tau (default :data:MAX_WORK); above it the subsequences are sampled with an even stride of at most m and the TheoBR ratio averages :data:THEOBR_RATIO_TERMS evenly spaced terms. max_work=0 always computes the full definitions. What was sampled is reported in meta["stride"] (per tau, 1 = every subsequence) and meta["theobr_ratio_terms"].

MTOT and TTOT are divided by the MTOT bias factor of the noise type identified at each tau (:data:MTOT_BIAS), HTOT by :data:HTOT_BIAS, which is what Stable32 and NIST SP 1065 report; bias_correction=False returns the raw values. The factors used are in meta["bias_factor"].

edf_for_result(kind, alpha, m, terms, n_phase)

EDF used for confidence intervals.

ADEV/OADEV/MDEV/TDEV/HDEV: exact discrete power-law EDF (:func:ntpstats.edf.edf). TOTDEV: NIST SP 1065 table 12 for FM noise (b * T/tau - c), OADEV EDF for PM noise.

series_stability(series, kinds=('oadev',), taus='octave', tau0=None, max_gap=3.0, detrend=None, ci=0.683, max_work=None, bias_correction=True)

Convenience wrapper: resample a :class:TimeSeries and compute.

detrend may be None, "linear" (remove constant frequency offset, which ADEV is blind to anyway but TDEV/MTIE are not) or "quadratic" (also remove linear frequency drift). max_work is passed to :func:compute (0: never sample MTOT/Theo subsequences), as is bias_correction (MTOT/TTOT).

slopes(result)

Local log-log slope between consecutive tau points.

identify_noise(result)

Rough dominant-noise label for each tau interval, from the local slope.

noise_alpha(x, m=1, dmax=2, min_points=30)

Dominant power-law noise exponent alpha of phase data at averaging factor m.

Lag-1 autocorrelation method (W. Riley & C. Greenhall, "Power law noise identification using the lag 1 autocorrelation", EFTF 2004)::

alpha =  2  white PM        alpha = -1  flicker FM
alpha =  1  flicker PM      alpha = -2  random-walk FM
alpha =  0  white FM

Returns NaN if fewer than min_points decimated samples remain.

edf_approx(kind, N, m, alpha, terms)

Deprecated since 2.1: kept for API compatibility, now returns the exact EDF from :func:edf_for_result.

chi2_interval(dev, edf, ci=0.683)

Two-sided confidence interval of a deviation given its EDF.

dynamic(series, kind='oadev', window=None, step=None, taus='octave', tau0=None, max_gap=3.0, detrend=None)

Sliding-window stability ("dynamic ADEV") to expose non-stationarity.

window and step are in seconds (defaults: 1/8 of the record and a quarter window). The tau grid is fixed by the window length so every column is comparable.