Clock & Time Messages

HORUS provides clock and time synchronization messages for simulation, replay, and multi-sensor time alignment. Both types are fixed-size with #[repr(C)] layout for zero-copy shared memory transport.

Clock

Simulation and replay time source. Allows nodes to operate in simulated time instead of wall-clock time.

use horus::prelude::*;

// Real-time wall clock
let wall = Clock::wall_clock();

// Simulation time at 2x speed
let sim = Clock::sim_time(5_000_000_000, 2.0);  // 5 seconds sim time, 2x speed
println!("Sim time: {} ns", sim.clock_ns);
println!("Speed: {}x", sim.sim_speed);

// Replay time (playing back recorded data)
let replay = Clock::replay_time(10_000_000_000, 0.5);  // 10s replay time, half speed

// Pause/resume
let paused = sim.set_paused(true);
println!("Paused: {}", paused.is_paused());

// Measure elapsed time between clock messages
let earlier = Clock::sim_time(1_000_000_000, 1.0);
let later = Clock::sim_time(2_000_000_000, 1.0);
let elapsed = later.elapsed_since(&earlier);
println!("Elapsed: {} ns", elapsed);

Fields (40 bytes, #[repr(C)]):

FieldTypeDescription
clock_nsu64Current simulation/replay time in nanoseconds
realtime_nsu64Wall-clock time for comparison
sim_speedf64Playback speed (1.0 = real-time, 2.0 = 2x, 0.5 = half)
pausedu80 = running, 1 = paused
sourceu8Time source (see constants)
timestamp_nsu64When this message was published

Source Constants:

ConstantValueDescription
SOURCE_WALL0Wall-clock (real time)
SOURCE_SIM1Simulation time
SOURCE_REPLAY2Replay/playback time

Methods:

MethodReturnsDescription
wall_clock()ClockCreate real-time clock
sim_time(sim_ns, speed)ClockCreate simulation time clock
replay_time(replay_ns, speed)ClockCreate replay time clock
elapsed_since(&earlier)u64Nanoseconds between two clock messages
is_paused()boolCheck if clock is paused
set_paused(paused)ClockReturn new clock with paused state (builder)

TimeReference

External time source for synchronization between sensors or between local clock and GPS/NTP/PTP time.

use horus::prelude::*;

// GPS time reference with offset
let time_ref = TimeReference::new(
    1_709_000_000_000_000_000,  // GPS time in nanoseconds
    "gps",                       // source name
    -500_000,                    // offset: local is 500µs behind GPS
);

println!("Source: {}", time_ref.source_name());

// Correct a local timestamp using the reference offset
let local_ts = 1_709_000_001_000_000_000;
let corrected = time_ref.correct_timestamp(local_ts);
println!("Corrected timestamp: {} ns", corrected);

Fields (56 bytes, #[repr(C)]):

FieldTypeDescription
time_ref_nsu64External reference time in nanoseconds
source[u8; 32]Source identifier (null-terminated): "gps", "ntp", "ptp"
offset_nsi64Signed offset: local_time - reference_time (nanoseconds)
timestamp_nsu64When this message was published

Methods:

MethodReturnsDescription
new(time_ref_ns, source, offset_ns)TimeReferenceCreate time reference
source_name()&strGet source identifier as string
correct_timestamp(local_ns)u64Apply offset to correct a local timestamp

Simulation Time Node Example

use horus::prelude::*;

struct SimAwareNode {
    clock_sub: Topic<Clock>,
    current_time_ns: u64,
    is_paused: bool,
}

impl Node for SimAwareNode {
    fn name(&self) -> &str { "SimAwareNode" }

    fn tick(&mut self) {
        // Read the latest clock message
        if let Some(clock) = self.clock_sub.recv() {
            self.is_paused = clock.is_paused();
            self.current_time_ns = clock.clock_ns;

            // Skip processing when paused
            if self.is_paused {
                return;
            }

            // Use sim time for physics, animation, etc.
            let sim_seconds = self.current_time_ns as f64 / 1e9;
            println!("Sim time: {:.3}s (speed: {}x)", sim_seconds, clock.sim_speed);
        }
    }
}

Time Synchronization Example

use horus::prelude::*;

struct TimeSyncNode {
    gps_time_sub: Topic<TimeReference>,
    ntp_time_sub: Topic<TimeReference>,
    best_offset_ns: i64,
}

impl Node for TimeSyncNode {
    fn name(&self) -> &str { "TimeSync" }

    fn tick(&mut self) {
        // Prefer GPS time when available
        if let Some(gps) = self.gps_time_sub.recv() {
            self.best_offset_ns = gps.offset_ns;
        } else if let Some(ntp) = self.ntp_time_sub.recv() {
            self.best_offset_ns = ntp.offset_ns;
        }
    }
}

See Also