statime/filters/
kalman.rs

1use super::{
2    matrix::{Matrix, Vector},
3    FilterEstimate,
4};
5#[allow(unused_imports)]
6use crate::float_polyfill::FloatPolyfill;
7use crate::{
8    filters::Filter,
9    port::Measurement,
10    time::{Duration, Time},
11};
12
13/// Configuration options for [KalmanFilter]
14#[derive(Debug, Copy, Clone, PartialEq)]
15pub struct KalmanConfiguration {
16    /// Threshold above which errors in time are corrected by steps
17    pub step_threshold: Duration,
18    /// Band of measured time offsets in which the algorithm doesn't try to
19    /// correct the offset, in standard deviations.
20    pub deadzone: f64,
21    /// Amount of time to take for correcting time offsets, in seconds.
22    ///
23    /// Lower values make the clock correct more quickly, but also less
24    /// precisely.
25    pub steer_time: Duration,
26
27    /// Maximum frequency offset to introduce during steering (ppm)
28    pub max_steer: f64,
29    /// Maximum correction to frequency (both from steering and to correct for
30    /// the properties of the clock) the algorithm can make. (ppm)
31    pub max_freq_offset: f64,
32
33    /// Initial uncertainty about the clocks frequency
34    pub initial_frequency_uncertainty: f64,
35    /// Initial estimate for the wander of the clock's frequency in s/s
36    pub initial_wander: f64,
37    /// Amount of uncertainty introduced into the delay over time.
38    /// Larger values allow quicker adaptation to changing conditions,
39    /// at the cost of less precise clock synchronization.
40    ///
41    /// This is automatically scaled with the size of the delay. (percentage per
42    /// second)
43    pub delay_wander: f64,
44
45    /// Likelyhood below which we start pushing the wander selection process
46    /// towards assuming a more precise clock.
47    pub precision_low_probability: f64,
48    /// Likelyhood above which we start pushing the wander selection process
49    /// towards assuming a less precise clock.
50    pub precision_high_probability: f64,
51    /// Amount of resistance to changes in wander (max 127)
52    pub precision_hysteresis: u8,
53
54    /// Maximum time between sync and delay to consider the combination for
55    /// automatic channel error estimation. Lower values improve the
56    /// estimate as it gets polluted less by uncertainties in the clock
57    /// frequency, but increase startup time.
58    pub estimate_threshold: Duration,
59    /// Amount of samples needed to start automatic measurement channel error
60    /// estimation (max 32)
61    pub difference_estimation_boundary: usize,
62    /// Amount of samples needed to switch automatic measurement channel error
63    /// estimation to using sample variance instead of largest-spread. (max
64    /// 32)
65    pub statistical_estimation_boundary: usize,
66    /// Multiplication factor on uncertainty estimation when it comes from
67    /// peer delay measurements (to compensate for there being multiple path
68    /// segments).
69    pub peer_delay_factor: f64,
70}
71
72impl Default for KalmanConfiguration {
73    fn default() -> Self {
74        Self {
75            step_threshold: Duration::from_seconds(1e-3),
76            deadzone: 0.0,
77            steer_time: Duration::from_seconds(2.0),
78            max_steer: 200.0,
79            max_freq_offset: 400.0,
80            initial_frequency_uncertainty: 100e-6,
81            initial_wander: 1e-16,
82            delay_wander: 1e-4 / 3600.0,
83            precision_low_probability: 1.0 / 3.0,
84            precision_high_probability: 2.0 / 3.0,
85            precision_hysteresis: 16,
86            estimate_threshold: Duration::from_millis(200),
87            difference_estimation_boundary: 4,
88            statistical_estimation_boundary: 8,
89            peer_delay_factor: 2.0,
90        }
91    }
92}
93
94/// Approximation of 1 - the chi-squared cdf with 1 degree of freedom
95/// source: https://en.wikipedia.org/wiki/Error_function
96fn chi_1(chi: f64) -> f64 {
97    const P: f64 = 0.3275911;
98    const A1: f64 = 0.254829592;
99    const A2: f64 = -0.284496736;
100    const A3: f64 = 1.421413741;
101    const A4: f64 = -1.453152027;
102    const A5: f64 = 1.061405429;
103
104    let x = (chi / 2.).sqrt();
105    let t = 1. / (1. + P * x);
106    (A1 * t + A2 * t * t + A3 * t * t * t + A4 * t * t * t * t + A5 * t * t * t * t * t)
107        * (-(x * x)).exp()
108}
109
110fn sqr(x: f64) -> f64 {
111    x * x
112}
113
114#[derive(Debug, Default, Copy, Clone)]
115struct MeasurementErrorEstimator {
116    data: [f64; 32],
117    next_idx: usize,
118    fill: usize,
119    last_sync: Option<(Time, Duration)>,
120    last_delay: Option<(Time, Duration)>,
121    peer_delay_detected: bool,
122}
123
124impl MeasurementErrorEstimator {
125    fn mean(&self) -> f64 {
126        self.data.iter().take(self.fill).sum::<f64>() / (self.data.len() as f64)
127    }
128
129    fn variance(&self) -> f64 {
130        let mean = self.mean();
131        self.data
132            .iter()
133            .take(self.fill)
134            .map(|v| sqr(v - mean))
135            .sum::<f64>()
136            / ((self.data.len() - 1) as f64)
137    }
138
139    fn range_size(&self) -> f64 {
140        self.data
141            .iter()
142            .take(self.fill)
143            .max_by(|x, y| {
144                if x > y {
145                    core::cmp::Ordering::Greater
146                } else {
147                    core::cmp::Ordering::Less
148                }
149            })
150            .unwrap()
151            - self
152                .data
153                .iter()
154                .take(self.fill)
155                .min_by(|x, y| {
156                    if x > y {
157                        core::cmp::Ordering::Greater
158                    } else {
159                        core::cmp::Ordering::Less
160                    }
161                })
162                .unwrap()
163    }
164
165    fn insert_entry(&mut self, entry: f64) {
166        log::info!("New entry {}", entry * 1e9);
167        self.data[self.next_idx] = entry;
168        self.next_idx = (self.next_idx + 1) % self.data.len();
169        self.fill = (self.fill + 1).min(self.data.len())
170    }
171
172    fn absorb_measurement(
173        &mut self,
174        m: Measurement,
175        estimated_frequency: f64,
176        config: &KalmanConfiguration,
177    ) {
178        if let Some(sync_offset) = m.raw_sync_offset {
179            if let Some((time, delay_offset)) = self.last_delay.take() {
180                if (m.event_time - time).abs() < config.estimate_threshold {
181                    self.insert_entry(
182                        sync_offset.seconds() - delay_offset.seconds()
183                            + (time - m.event_time).seconds() * estimated_frequency,
184                    );
185                    log::info!(
186                        "New uncertainty estimate: {}ns",
187                        self.measurement_variance(config).sqrt() * 1e9,
188                    );
189                } else {
190                    self.last_sync = Some((m.event_time, sync_offset));
191                }
192            } else {
193                self.last_sync = Some((m.event_time, sync_offset));
194            }
195        }
196
197        if let Some(delay_offset) = m.raw_delay_offset {
198            if let Some((time, sync_offset)) = self.last_sync.take() {
199                if (m.event_time - time).abs() < config.estimate_threshold {
200                    self.insert_entry(
201                        sync_offset.seconds() - delay_offset.seconds()
202                            + (m.event_time - time).seconds() * estimated_frequency,
203                    );
204                    log::info!(
205                        "New uncertainty estimate: {}ns",
206                        self.measurement_variance(config).sqrt() * 1e9,
207                    );
208                } else {
209                    self.last_delay = Some((m.event_time, delay_offset));
210                }
211            } else {
212                self.last_delay = Some((m.event_time, delay_offset));
213            }
214        }
215
216        if let Some(peer_delay) = m.peer_delay {
217            self.last_delay = None;
218            self.last_sync = None;
219            self.peer_delay_detected = true;
220            self.insert_entry(peer_delay.seconds());
221        }
222    }
223
224    fn measurement_variance(&self, config: &KalmanConfiguration) -> f64 {
225        if self.fill < config.difference_estimation_boundary {
226            sqr(config.steer_time.seconds())
227        } else if self.fill < config.statistical_estimation_boundary {
228            sqr(self.range_size())
229        } else {
230            self.variance() / 2.0
231        }
232    }
233
234    fn peer_delay(&self) -> bool {
235        self.peer_delay_detected
236    }
237}
238
239#[derive(Clone, Debug)]
240struct InnerFilter {
241    state: Vector<3>,
242    uncertainty: Matrix<3, 3>,
243    filter_time: Time,
244}
245
246impl InnerFilter {
247    const MEASUREMENT_SYNC: Matrix<1, 3> = Matrix::new([[1.0, 0.0, 1.0]]);
248    const MEASUREMENT_DELAY: Matrix<1, 3> = Matrix::new([[1.0, 0.0, -1.0]]);
249    const MEASUREMENT_PEER_DELAY: Matrix<1, 3> = Matrix::new([[0.0, 0.0, 1.0]]);
250
251    fn new(initial_offset: f64, time: Time, config: &KalmanConfiguration) -> Self {
252        Self {
253            state: Vector::new_vector([initial_offset, 0.0, 0.0]),
254            uncertainty: Matrix::new([
255                [sqr(config.step_threshold.seconds()), 0.0, 0.0],
256                [0.0, sqr(config.initial_frequency_uncertainty), 0.0],
257                [0.0, 0.0, sqr(config.step_threshold.seconds())],
258            ]),
259            filter_time: time,
260        }
261    }
262
263    fn progress_filtertime(&mut self, time: Time, wander: f64, config: &KalmanConfiguration) {
264        debug_assert!(time >= self.filter_time);
265        if time < self.filter_time {
266            return;
267        }
268
269        let delta_t = (time - self.filter_time).seconds();
270        let update = Matrix::new([[1.0, delta_t, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]);
271        let process_noise = Matrix::new([
272            [
273                wander * delta_t * delta_t * delta_t / 3.,
274                wander * delta_t * delta_t / 2.,
275                0.,
276            ],
277            [wander * delta_t * delta_t / 2., wander * delta_t, 0.],
278            [
279                0.,
280                0.,
281                config.delay_wander * delta_t * sqr(self.state.ventry(2)),
282            ],
283        ]);
284
285        self.state = update * self.state;
286        self.uncertainty = update * self.uncertainty * update.transpose() + process_noise;
287        self.filter_time = time;
288    }
289
290    fn absorb_sync_offset(&mut self, sync_offset: f64, variance: f64) {
291        let measurement_vec = Vector::new_vector([sync_offset]);
292        let measurement_noise = Matrix::new([[variance]]);
293        self.absorb_measurement(measurement_vec, Self::MEASUREMENT_SYNC, measurement_noise);
294    }
295
296    fn absorb_delay_offset(&mut self, delay_offset: f64, variance: f64) {
297        let measurement_vec = Vector::new_vector([delay_offset]);
298        let measurement_noise = Matrix::new([[variance]]);
299        self.absorb_measurement(measurement_vec, Self::MEASUREMENT_DELAY, measurement_noise);
300    }
301
302    fn absorb_peer_delay(&mut self, peer_delay: f64, variance: f64) {
303        let measurement_vec = Vector::new_vector([peer_delay]);
304        let measurement_noise = Matrix::new([[variance]]);
305        self.absorb_measurement(
306            measurement_vec,
307            Self::MEASUREMENT_PEER_DELAY,
308            measurement_noise,
309        )
310    }
311
312    fn absorb_measurement(
313        &mut self,
314        measurement_vec: Vector<1>,
315        measurement_transform: Matrix<1, 3>,
316        measurement_noise: Matrix<1, 1>,
317    ) {
318        let (prediction, uncertainty) = self.predict(measurement_transform);
319
320        let difference = measurement_vec - prediction;
321        let difference_covariance = uncertainty + measurement_noise;
322        let update_strength =
323            self.uncertainty * measurement_transform.transpose() * difference_covariance.inverse();
324        self.state = self.state + update_strength * difference;
325        self.uncertainty = ((Matrix::unit() - update_strength * measurement_transform)
326            * self.uncertainty)
327            .symmetrize();
328    }
329
330    fn absorb_frequency_steer(
331        &mut self,
332        steer: f64,
333        time: Time,
334        wander: f64,
335        config: &KalmanConfiguration,
336    ) {
337        self.progress_filtertime(time, wander, config);
338        self.state = self.state + Vector::new_vector([0., steer * 1e-6, 0.]);
339    }
340
341    fn absorb_offset_steer(&mut self, steer: f64) {
342        self.state = self.state + Vector::new_vector([steer, 0., 0.]);
343        self.filter_time += Duration::from_seconds(steer);
344    }
345
346    fn predict<const N: usize>(
347        &self,
348        measurement_transform: Matrix<N, 3>,
349    ) -> (Vector<N>, Matrix<N, N>) {
350        let prediction = measurement_transform * self.state;
351        let uncertainty =
352            measurement_transform * self.uncertainty * measurement_transform.transpose();
353        (prediction, uncertainty)
354    }
355
356    fn predict_sync_offset(&self) -> (f64, f64) {
357        let (prediction, uncertainty) = self.predict(Self::MEASUREMENT_SYNC);
358        (prediction.entry(0, 0), uncertainty.entry(0, 0))
359    }
360
361    fn predict_delay_offset(&self) -> (f64, f64) {
362        let (prediction, uncertainty) = self.predict(Self::MEASUREMENT_DELAY);
363        (prediction.entry(0, 0), uncertainty.entry(0, 0))
364    }
365}
366
367#[derive(Default, Debug, Clone)]
368struct BaseFilter(Option<InnerFilter>);
369
370impl BaseFilter {
371    fn new() -> Self {
372        Self(None)
373    }
374
375    fn progress_filtertime(&mut self, time: Time, wander: f64, config: &KalmanConfiguration) {
376        match &mut self.0 {
377            Some(inner) => inner.progress_filtertime(time, wander, config),
378            None => self.0 = Some(InnerFilter::new(0.0, time, config)),
379        }
380    }
381
382    fn absorb_sync_offset(
383        &mut self,
384        sync_offset: f64,
385        variance: f64,
386        config: &KalmanConfiguration,
387    ) {
388        if let Some(inner) = &mut self.0 {
389            if (sync_offset - inner.state.ventry(0)).abs() > config.step_threshold.seconds() {
390                log::info!("Measurement too far from state, resetting");
391                *inner = InnerFilter::new(sync_offset, inner.filter_time, config);
392            } else {
393                inner.absorb_sync_offset(sync_offset, variance)
394            }
395        }
396    }
397
398    fn absorb_delay_offset(
399        &mut self,
400        delay_offset: f64,
401        variance: f64,
402        config: &KalmanConfiguration,
403    ) {
404        if let Some(inner) = &mut self.0 {
405            if (delay_offset - inner.state.ventry(0)).abs() > config.step_threshold.seconds() {
406                log::info!("Measurement too far from state, resetting");
407                *inner = InnerFilter::new(delay_offset, inner.filter_time, config);
408            } else {
409                inner.absorb_delay_offset(delay_offset, variance)
410            }
411        }
412    }
413
414    fn absorb_peer_delay(&mut self, peer_delay: f64, variance: f64) {
415        if let Some(inner) = &mut self.0 {
416            inner.absorb_peer_delay(peer_delay, variance)
417        }
418    }
419
420    fn absorb_frequency_steer(
421        &mut self,
422        steer: f64,
423        time: Time,
424        wander: f64,
425        config: &KalmanConfiguration,
426    ) {
427        match &mut self.0 {
428            Some(inner) => inner.absorb_frequency_steer(steer, time, wander, config),
429            None => self.0 = Some(InnerFilter::new(0.0, time, config)),
430        }
431    }
432
433    fn absorb_offset_steer(&mut self, steer: f64) {
434        if let Some(inner) = &mut self.0 {
435            inner.absorb_offset_steer(steer)
436        }
437    }
438
439    fn offset(&self) -> f64 {
440        self.0
441            .as_ref()
442            .map(|inner| inner.state.ventry(0))
443            .unwrap_or(0.0)
444    }
445
446    fn offset_uncertainty(&self, config: &KalmanConfiguration) -> f64 {
447        self.0
448            .as_ref()
449            .map(|inner| inner.uncertainty.entry(0, 0).sqrt())
450            .unwrap_or(config.step_threshold.seconds())
451    }
452
453    fn freq_offset(&self) -> f64 {
454        self.0
455            .as_ref()
456            .map(|inner| inner.state.ventry(1))
457            .unwrap_or(0.0)
458    }
459
460    fn freq_offset_uncertainty(&self, config: &KalmanConfiguration) -> f64 {
461        self.0
462            .as_ref()
463            .map(|inner| inner.uncertainty.entry(1, 1).sqrt())
464            .unwrap_or(config.initial_frequency_uncertainty)
465    }
466
467    fn mean_delay(&self) -> f64 {
468        self.0
469            .as_ref()
470            .map(|inner| inner.state.ventry(2))
471            .unwrap_or(0.0)
472    }
473
474    fn mean_delay_uncertainty(&self, config: &KalmanConfiguration) -> f64 {
475        self.0
476            .as_ref()
477            .map(|inner| inner.uncertainty.entry(2, 2).sqrt())
478            .unwrap_or(config.step_threshold.seconds())
479    }
480
481    fn predict_sync_offset(&self, config: &KalmanConfiguration) -> (f64, f64) {
482        self.0
483            .as_ref()
484            .map(|inner| inner.predict_sync_offset())
485            .unwrap_or((0.0, sqr(config.step_threshold.seconds())))
486    }
487
488    fn predict_delay_offset(&self, config: &KalmanConfiguration) -> (f64, f64) {
489        self.0
490            .as_ref()
491            .map(|inner| inner.predict_delay_offset())
492            .unwrap_or((0.0, sqr(config.step_threshold.seconds())))
493    }
494
495    fn after_filter_time(&self, time: Time) -> bool {
496        match &self.0 {
497            Some(inner) => time >= inner.filter_time,
498            None => true,
499        }
500    }
501}
502
503fn clamp_adjustment(current: f64, error: f64, bound: f64) -> f64 {
504    if current + error > bound {
505        bound - current
506    } else if current + error < -bound {
507        -bound - current
508    } else {
509        error
510    }
511}
512
513/// Kalman filter based way for controlling the clock
514pub struct KalmanFilter {
515    config: KalmanConfiguration,
516    running_filter: BaseFilter,
517    wander_filter: BaseFilter,
518    wander_score: i8,
519    wander: f64,
520    wander_measurement_error: f64,
521    measurement_error_estimator: MeasurementErrorEstimator,
522    cur_frequency: Option<f64>,
523}
524
525impl Filter for KalmanFilter {
526    type Config = KalmanConfiguration;
527
528    fn new(config: Self::Config) -> Self {
529        let measurement_error_estimator = MeasurementErrorEstimator::default();
530        KalmanFilter {
531            running_filter: BaseFilter::new(),
532            wander_filter: BaseFilter::new(),
533            wander_score: 0,
534            wander: config.initial_wander,
535            wander_measurement_error: measurement_error_estimator
536                .measurement_variance(&config)
537                .sqrt(),
538            measurement_error_estimator,
539            cur_frequency: None,
540            config,
541        }
542    }
543
544    fn measurement<C: crate::Clock>(
545        &mut self,
546        m: Measurement,
547        clock: &mut C,
548    ) -> super::FilterUpdate {
549        if !self.running_filter.after_filter_time(m.event_time) {
550            return super::FilterUpdate::default();
551        }
552
553        self.measurement_error_estimator.absorb_measurement(
554            m,
555            self.running_filter.freq_offset(),
556            &self.config,
557        );
558
559        self.update_wander(m);
560
561        self.running_filter
562            .progress_filtertime(m.event_time, self.wander, &self.config);
563        if let Some(sync_offset) = m.raw_sync_offset {
564            // We can start controlling, so need a proper frequency.
565            self.ensure_freq_init(clock);
566
567            self.running_filter.absorb_sync_offset(
568                sync_offset.seconds(),
569                self.measurement_error_estimator
570                    .measurement_variance(&self.config)
571                    * (if self.measurement_error_estimator.peer_delay() {
572                        self.config.peer_delay_factor
573                    } else {
574                        1.0
575                    }),
576                &self.config,
577            );
578        }
579        if let Some(delay_offset) = m.raw_delay_offset {
580            // We can start controlling, so need a proper frequency.
581            self.ensure_freq_init(clock);
582
583            self.running_filter.absorb_delay_offset(
584                delay_offset.seconds(),
585                self.measurement_error_estimator
586                    .measurement_variance(&self.config)
587                    * (if self.measurement_error_estimator.peer_delay() {
588                        self.config.peer_delay_factor
589                    } else {
590                        1.0
591                    }),
592                &self.config,
593            );
594        }
595        if let Some(peer_delay) = m.peer_delay {
596            self.running_filter.absorb_peer_delay(
597                peer_delay.seconds(),
598                self.measurement_error_estimator
599                    .measurement_variance(&self.config)
600                    * (if self.measurement_error_estimator.peer_delay() {
601                        self.config.peer_delay_factor
602                    } else {
603                        1.0
604                    }),
605            );
606        }
607
608        self.display_state();
609
610        self.steer(clock)
611    }
612
613    fn update<C: crate::Clock>(&mut self, clock: &mut C) -> super::FilterUpdate {
614        // Remote has gone away, set frequency as close as possible to our best estimate
615        // of correct
616        self.change_frequency(0.0, clock);
617        super::FilterUpdate {
618            next_update: None,
619            mean_delay: Some(Duration::from_seconds(self.running_filter.mean_delay())),
620        }
621    }
622
623    fn demobilize<C: crate::Clock>(mut self, clock: &mut C) {
624        // Remote has gone away, set frequency as close as possible to our best estimate
625        // of correct
626        self.change_frequency(0.0, clock);
627    }
628
629    fn current_estimates(&self) -> super::FilterEstimate {
630        FilterEstimate {
631            offset_from_master: Duration::from_seconds(self.running_filter.offset()),
632            mean_delay: Duration::from_seconds(self.running_filter.mean_delay()),
633        }
634    }
635}
636
637impl KalmanFilter {
638    fn change_frequency<C: crate::Clock>(&mut self, target: f64, clock: &mut C) {
639        if let Some(cur_frequency) = self.cur_frequency {
640            let error_ppm = clamp_adjustment(
641                cur_frequency,
642                target - self.running_filter.freq_offset() * 1e6,
643                self.config.max_freq_offset,
644            );
645            if let Ok(time) = clock.set_frequency(cur_frequency + error_ppm) {
646                self.cur_frequency = Some(cur_frequency + error_ppm);
647                self.running_filter.absorb_frequency_steer(
648                    error_ppm,
649                    time,
650                    self.wander,
651                    &self.config,
652                );
653                self.wander_filter.absorb_frequency_steer(
654                    error_ppm,
655                    time,
656                    self.wander,
657                    &self.config,
658                );
659                log::info!(
660                    "Steered frequency by {}ppm (target: {}ppm)",
661                    error_ppm,
662                    target
663                );
664            } else {
665                log::error!("Could not adjust clock frequency");
666            }
667        }
668    }
669
670    fn step<C: crate::Clock>(&mut self, clock: &mut C, offset: f64) {
671        if clock.step_clock(Duration::from_seconds(-offset)).is_ok() {
672            log::info!("Stepped clock by {}s", -offset);
673            self.running_filter.absorb_offset_steer(-offset);
674            self.wander_filter.absorb_offset_steer(-offset);
675        }
676    }
677
678    fn display_state(&self) {
679        log::info!(
680            "Estimated offset {}ns+-{}ns, freq {}+-{}, delay {}+-{}",
681            self.running_filter.offset() * 1e9,
682            self.running_filter.offset_uncertainty(&self.config) * 1e9,
683            self.running_filter.freq_offset() * 1e6,
684            self.running_filter.freq_offset_uncertainty(&self.config) * 1e6,
685            self.running_filter.mean_delay() * 1e9,
686            self.running_filter.mean_delay_uncertainty(&self.config) * 1e9
687        );
688    }
689
690    fn steer<C: crate::Clock>(&mut self, clock: &mut C) -> super::FilterUpdate {
691        let error = self.running_filter.offset();
692        if error.abs() < self.config.step_threshold.seconds() {
693            let desired_adjust = error.signum()
694                * (error.abs()
695                    - self.running_filter.offset_uncertainty(&self.config) * self.config.deadzone)
696                    .max(0.0);
697            let target = (-desired_adjust * 1e6 / self.config.steer_time.seconds())
698                .clamp(-self.config.max_steer, self.config.max_steer);
699            self.change_frequency(target, clock);
700            super::FilterUpdate {
701                next_update: Some(core::time::Duration::from_secs_f64(
702                    self.config.steer_time.seconds(),
703                )),
704                mean_delay: Some(Duration::from_seconds(self.running_filter.mean_delay())),
705            }
706        } else {
707            self.step(clock, error);
708            super::FilterUpdate {
709                next_update: None,
710                mean_delay: Some(Duration::from_seconds(self.running_filter.mean_delay())),
711            }
712        }
713    }
714
715    fn wander_score_update(&mut self, uncertainty: f64, prediction: f64, actual: f64) {
716        log::info!("Wander uncertainty: {}ns", uncertainty.sqrt() * 1e9);
717        if self.wander_measurement_error
718            > 10.0
719                * self
720                    .measurement_error_estimator
721                    .measurement_variance(&self.config)
722                    .sqrt()
723        {
724            self.wander_filter = self.running_filter.clone();
725            self.wander_measurement_error = self
726                .measurement_error_estimator
727                .measurement_variance(&self.config)
728                .sqrt()
729        } else if uncertainty.sqrt() > 10.0 * self.wander_measurement_error {
730            log::info!(
731                "Wander update predict: {}ns, actual: {}ns",
732                prediction * 1e9,
733                actual * 1e9
734            );
735            let p = 1.
736                - chi_1(
737                    sqr(actual - prediction) / (uncertainty + sqr(self.wander_measurement_error)),
738                );
739            log::info!("p: {}", p);
740            if p < self.config.precision_low_probability {
741                self.wander_score = self.wander_score.saturating_sub(1);
742            } else if p > self.config.precision_high_probability {
743                self.wander_score = self.wander_score.saturating_add(1);
744            } else {
745                self.wander_score = self.wander_score - self.wander_score.signum();
746            }
747            log::info!("Wander update");
748            self.wander_filter = self.running_filter.clone();
749            self.wander_measurement_error = self
750                .measurement_error_estimator
751                .measurement_variance(&self.config)
752                .sqrt();
753        }
754    }
755
756    fn update_wander(&mut self, m: Measurement) {
757        self.wander_filter
758            .progress_filtertime(m.event_time, self.wander, &self.config);
759        if let Some(sync_offset) = m.raw_sync_offset {
760            let (prediction, uncertainty) = self.wander_filter.predict_sync_offset(&self.config);
761            self.wander_score_update(uncertainty, prediction, sync_offset.seconds());
762        }
763        if let Some(delay_offset) = m.raw_delay_offset {
764            let (prediction, uncertainty) = self.wander_filter.predict_delay_offset(&self.config);
765            self.wander_score_update(uncertainty, prediction, delay_offset.seconds());
766        }
767        log::info!("wander score: {}", self.wander_score);
768        if self.wander_score < -(self.config.precision_hysteresis as i8) {
769            self.wander /= 4.0;
770            self.wander_score = 0;
771            log::info!("Updated wander estimate: {:e}", self.wander);
772        }
773        if self.wander_score > (self.config.precision_hysteresis as i8) {
774            self.wander *= 4.0;
775            self.wander_score = 0;
776            log::info!("Updated wander estimate: {:e}", self.wander);
777        }
778    }
779
780    fn ensure_freq_init<C: crate::Clock>(&mut self, clock: &mut C) {
781        // TODO: At some point we should probably look at a better
782        // mechanism for this than just resetting the frequency.
783        if self.cur_frequency.is_none() {
784            if clock.set_frequency(0.0).is_ok() {
785                self.cur_frequency = Some(0.0);
786            } else {
787                log::error!("Could not adjust clock frequency");
788            }
789        }
790    }
791}
792
793#[cfg(test)]
794mod tests {
795    use super::*;
796    use crate::Clock;
797
798    #[derive(Default)]
799    struct TestClock {
800        last_freq: Option<f64>,
801    }
802
803    impl Clock for TestClock {
804        type Error = core::convert::Infallible;
805
806        fn now(&self) -> Time {
807            Time::from_nanos(0)
808        }
809
810        fn step_clock(&mut self, _offset: Duration) -> Result<Time, Self::Error> {
811            panic!("Test should not step clock");
812        }
813
814        fn set_frequency(&mut self, ppm: f64) -> Result<Time, Self::Error> {
815            self.last_freq = Some(ppm);
816            Ok(Time::from_nanos(0))
817        }
818
819        fn set_properties(
820            &mut self,
821            _time_properties_ds: &crate::config::TimePropertiesDS,
822        ) -> Result<(), Self::Error> {
823            panic!("Test should not set properties");
824        }
825    }
826
827    #[test]
828    fn check_frequency_steering_bounding() {
829        let timebase = Time::from_nanos(0);
830        let mut filter = KalmanFilter {
831            config: KalmanConfiguration {
832                max_freq_offset: 10.0,
833                ..Default::default()
834            },
835            running_filter: BaseFilter(Some(InnerFilter {
836                state: Vector::new_vector([0.0, 0.0, 0.0]),
837                uncertainty: Matrix::new([[1e-17, 0.0, 0.0], [0.0, 1e-16, 0.0], [0.0, 0.0, 1e-18]]),
838                filter_time: timebase,
839            })),
840            wander_filter: BaseFilter(None),
841            wander_score: 0,
842            wander: KalmanConfiguration::default().initial_wander,
843            wander_measurement_error: 1.0,
844            measurement_error_estimator: MeasurementErrorEstimator::default(),
845            cur_frequency: Some(0.0),
846        };
847        let mut clock = TestClock::default();
848        filter.change_frequency(50.0, &mut clock);
849        assert_eq!(clock.last_freq, Some(10.0));
850        filter.change_frequency(50.0, &mut clock);
851        assert_eq!(clock.last_freq, Some(10.0));
852        filter.change_frequency(-50.0, &mut clock);
853        assert_eq!(clock.last_freq, Some(-10.0));
854
855        let timebase = Time::from_nanos(0);
856        let mut filter = KalmanFilter {
857            config: KalmanConfiguration {
858                max_freq_offset: 10.0,
859                ..Default::default()
860            },
861            running_filter: BaseFilter(Some(InnerFilter {
862                state: Vector::new_vector([0.0, 0.0, 0.0]),
863                uncertainty: Matrix::new([[1e-17, 0.0, 0.0], [0.0, 1e-16, 0.0], [0.0, 0.0, 1e-18]]),
864                filter_time: timebase,
865            })),
866            wander_filter: BaseFilter(None),
867            wander_score: 0,
868            wander: KalmanConfiguration::default().initial_wander,
869            wander_measurement_error: 1.0,
870            measurement_error_estimator: MeasurementErrorEstimator::default(),
871            cur_frequency: Some(0.0),
872        };
873        let mut clock = TestClock::default();
874        filter.change_frequency(-50.0, &mut clock);
875        assert_eq!(clock.last_freq, Some(-10.0));
876        filter.change_frequency(-50.0, &mut clock);
877        assert_eq!(clock.last_freq, Some(-10.0));
878        filter.change_frequency(50.0, &mut clock);
879        assert_eq!(clock.last_freq, Some(10.0));
880
881        let timebase = Time::from_nanos(0);
882        let mut filter = KalmanFilter {
883            config: KalmanConfiguration {
884                max_freq_offset: 10.0,
885                ..Default::default()
886            },
887            running_filter: BaseFilter(Some(InnerFilter {
888                state: Vector::new_vector([0.0, 0.0, 0.0]),
889                uncertainty: Matrix::new([[1e-17, 0.0, 0.0], [0.0, 1e-16, 0.0], [0.0, 0.0, 1e-18]]),
890                filter_time: timebase,
891            })),
892            wander_filter: BaseFilter(None),
893            wander_score: 0,
894            wander: KalmanConfiguration::default().initial_wander,
895            wander_measurement_error: 1.0,
896            measurement_error_estimator: MeasurementErrorEstimator::default(),
897            cur_frequency: Some(20.0),
898        };
899        let mut clock = TestClock::default();
900        filter.change_frequency(50.0, &mut clock);
901        assert_eq!(clock.last_freq, Some(10.0));
902
903        let timebase = Time::from_nanos(0);
904        let mut filter = KalmanFilter {
905            config: KalmanConfiguration {
906                max_freq_offset: 10.0,
907                ..Default::default()
908            },
909            running_filter: BaseFilter(Some(InnerFilter {
910                state: Vector::new_vector([0.0, 0.0, 0.0]),
911                uncertainty: Matrix::new([[1e-17, 0.0, 0.0], [0.0, 1e-16, 0.0], [0.0, 0.0, 1e-18]]),
912                filter_time: timebase,
913            })),
914            wander_filter: BaseFilter(None),
915            wander_score: 0,
916            wander: KalmanConfiguration::default().initial_wander,
917            wander_measurement_error: 1.0,
918            measurement_error_estimator: MeasurementErrorEstimator::default(),
919            cur_frequency: Some(-20.0),
920        };
921        let mut clock = TestClock::default();
922        filter.change_frequency(50.0, &mut clock);
923        assert_eq!(clock.last_freq, Some(10.0));
924    }
925}