clock_steering/lib.rs
1//! Logic for steering OS clocks, aimed at NTP and PTP.
2//!
3//! This code is used in our implementations of NTP [ntpd-rs](https://github.com/pendulum-project/ntpd-rs) and PTP [statime](https://github.com/pendulum-project/statime).
4use core::time::Duration;
5
6#[cfg(unix)]
7pub mod unix;
8
9/// A moment in time.
10///
11/// The format makes it easy to convert into libc data structures, and supports subnanoseconds that
12/// certain hardware can provide for additional precision. The value is an offset from the [unix epoch](https://en.wikipedia.org/wiki/Unix_time).
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
14pub struct Timestamp {
15 pub seconds: libc::time_t,
16 /// Nanos must be between 0 and 999999999 inclusive
17 pub nanos: u32,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
21pub struct TimeOffset {
22 pub seconds: libc::time_t,
23 /// Nanos must be between 0 and 999999999 inclusive
24 pub nanos: u32,
25}
26
27/// Indicate whether a leap second must be applied
28#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
29pub enum LeapIndicator {
30 /// No leap second warning
31 #[default]
32 NoWarning,
33 /// Last minute of the day has 61 seconds
34 Leap61,
35 /// Last minute of the day has 59 seconds
36 Leap59,
37 /// Unknown leap second status (the clock is unsynchronized)
38 Unknown,
39}
40
41/// Trait for reading information from and modifying an OS clock
42pub trait Clock {
43 type Error: std::error::Error;
44
45 // feature(error_in_core) https://github.com/rust-lang/rust/issues/103765
46 // type Error: core::error::Error;
47
48 /// Get the current time.
49 fn now(&self) -> Result<Timestamp, Self::Error>;
50
51 /// Get the clock's resolution.
52 ///
53 /// The output [`Timestamp`] will be all zeros when the resolution is
54 /// unavailable.
55 fn resolution(&self) -> Result<Timestamp, Self::Error>;
56
57 /// Change the frequency of the clock.
58 /// Returns the time at which the change was applied.
59 ///
60 /// The unit of the input is milliseconds (of drift) per second,
61 /// compared to the "natural" frequency of the clock.
62 fn set_frequency(&self, frequency: f64) -> Result<Timestamp, Self::Error>;
63
64 /// Get the frequency of the clock
65 /// The unit of the output is milliseconds (of drift) per second,
66 /// compared to the "natural" frequency of the clock.
67 fn get_frequency(&self) -> Result<f64, Self::Error>;
68
69 /// Change the current time of the clock by an offset.
70 /// Returns the time at which the change was applied.
71 fn step_clock(&self, offset: TimeOffset) -> Result<Timestamp, Self::Error>;
72
73 /// Change the indicators for upcoming leap seconds.
74 fn set_leap_seconds(&self, leap_status: LeapIndicator) -> Result<(), Self::Error>;
75
76 /// Disable all standard NTP kernel clock discipline. It is all your responsibility now.
77 ///
78 /// The disabled settings are:
79 ///
80 /// - [`libc::STA_PLL`]: kernel phase-locked loop
81 /// - [`libc::STA_FLL`]: kernel frequency-locked loop
82 /// - [`libc::STA_PPSTIME`]: pulse-per-second time
83 /// - [`libc::STA_PPSFREQ`]: pulse-per-second frequency discipline
84 fn disable_kernel_ntp_algorithm(&self) -> Result<(), Self::Error>;
85
86 /// Set the offset between TAI and UTC.
87 fn set_tai(&self, tai_offset: i32) -> Result<(), Self::Error>;
88
89 /// Get the offset between TAI and UTC.
90 fn get_tai(&self) -> Result<i32, Self::Error>;
91
92 /// Provide the system with the current best estimates for the statistical
93 /// error of the clock, and the maximum deviation due to frequency error and
94 /// distance to the root clock.
95 fn error_estimate_update(
96 &self,
97 estimated_error: Duration,
98 maximum_error: Duration,
99 ) -> Result<(), Self::Error>;
100}