feat(adr-106): server-side µs timestamp on raw-CSI ingest

Closes the first ADR-106 open item without an FW change. On every
raw-CSI frame we now stamp `ns.latest_timestamp_us` with
SystemTime::now() in µs since UNIX epoch. NodeInfo.timestamp_us
surfaces it on WS via the already-wired skip_serializing_if guard.

Accuracy is wall-clock + Mac monotonic + LAN jitter ≈ ~1 ms. Verified
cross-node skew ts(node1) - ts(node2) = 1556 µs in a single test, well
within the 5-10 ms tolerance needed for FFT-based vital-signs
correlation across sensors.

Sensor-side ESP-IDF rx_ctrl.timestamp (true RX-time µs) is still
better and remains on the open list for a future FW header bump
(reserved bytes [18..19] are only 2 of the 4 we'd need — header
extension required, opt-in via new magic).
This commit is contained in:
arsen 2026-05-17 12:04:11 +07:00
parent c6208621b5
commit 68068d73d8
1 changed files with 12 additions and 0 deletions

View File

@ -4909,6 +4909,18 @@ async fn udp_receiver_task(state: SharedState, udp_port: u16) {
}
ns.latest_noise_floor = frame.noise_floor;
ns.latest_n_antennas = frame.n_antennas;
// ADR-106 follow-up: server-side receive timestamp
// in µs since UNIX epoch. Not as precise as
// sensor-side `info->rx_ctrl.timestamp` would be,
// but good enough for cross-node alignment within
// ~1 ms (Mac monotonic + LAN jitter). Sensor-side
// timestamp deferred to a future FW change that
// extends the 0xC511_0001 header — see ADR-106
// Open Items.
ns.latest_timestamp_us = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_micros() as u64)
.unwrap_or(0);
let sample_rate_hz = 1000.0 / 500.0_f64;
let (features, mut classification, breathing_rate_hz, sub_variances, raw_motion) =