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)]):
| Field | Type | Description |
|---|---|---|
clock_ns | u64 | Current simulation/replay time in nanoseconds |
realtime_ns | u64 | Wall-clock time for comparison |
sim_speed | f64 | Playback speed (1.0 = real-time, 2.0 = 2x, 0.5 = half) |
paused | u8 | 0 = running, 1 = paused |
source | u8 | Time source (see constants) |
timestamp_ns | u64 | When this message was published |
Source Constants:
| Constant | Value | Description |
|---|---|---|
SOURCE_WALL | 0 | Wall-clock (real time) |
SOURCE_SIM | 1 | Simulation time |
SOURCE_REPLAY | 2 | Replay/playback time |
Methods:
| Method | Returns | Description |
|---|---|---|
wall_clock() | Clock | Create real-time clock |
sim_time(sim_ns, speed) | Clock | Create simulation time clock |
replay_time(replay_ns, speed) | Clock | Create replay time clock |
elapsed_since(&earlier) | u64 | Nanoseconds between two clock messages |
is_paused() | bool | Check if clock is paused |
set_paused(paused) | Clock | Return 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)]):
| Field | Type | Description |
|---|---|---|
time_ref_ns | u64 | External reference time in nanoseconds |
source | [u8; 32] | Source identifier (null-terminated): "gps", "ntp", "ptp" |
offset_ns | i64 | Signed offset: local_time - reference_time (nanoseconds) |
timestamp_ns | u64 | When this message was published |
Methods:
| Method | Returns | Description |
|---|---|---|
new(time_ref_ns, source, offset_ns) | TimeReference | Create time reference |
source_name() | &str | Get source identifier as string |
correct_timestamp(local_ns) | u64 | Apply 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
- Sensor Messages - NavSatFix for GPS data
- Diagnostics Messages - Heartbeat for system health