ntpstats.filters¶
Clock-state estimation from noisy offset measurements.
Replaces the four ad-hoc Kalman variants of the 2012 prototype (which assumed a fixed 32 s poll, mixed filtered output back into the raw timestamps and never modelled the time step) with one standard two-state (phase, frequency) Kalman filter supporting irregular sampling, plus an optional Rauch-Tung-Striebel smoother for offline analysis.
State model (continuous-time random-walk FM + white FM clock)::
x_k = [phase (s), frequency (s/s)]
x_k+1 = F(dt) x_k + w, F = [[1, dt], [0, 1]]
Q(dt) = q_phase * [[dt, 0], [0, 0]]
+ q_freq * [[dt^3/3, dt^2/2], [dt^2/2, dt]]
z_k = phase + v, v ~ N(0, r)
q_phase (s^2/s) is the white-FM diffusion and q_freq (1/s) the
random-walk-FM diffusion; they relate to the Allan variance by
AVAR(tau) ~= q_phase/tau + q_freq*tau/3. :func:fit_noise estimates
them (and r) from an OADEV curve so the filter can be tuned from data.
fit_noise(series)
¶
Rough (r, q_phase, q_freq) from the series' OADEV.
Least-squares fit of AVAR(tau) = 3 r / tau^2 + q_phase / tau + q_freq tau / 3
(white PM + white FM + RW FM) on the octave-spaced OADEV, with
non-negative coefficients.
kalman(series, r=None, q_phase=None, q_freq=None, smooth=False, gate=5.0, delay_weighting=True)
¶
Run the two-state Kalman filter over series.
Parameters default to :func:fit_noise estimates. gate rejects
measurements whose normalised innovation exceeds gate sigmas (popcorn
spikes from queueing delay); None disables gating. With
smooth=True an RTS backward pass gives the offline optimum.
If the series has a delay column and delay_weighting is true,
each measurement's variance is inflated by ((delay - min_delay)/2)^2,
the square of the worst-case error its queueing could cause, so
low-delay samples dominate (a soft version of NTP's clock filter).
kalman_series(series, **kw)
¶
Return a new :class:TimeSeries holding the filtered offset.