Feed an event into this algorithm Returns [`Some`] if the event has improved this source's [`ClockParameters`].
(&mut self, event: event::Ntp)
| 67 | /// |
| 68 | /// Returns [`Some`] if the event has improved this source's [`ClockParameters`]. |
| 69 | pub fn feed(&mut self, event: event::Ntp) -> Option<&ClockParameters> { |
| 70 | // FIXME: take a MONOTONIC_COARSE timestamp *before* computing the clock error bound. |
| 71 | // |
| 72 | // The only use of this timestamp is to support and maintain the behavior for clients built |
| 73 | // against ClockBound 2.0. These clients grow the CEB by calculating the time elapsed |
| 74 | // between the instant the ClockParameters were computed, and the instant they read the |
| 75 | // system clock. This needs to be a bit pessimistic, and the `as_of_monotonic` timestamp |
| 76 | // should be taken *before* the time at which the CEB is calculated. |
| 77 | // |
| 78 | // Here this should be *before* the TSC post read of event fed to the algorithm. That would |
| 79 | // require carrying this `as_of_monotonic` timestamp from the IO components. Instead, we |
| 80 | // are taking a short cut and placing this timestamp slightly in the past, by 10 |
| 81 | // milliseconds to account for possible events where the daemon is scheduled out. |
| 82 | // |
| 83 | // The CEB is made worse by around 150 nanoseconds (assuming a 15PPM oscillator drift), |
| 84 | // which is negligible for ClockBound 2.0 clients. Moving this timestamp in the past may |
| 85 | // also help reduce the risk of causality breach errors seen when using the |
| 86 | // CLOCK_MONOTONIC_COARSE clock. |
| 87 | // |
| 88 | // This will be eliminated once we decide to stop supporting ClockBound 2.0 clients. |
| 89 | let as_of_monotonic = MonotonicCoarse.get_time(); |
| 90 | let as_of_monotonic = if as_of_monotonic > Instant::from_millis(10) { |
| 91 | as_of_monotonic - Duration::from_millis(10) |
| 92 | } else { |
| 93 | Instant::from_millis(0) |
| 94 | }; |
| 95 | |
| 96 | let tsc_midpoint = event.tsc_midpoint(); |
| 97 | |
| 98 | // First update the internal local (current SKM) and estimate (long term) |
| 99 | // sample buffers |
| 100 | let within_threshold = self |
| 101 | .feed_internal_buffers(event) |
| 102 | .inspect_err(|error_msg| match error_msg { |
| 103 | FeedError::Old { event, .. } => { |
| 104 | tracing::warn!(?event, ?error_msg); |
| 105 | } |
| 106 | }) |
| 107 | .ok()?; // early exit if there was an error with the sample |
| 108 | |
| 109 | if !within_threshold { |
| 110 | // At this point, if the input does not meet our expectations on the rtt threshold, |
| 111 | // there is no more processing to do. The end calculation will not be more accurate than |
| 112 | // the previous value (if we have one) |
| 113 | tracing::trace!("Early exit. Event not within threshold"); |
| 114 | return None; |
| 115 | } |
| 116 | |
| 117 | // Functionality from this point will fill out the equation |
| 118 | // `C(t) = TSC(t) × p^ + K − θ^(t)` where: |
| 119 | // - `C(t)` is the absolute time. Corrected. This is effectively the output of the clock sync algorithm |
| 120 | // - `TSC(t)` is the tsc reading at a time |
| 121 | // - `p^` is the estimation of the clock period |
| 122 | // - `K` is the "epoch" (the uncorrected time at `TSC(0)`) |
| 123 | // - `θ^(t)` is the time correction |
| 124 | |
| 125 | // Calculate uncorrected clock, aka `p^` and `K` |
| 126 | self.uncorrected_clock = Self::calculate_uncorrected_clock(&self.local, &self.estimate); |