Skip to main content

sim_lib_view_device/
clock.rs

1//! Deterministic frame clock for device-rate local adaptation.
2
3use crate::RateClass;
4
5/// Modeled device-rate clock used by [`crate::AdapterLoop`].
6///
7/// The clock is a caller-advanced tick index. It does not read wall time, sleep,
8/// schedule work, or own an executor.
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub struct FrameClock {
11    /// Monotone device-frame tick.
12    pub tick: u64,
13    /// Timing envelope used to interpret tick distance.
14    pub rate: RateClass,
15}
16
17impl FrameClock {
18    /// Builds a clock at `tick` for the given rate envelope.
19    pub fn new(tick: u64, rate: RateClass) -> Self {
20        Self { tick, rate }
21    }
22
23    /// Builds a clock at tick zero.
24    pub fn at_zero(rate: RateClass) -> Self {
25        Self::new(0, rate)
26    }
27
28    /// Advances the modeled tick by one.
29    pub fn advance(&mut self) {
30        self.tick = self.tick.saturating_add(1);
31    }
32
33    /// Returns elapsed modeled milliseconds since `state_seq`.
34    pub fn elapsed_ms_since(self, state_seq: u64) -> u64 {
35        let elapsed_ticks = self.tick.saturating_sub(state_seq);
36        elapsed_ticks.saturating_mul(1000) / u64::from(self.rate.adapt_hz.max(1))
37    }
38
39    /// Returns whether a sample from `state_seq` exceeds the stale window.
40    pub fn stale(self, state_seq: u64) -> bool {
41        self.elapsed_ms_since(state_seq) > u64::from(self.rate.max_stale_ms)
42    }
43}