//! Monotonic and wall-clock binding. //! //! `MonoClock` is authoritative for tick budgets, telemetry-skew compensation, //! and inter-frame correlation. `WallClock` is GPS-bound when locked and NTP at //! boot. Drift > 200 ms surfaces as yellow health on the affected component. use std::time::Instant; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ClockSource { Gnss, Host, Coast, } /// Process-monotonic clock — never goes backwards, immune to NTP adjustments. #[derive(Debug, Clone, Copy)] pub struct MonoClock { boot: Instant, } impl MonoClock { pub fn new() -> Self { Self { boot: Instant::now(), } } /// Nanoseconds since this clock was constructed. pub fn elapsed_ns(&self) -> u64 { self.boot.elapsed().as_nanos() as u64 } } impl Default for MonoClock { fn default() -> Self { Self::new() } } /// Wall-clock binding — produced from `MonoClock` via the active `ClockSource`. /// Drift beyond the threshold MUST be surfaced as a yellow health detail. #[derive(Debug, Clone)] pub struct WallClock { pub source: ClockSource, } impl WallClock { pub fn new(source: ClockSource) -> Self { Self { source } } pub fn now(&self) -> chrono::DateTime { chrono::Utc::now() } } #[cfg(test)] mod tests { use super::*; #[test] fn mono_clock_is_monotonic() { let clock = MonoClock::new(); let t1 = clock.elapsed_ns(); let t2 = clock.elapsed_ns(); assert!(t2 >= t1, "monotonic clock went backwards: {t1} -> {t2}"); } }