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 }>;
#[derive(Copy, Clone, Debug, Hash)]
pub struct TaiClock<const EPOCH_REF: i64> {
timestamp_ref: TaiTime<EPOCH_REF>,
wall_clock_ref: Instant,
}
impl<const EPOCH_REF: i64> TaiClock<EPOCH_REF> {
pub fn init_at(now: TaiTime<EPOCH_REF>) -> Self {
Self::init_from_instant(now, Instant::now())
}
pub fn init_from_utc(leap_secs: i64) -> Self {
let (system_time_ref, instant_ref) = get_correlated_time_refs();
Self::init_from_instant(
TaiTime::from_system_time(&system_time_ref, leap_secs),
instant_ref,
)
}
pub fn init_from_instant(timestamp_ref: TaiTime<EPOCH_REF>, wall_clock_ref: Instant) -> Self {
Self {
timestamp_ref,
wall_clock_ref,
}
}
pub fn init_from_system_time(
timestamp_ref: TaiTime<EPOCH_REF>,
wall_clock_ref: SystemTime,
) -> Self {
let (system_time_ref, instant_ref) = get_correlated_time_refs();
let timestamp_ref = if wall_clock_ref > system_time_ref {
timestamp_ref - wall_clock_ref.duration_since(system_time_ref).unwrap()
} else {
timestamp_ref + system_time_ref.duration_since(wall_clock_ref).unwrap()
};
Self::init_from_instant(timestamp_ref, instant_ref)
}
pub fn now(&self) -> TaiTime<EPOCH_REF> {
let now = Instant::now();
if now >= self.wall_clock_ref {
self.timestamp_ref + now.duration_since(self.wall_clock_ref)
} else {
self.timestamp_ref - self.wall_clock_ref.duration_since(now)
}
}
}
fn get_correlated_time_refs() -> (SystemTime, Instant) {
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);
}
}
(measurement.2, measurement.0 + measurement.1.mul_f32(0.5))
}
#[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: Duration = Duration::from_millis(20);
let clock = MonotonicClock::init_at(TIME_REF);
assert!(clock.now().duration_since(TIME_REF) <= TOLERANCE);
}
#[test]
fn clock_init_from_utc_smoke() {
use std::time::Duration;
const LEAP_SECS: i64 = 123; const TOLERANCE: 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);
} else {
assert!(tai_now_from_utc.duration_since(tai_now_from_clock) <= TOLERANCE);
}
}
#[test]
fn clock_init_from_past_instant_smoke() {
use std::time::Duration;
use crate::MonotonicTime;
const OFFSET: Duration = Duration::from_secs(1000);
const TOLERANCE: Duration = Duration::from_millis(20);
let t0 = MonotonicTime::new(123, 456);
let clock = MonotonicClock::init_from_instant(t0, Instant::now() - OFFSET);
let delta = clock.now().duration_since(t0 + OFFSET);
assert!(delta <= TOLERANCE);
}
#[test]
fn clock_init_from_future_instant_smoke() {
use std::time::Duration;
use crate::MonotonicTime;
const OFFSET: Duration = Duration::from_secs(1000);
const TOLERANCE: Duration = Duration::from_millis(20);
let t0 = MonotonicTime::new(123, 456);
let clock = MonotonicClock::init_from_instant(t0, Instant::now() + OFFSET);
let delta = clock.now().duration_since(t0 - OFFSET);
assert!(delta <= TOLERANCE);
}
#[test]
fn clock_init_from_past_system_time_smoke() {
use std::time::Duration;
use crate::MonotonicTime;
const OFFSET: Duration = Duration::from_secs(1000);
const TOLERANCE: Duration = Duration::from_millis(20);
let t0 = MonotonicTime::new(123, 456);
let clock = MonotonicClock::init_from_system_time(t0, SystemTime::now() - OFFSET);
let delta = clock.now().duration_since(t0 + OFFSET);
assert!(delta <= TOLERANCE);
}
#[test]
fn clock_init_from_future_system_time_smoke() {
use std::time::Duration;
use crate::MonotonicTime;
const OFFSET: Duration = Duration::from_secs(1000);
const TOLERANCE: Duration = Duration::from_millis(20);
let t0 = MonotonicTime::new(123, 456);
let clock = MonotonicClock::init_from_system_time(t0, SystemTime::now() + OFFSET);
let delta = clock.now().duration_since(t0 - OFFSET);
assert!(delta <= TOLERANCE);
}
}