Skip to content

Commit

Permalink
Implemented proper support for TAI and UTC.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidv1992 committed Oct 25, 2023
1 parent 3850868 commit 2e5b836
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 67 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion statime-linux/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ rand = { version = "0.8.5", default-features = false, features = ["std", "std_rn
serde = { version = "1.0.189", features = ["derive"] }


clock-steering = { git = "https://github.com/pendulum-project/clock-steering.git", rev = "8ca7481" }
clock-steering = { git = "https://github.com/pendulum-project/clock-steering.git", rev = "716a540" }
timestamped-socket = { git = "https://github.com/pendulum-project/timestamped-socket.git", rev = "d0e4ed7", features = ["serde"] }
106 changes: 67 additions & 39 deletions statime-linux/src/clock/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,55 +7,55 @@ use statime::{Clock, Duration, Time, TimePropertiesDS};

#[derive(Debug, Clone)]
pub struct LinuxClock {
pub clock: clock_steering::unix::UnixClock,
clock: clock_steering::unix::UnixClock,
is_tai: bool,
}

impl LinuxClock {
pub const CLOCK_REALTIME: Self = Self {
clock: UnixClock::CLOCK_REALTIME,
pub const CLOCK_TAI: Self = Self {
clock: UnixClock::CLOCK_TAI,
is_tai: true,
};

pub fn open(path: impl AsRef<Path>) -> std::io::Result<Self> {
let clock = UnixClock::open(path)?;

Ok(Self { clock })
}
}

impl clock_steering::Clock for LinuxClock {
type Error = clock_steering::unix::Error;

fn now(&self) -> Result<clock_steering::Timestamp, Self::Error> {
self.clock.now()
}

fn resolution(&self) -> Result<clock_steering::Timestamp, Self::Error> {
self.clock.resolution()
}

fn set_frequency(&self, frequency: f64) -> Result<clock_steering::Timestamp, Self::Error> {
self.clock.set_frequency(frequency)
Ok(Self {
clock,
is_tai: false,
})
}

fn step_clock(&self, offset: TimeOffset) -> Result<clock_steering::Timestamp, Self::Error> {
self.clock.step_clock(offset)
/// Return three timestamps t1 t2 and t3 minted in that order.
/// T1 and T3 are minted using the system TAI clock and T2 by the hardware
/// clock
pub fn system_offset(&self) -> Result<(Time, Time, Time), clock_steering::unix::Error> {
// Clock crate's system offset gives the T1 and T3 timestamps on the
// CLOCK_REALTIME timescale which is UTC, not TAI, so we need to correct
// here.
self.clock.system_offset().map(|(mut t1, t2, mut t3)| {
let tai_offset = UnixClock::CLOCK_REALTIME.get_tai().unwrap();
t1.seconds += tai_offset as libc::time_t;
t3.seconds += tai_offset as libc::time_t;
(
clock_timestamp_to_time(t1),
clock_timestamp_to_time(t2),
clock_timestamp_to_time(t3),
)
})
}

fn set_leap_seconds(
&self,
leap_status: clock_steering::LeapIndicator,
) -> Result<(), Self::Error> {
self.clock.set_leap_seconds(leap_status)
pub fn get_tai_offset(&self) -> Result<i32, clock_steering::unix::Error> {
if self.is_tai {
UnixClock::CLOCK_REALTIME.get_tai()
} else {
self.clock.get_tai()
}
}
}

fn error_estimate_update(
&self,
estimated_error: std::time::Duration,
maximum_error: std::time::Duration,
) -> Result<(), Self::Error> {
self.clock
.error_estimate_update(estimated_error, maximum_error)
}
fn clock_timestamp_to_time(t: clock_steering::Timestamp) -> statime::Time {
Time::from_nanos((t.seconds as u64) * 1_000_000_000 + (t.nanos as u64))
}

fn time_from_timestamp(timestamp: clock_steering::Timestamp, fallback: Time) -> Time {
Expand All @@ -80,7 +80,15 @@ impl Clock for LinuxClock {
fn set_frequency(&mut self, freq: f64) -> Result<Time, Self::Error> {
use clock_steering::Clock;
log::trace!("Setting clock frequency to {:e}ppm", freq);
let timestamp = self.clock.set_frequency(freq)?;
let timestamp = if self.is_tai {
// Clock tai can't directly adjust frequency, so drive this through
// clock_realtime and adjust the received timestamp
let mut ts = UnixClock::CLOCK_REALTIME.set_frequency(freq)?;
ts.seconds += UnixClock::CLOCK_REALTIME.get_tai()? as libc::time_t;
ts
} else {
self.clock.set_frequency(freq)?
};
Ok(time_from_timestamp(timestamp, statime::Clock::now(self)))
}

Expand All @@ -103,12 +111,32 @@ impl Clock for LinuxClock {
(offset.seconds as f64) * 1e9 + (offset.nanos as f64)
);

let timestamp = self.clock.step_clock(offset)?;
let timestamp = if self.is_tai {
// Clock tai can't directly step, so drive this through clock_realtime
// and adjust the received timestamp
let mut ts = UnixClock::CLOCK_REALTIME.step_clock(offset)?;
ts.seconds += UnixClock::CLOCK_REALTIME.get_tai()? as libc::time_t;
ts
} else {
self.clock.step_clock(offset)?
};
Ok(time_from_timestamp(timestamp, statime::Clock::now(self)))
}

fn set_properties(&mut self, _time_properties: &TimePropertiesDS) -> Result<(), Self::Error> {
// For now just ignore these
fn set_properties(&mut self, time_properties: &TimePropertiesDS) -> Result<(), Self::Error> {
use clock_steering::Clock;

// These properties should always be communicated to the system clock.

if let Some(offset) = time_properties.utc_offset() {
UnixClock::CLOCK_REALTIME.set_tai(offset as _)?;
}

UnixClock::CLOCK_REALTIME.set_leap_seconds(match time_properties.leap_indicator() {
statime::LeapIndicator::NoLeap => clock_steering::LeapIndicator::NoWarning,
statime::LeapIndicator::Leap61 => clock_steering::LeapIndicator::Leap61,
statime::LeapIndicator::Leap59 => clock_steering::LeapIndicator::Leap59,
})?;

Ok(())
}
Expand Down
77 changes: 51 additions & 26 deletions statime-linux/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,6 @@ fn start_clock_task(clock: LinuxClock) -> tokio::sync::watch::Sender<ClockSyncMo
mode_sender
}

fn clock_timestamp_to_time(t: clock_steering::Timestamp) -> statime::Time {
Time::from_nanos((t.seconds as u64) * 1_000_000_000 + (t.nanos as u64))
}

async fn clock_task(
clock: LinuxClock,
mut mode_receiver: tokio::sync::watch::Receiver<ClockSyncMode>,
Expand All @@ -187,15 +183,12 @@ async fn clock_task(
let mut current_mode = *mode_receiver.borrow_and_update();
let mut filter_clock = match current_mode {
ClockSyncMode::FromSystem => clock.clone(),
ClockSyncMode::ToSystem => LinuxClock::CLOCK_REALTIME,
ClockSyncMode::ToSystem => LinuxClock::CLOCK_TAI,
};
loop {
tokio::select! {
() = &mut measurement_timer => {
let (t1, t2, t3) = clock.clock.system_offset().expect("Unable to determine offset from system clock");
let t1 = clock_timestamp_to_time(t1);
let t2 = clock_timestamp_to_time(t2);
let t3 = clock_timestamp_to_time(t3);
let (t1, t2, t3) = clock.system_offset().expect("Unable to determine offset from system clock");

log::debug!("Interclock measurement: {} {} {}", t1, t2, t3);

Expand Down Expand Up @@ -241,7 +234,7 @@ async fn clock_task(
new_filter.demobilize(&mut filter_clock);
match new_mode {
ClockSyncMode::FromSystem => filter_clock = clock.clone(),
ClockSyncMode::ToSystem => filter_clock = LinuxClock::CLOCK_REALTIME,
ClockSyncMode::ToSystem => filter_clock = LinuxClock::CLOCK_TAI,
}
current_mode = new_mode;
}
Expand Down Expand Up @@ -321,14 +314,11 @@ async fn actual_main() {
}
None => {
clock_port_map.push(None);
(
LinuxClock::CLOCK_REALTIME,
InterfaceTimestampMode::SoftwareAll,
)
(LinuxClock::CLOCK_TAI, InterfaceTimestampMode::SoftwareAll)
}
};
let rng = StdRng::from_entropy();
let port = instance.add_port(port_config.into(), 0.25, port_clock, rng);
let port = instance.add_port(port_config.into(), 0.25, port_clock.clone(), rng);

let (main_task_sender, port_task_receiver) = tokio::sync::mpsc::channel(1);
let (port_task_sender, main_task_receiver) = tokio::sync::mpsc::channel(1);
Expand All @@ -354,6 +344,7 @@ async fn actual_main() {
event_socket,
general_socket,
bmca_notify_receiver.clone(),
port_clock,
));
}
statime_linux::config::NetworkMode::Ipv6 => {
Expand All @@ -368,6 +359,7 @@ async fn actual_main() {
event_socket,
general_socket,
bmca_notify_receiver.clone(),
port_clock,
));
}
statime_linux::config::NetworkMode::Ethernet => {
Expand All @@ -382,6 +374,7 @@ async fn actual_main() {
.expect("Unable to get network interface index") as _,
socket,
bmca_notify_receiver.clone(),
port_clock,
));
}
}
Expand Down Expand Up @@ -474,6 +467,7 @@ async fn port_task<A: NetworkAddress + PtpTargetAddress>(
mut event_socket: Socket<A, Open>,
mut general_socket: Socket<A, Open>,
mut bmca_notify: tokio::sync::watch::Receiver<bool>,
clock: LinuxClock,
) {
let mut timers = Timers {
port_sync_timer: pin!(Timer::new()),
Expand All @@ -489,15 +483,22 @@ async fn port_task<A: NetworkAddress + PtpTargetAddress>(
// handle post-bmca actions
let (mut port, actions) = port_in_bmca.end_bmca();

let mut pending_timestamp =
handle_actions(actions, &mut event_socket, &mut general_socket, &mut timers).await;
let mut pending_timestamp = handle_actions(
actions,
&mut event_socket,
&mut general_socket,
&mut timers,
&clock,
)
.await;

while let Some((context, timestamp)) = pending_timestamp {
pending_timestamp = handle_actions(
port.handle_send_timestamp(context, timestamp),
&mut event_socket,
&mut general_socket,
&mut timers,
&clock,
)
.await;
}
Expand All @@ -509,7 +510,10 @@ async fn port_task<A: NetworkAddress + PtpTargetAddress>(
let mut actions = tokio::select! {
result = event_socket.recv(&mut event_buffer) => match result {
Ok(packet) => {
if let Some(timestamp) = packet.timestamp {
if let Some(mut timestamp) = packet.timestamp {
// get_tai gives zero if this is a hardware clock, and the needed
// correction when this port uses software timestamping
timestamp.seconds += clock.get_tai_offset().expect("Unable to get tai offset") as libc::time_t;
log::trace!("Recv timestamp: {:?}", packet.timestamp);
port.handle_event_receive(&event_buffer[..packet.bytes_read], timestamp_to_time(timestamp))
} else {
Expand Down Expand Up @@ -545,9 +549,14 @@ async fn port_task<A: NetworkAddress + PtpTargetAddress>(
};

loop {
let pending_timestamp =
handle_actions(actions, &mut event_socket, &mut general_socket, &mut timers)
.await;
let pending_timestamp = handle_actions(
actions,
&mut event_socket,
&mut general_socket,
&mut timers,
&clock,
)
.await;

// there might be more actions to handle based on the current action
actions = match pending_timestamp {
Expand All @@ -574,6 +583,7 @@ async fn ethernet_port_task(
interface: libc::c_int,
mut socket: Socket<EthernetAddress, Open>,
mut bmca_notify: tokio::sync::watch::Receiver<bool>,
clock: LinuxClock,
) {
let mut timers = Timers {
port_sync_timer: pin!(Timer::new()),
Expand All @@ -590,14 +600,15 @@ async fn ethernet_port_task(
let (mut port, actions) = port_in_bmca.end_bmca();

let mut pending_timestamp =
handle_actions_ethernet(actions, interface, &mut socket, &mut timers).await;
handle_actions_ethernet(actions, interface, &mut socket, &mut timers, &clock).await;

while let Some((context, timestamp)) = pending_timestamp {
pending_timestamp = handle_actions_ethernet(
port.handle_send_timestamp(context, timestamp),
interface,
&mut socket,
&mut timers,
&clock,
)
.await;
}
Expand All @@ -608,7 +619,10 @@ async fn ethernet_port_task(
let mut actions = tokio::select! {
result = socket.recv(&mut event_buffer) => match result {
Ok(packet) => {
if let Some(timestamp) = packet.timestamp {
if let Some(mut timestamp) = packet.timestamp {
// get_tai gives zero if this is a hardware clock, and the needed
// correction when this port uses software timestamping
timestamp.seconds += clock.get_tai_offset().expect("Unable to get tai offset") as libc::time_t;
log::trace!("Recv timestamp: {:?}", packet.timestamp);
port.handle_event_receive(&event_buffer[..packet.bytes_read], timestamp_to_time(timestamp))
} else {
Expand Down Expand Up @@ -640,7 +654,8 @@ async fn ethernet_port_task(

loop {
let pending_timestamp =
handle_actions_ethernet(actions, interface, &mut socket, &mut timers).await;
handle_actions_ethernet(actions, interface, &mut socket, &mut timers, &clock)
.await;

// there might be more actions to handle based on the current action
actions = match pending_timestamp {
Expand Down Expand Up @@ -668,6 +683,7 @@ async fn handle_actions<A: NetworkAddress + PtpTargetAddress>(
event_socket: &mut Socket<A, Open>,
general_socket: &mut Socket<A, Open>,
timers: &mut Timers<'_>,
clock: &LinuxClock,
) -> Option<(TimestampContext, Time)> {
let mut pending_timestamp = None;

Expand All @@ -681,7 +697,11 @@ async fn handle_actions<A: NetworkAddress + PtpTargetAddress>(
.expect("Failed to send event message");

// anything we send later will have a later pending (send) timestamp
if let Some(time) = time {
if let Some(mut time) = time {
// get_tai gives zero if this is a hardware clock, and the needed
// correction when this port uses software timestamping
time.seconds +=
clock.get_tai_offset().expect("Unable to get tai offset") as libc::time_t;
log::trace!("Send timestamp {:?}", time);
pending_timestamp = Some((context, timestamp_to_time(time)));
} else {
Expand Down Expand Up @@ -720,6 +740,7 @@ async fn handle_actions_ethernet(
interface: libc::c_int,
socket: &mut Socket<EthernetAddress, Open>,
timers: &mut Timers<'_>,
clock: &LinuxClock,
) -> Option<(TimestampContext, Time)> {
let mut pending_timestamp = None;

Expand All @@ -740,7 +761,11 @@ async fn handle_actions_ethernet(
.expect("Failed to send event message");

// anything we send later will have a later pending (send) timestamp
if let Some(time) = time {
if let Some(mut time) = time {
// get_tai gives zero if this is a hardware clock, and the needed
// correction when this port uses software timestamping
time.seconds +=
clock.get_tai_offset().expect("Unable to get tai offset") as libc::time_t;
log::trace!("Send timestamp {:?}", time);
pending_timestamp = Some((context, timestamp_to_time(time)));
} else {
Expand Down
4 changes: 4 additions & 0 deletions statime/src/datastructures/datasets/time_properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,8 @@ impl TimePropertiesDS {
pub fn leap_indicator(&self) -> LeapIndicator {
self.leap_indicator
}

pub fn utc_offset(&self) -> Option<i16> {
self.current_utc_offset
}
}

0 comments on commit 2e5b836

Please sign in to comment.