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.