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#[derive(Debug, Copy, Clone, PartialEq)]
15pub struct KalmanConfiguration {
16 pub step_threshold: Duration,
18 pub deadzone: f64,
21 pub steer_time: Duration,
26
27 pub max_steer: f64,
29 pub max_freq_offset: f64,
32
33 pub initial_frequency_uncertainty: f64,
35 pub initial_wander: f64,
37 pub delay_wander: f64,
44
45 pub precision_low_probability: f64,
48 pub precision_high_probability: f64,
51 pub precision_hysteresis: u8,
53
54 pub estimate_threshold: Duration,
59 pub difference_estimation_boundary: usize,
62 pub statistical_estimation_boundary: usize,
66 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
94fn 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
513pub 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 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 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 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 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 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}