statime_linux/clock/
mod.rs1use std::path::Path;
4
5use clock_steering::{unix::UnixClock, TimeOffset};
6use statime::{
7 config::{LeapIndicator, TimePropertiesDS},
8 time::{Duration, Time},
9 Clock, OverlayClock, SharedClock,
10};
11
12#[derive(Debug, Clone)]
13pub struct LinuxClock {
14 clock: clock_steering::unix::UnixClock,
15 is_tai: bool,
16}
17
18impl LinuxClock {
19 pub const CLOCK_TAI: Self = Self {
20 clock: UnixClock::CLOCK_TAI,
21 is_tai: true,
22 };
23
24 pub fn open(path: impl AsRef<Path>) -> std::io::Result<Self> {
25 let clock = UnixClock::open(path)?;
26
27 Ok(Self {
28 clock,
29 is_tai: false,
30 })
31 }
32
33 pub fn init(&mut self) -> Result<(), clock_steering::unix::Error> {
36 use clock_steering::Clock;
37
38 let ts = self.clock.now()?;
39 #[allow(clippy::unnecessary_cast)]
40 if ts.seconds < 0 || ts.seconds as i64 > (u64::MAX / 1000000000) as i64 {
41 self.clock.step_clock(TimeOffset {
42 seconds: -ts.seconds + 1,
43 nanos: 0,
44 })?;
45 }
46
47 Ok(())
48 }
49
50 pub fn open_idx(idx: u32) -> std::io::Result<Self> {
51 let path = format!("/dev/ptp{}", idx);
52 Self::open(path)
53 }
54
55 pub fn system_offset(&self) -> Result<(Time, Time, Time), clock_steering::unix::Error> {
59 self.clock.system_offset().map(|(mut t1, t2, mut t3)| {
63 use clock_steering::Clock;
64 let tai_offset = UnixClock::CLOCK_REALTIME.get_tai().unwrap();
65 t1.seconds += tai_offset as libc::time_t;
66 t3.seconds += tai_offset as libc::time_t;
67 (
68 clock_timestamp_to_time(t1),
69 clock_timestamp_to_time(t2),
70 clock_timestamp_to_time(t3),
71 )
72 })
73 }
74
75 pub fn get_tai_offset(&self) -> Result<i32, clock_steering::unix::Error> {
76 use clock_steering::Clock;
77 if self.is_tai {
78 UnixClock::CLOCK_REALTIME.get_tai()
79 } else {
80 self.clock.get_tai()
81 }
82 }
83}
84
85fn clock_timestamp_to_time(t: clock_steering::Timestamp) -> Time {
86 Time::from_nanos((t.seconds as u64) * 1_000_000_000 + (t.nanos as u64))
87}
88
89fn time_from_timestamp(timestamp: clock_steering::Timestamp, fallback: Time) -> Time {
90 let Ok(seconds): Result<u64, _> = timestamp.seconds.try_into() else {
91 return fallback;
92 };
93
94 let nanos = seconds * 1_000_000_000 + timestamp.nanos as u64;
95 Time::from_nanos_subnanos(nanos, 0)
96}
97
98impl Clock for LinuxClock {
99 type Error = clock_steering::unix::Error;
100
101 fn now(&self) -> Time {
102 use clock_steering::Clock;
103
104 let timestamp = self.clock.now().unwrap();
105 time_from_timestamp(timestamp, Time::from_fixed_nanos(0))
106 }
107
108 fn set_frequency(&mut self, freq: f64) -> Result<Time, Self::Error> {
109 use clock_steering::Clock;
110 log::trace!("Setting clock frequency to {:e}ppm", freq);
111 let timestamp = if self.is_tai {
112 let mut ts = UnixClock::CLOCK_REALTIME.set_frequency(freq)?;
115 ts.seconds += UnixClock::CLOCK_REALTIME.get_tai()? as libc::time_t;
116 ts
117 } else {
118 self.clock.set_frequency(freq)?
119 };
120 Ok(time_from_timestamp(timestamp, statime::Clock::now(self)))
121 }
122
123 fn step_clock(&mut self, time_offset: Duration) -> Result<Time, Self::Error> {
124 use clock_steering::Clock;
125
126 let offset_nanos: i128 = time_offset.nanos_rounded();
129 let offset = TimeOffset {
130 seconds: offset_nanos
131 .div_euclid(1_000_000_000)
132 .try_into()
133 .expect("Unexpected jump larger than 2^64 seconds"),
134 nanos: offset_nanos.rem_euclid(1_000_000_000) as _, };
136
137 log::trace!(
138 "Stepping clock {:e}ns",
139 (offset.seconds as f64) * 1e9 + (offset.nanos as f64)
140 );
141
142 let timestamp = if self.is_tai {
143 let mut ts = UnixClock::CLOCK_REALTIME.step_clock(offset)?;
146 ts.seconds += UnixClock::CLOCK_REALTIME.get_tai()? as libc::time_t;
147 ts
148 } else {
149 self.clock.step_clock(offset)?
150 };
151 Ok(time_from_timestamp(timestamp, statime::Clock::now(self)))
152 }
153
154 fn set_properties(&mut self, time_properties: &TimePropertiesDS) -> Result<(), Self::Error> {
155 use clock_steering::Clock;
156
157 if let Some(offset) = time_properties.utc_offset() {
160 UnixClock::CLOCK_REALTIME.set_tai(offset as _)?;
161 }
162
163 UnixClock::CLOCK_REALTIME.set_leap_seconds(match time_properties.leap_indicator() {
164 LeapIndicator::NoLeap => clock_steering::LeapIndicator::NoWarning,
165 LeapIndicator::Leap61 => clock_steering::LeapIndicator::Leap61,
166 LeapIndicator::Leap59 => clock_steering::LeapIndicator::Leap59,
167 })?;
168
169 Ok(())
170 }
171}
172
173pub fn libc_timespec_into_instant(spec: libc::timespec) -> Time {
174 Time::from_fixed_nanos(spec.tv_sec as i128 * 1_000_000_000i128 + spec.tv_nsec as i128)
175}
176
177pub trait PortTimestampToTime {
178 fn port_timestamp_to_time(&self, ts: timestamped_socket::socket::Timestamp) -> Time;
179}
180
181impl PortTimestampToTime for LinuxClock {
182 fn port_timestamp_to_time(&self, mut ts: timestamped_socket::socket::Timestamp) -> Time {
183 ts.seconds += self.get_tai_offset().expect("Unable to get tai offset") as i64;
186 Time::from_fixed_nanos(ts.seconds as i128 * 1_000_000_000i128 + ts.nanos as i128)
187 }
188}
189
190impl PortTimestampToTime for OverlayClock<LinuxClock> {
191 fn port_timestamp_to_time(&self, ts: timestamped_socket::socket::Timestamp) -> Time {
192 let roclock_time = self.underlying().port_timestamp_to_time(ts);
193 self.time_from_underlying(roclock_time)
194 }
195}
196
197impl PortTimestampToTime for SharedClock<OverlayClock<LinuxClock>> {
198 fn port_timestamp_to_time(&self, ts: timestamped_socket::socket::Timestamp) -> Time {
199 self.0.lock().unwrap().port_timestamp_to_time(ts)
200 }
201}