use std::time::{Instant, SystemTime};
use crate::{BdtTime, GpsTime, GstTime, MonotonicTime, Tai1958Time, Tai1972Time, TaiTime};
pub type MonotonicClock = TaiClock<{ MonotonicTime::EPOCH_REF }>;
pub type GpsClock = TaiClock<{ GpsTime::EPOCH_REF }>;
pub type GstClock = TaiClock<{ GstTime::EPOCH_REF }>;
pub type BdtClock = TaiClock<{ BdtTime::EPOCH_REF }>;
pub type Tai1958Clock = TaiClock<{ Tai1958Time::EPOCH_REF }>;
pub type Tai1972Clock = TaiClock<{ Tai1972Time::EPOCH_REF }>;
pub struct TaiClock<const EPOCH_REF: i64> {
timestamp_ref: TaiTime<EPOCH_REF>,
instant_ref: Instant,
}
impl<const EPOCH_REF: i64> TaiClock<EPOCH_REF> {
pub fn init_at(now: TaiTime<EPOCH_REF>) -> Self {
Self {
timestamp_ref: now,
instant_ref: Instant::now(),
}
}
pub fn init_from_utc(leap_secs: i64) -> Self {
const EXTRA_SAMPLES: usize = 2;
let mut instant = Instant::now();
let system_time = SystemTime::now();
let mut instant_after = Instant::now();
let delta = instant_after.saturating_duration_since(instant); let mut measurement = (instant, delta, system_time);
for _ in 0..EXTRA_SAMPLES {
instant = instant_after;
let system_time = SystemTime::now();
instant_after = Instant::now();
let delta = instant_after.saturating_duration_since(instant);
if measurement.1.is_zero() || (delta < measurement.1 && !delta.is_zero()) {
measurement = (instant, delta, system_time);
}
}
Self {
timestamp_ref: TaiTime::from_system_time(&measurement.2, leap_secs),
instant_ref: measurement.0 + measurement.1.mul_f32(0.5), }
}
pub fn now(&self) -> TaiTime<EPOCH_REF> {
let delta = Instant::now().saturating_duration_since(self.instant_ref);
self.timestamp_ref + delta
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clock_init_at_smoke() {
use std::time::Duration;
const TIME_REF: MonotonicTime = MonotonicTime::new(-12345678, 987654321); const TOLERANCE_MILLIS: Duration = Duration::from_millis(20);
let clock = MonotonicClock::init_at(TIME_REF);
assert!(clock.now().duration_since(TIME_REF) <= TOLERANCE_MILLIS);
}
#[test]
fn clock_init_from_utc_smoke() {
use std::time::Duration;
const LEAP_SECS: i64 = 123; const TOLERANCE_MILLIS: Duration = Duration::from_millis(20);
let clock = MonotonicClock::init_from_utc(LEAP_SECS);
let utc_now = SystemTime::UNIX_EPOCH.elapsed().unwrap();
let tai_now_from_utc = MonotonicTime::new(LEAP_SECS, 0) + utc_now;
let tai_now_from_clock = clock.now();
if tai_now_from_clock >= tai_now_from_utc {
assert!(tai_now_from_clock.duration_since(tai_now_from_utc) <= TOLERANCE_MILLIS);
} else {
assert!(tai_now_from_utc.duration_since(tai_now_from_clock) <= TOLERANCE_MILLIS);
}
}
}