//! The car unit has no RTC battery: the clock boots stale and is stepped later by //! chrony (NTP or GNSS). Recordings therefore carry enough to correct timestamps //! afterwards — sync state at start, monotonic time, and every step observed. use serde_json::{json, Value}; fn clock(id: libc::clockid_t) -> f64 { let mut ts: libc::timespec = unsafe { std::mem::zeroed() }; unsafe { libc::clock_gettime(id, &mut ts) }; ts.tv_sec as f64 + ts.tv_nsec as f64 * 1e-9 } pub fn realtime() -> f64 { clock(libc::CLOCK_REALTIME) } pub fn monotonic() -> f64 { clock(libc::CLOCK_MONOTONIC) } /// realtime − monotonic. Constant while the clock only slews; a jump means a step. pub fn offset() -> f64 { realtime() - monotonic() } pub fn boot_id() -> String { std::fs::read_to_string("/proc/sys/kernel/random/boot_id").map(|s| s.trim().to_string()).unwrap_or_default() } /// Kernel view of synchronisation, read without privilege (adjtimex with modes = 0). pub fn sync_state() -> Value { let mut tx: libc::timex = unsafe { std::mem::zeroed() }; let state = unsafe { libc::adjtimex(&mut tx) }; let unsync = state == libc::TIME_ERROR || tx.status & libc::STA_UNSYNC != 0; json!({ "synced": !unsync, "maxerror_us": tx.maxerror, "esterror_us": tx.esterror }) } pub fn hostname() -> String { std::fs::read_to_string("/proc/sys/kernel/hostname").map(|s| s.trim().to_string()).unwrap_or_default() }