feat(adr-110 P10): apply_to_local + NodeState::mesh_aligned_us + full ADR rewrite

Iter 16 closes the math loop and updates ADR-110 to reflect the full
P1-P10 sprint outcome (per user request).

Code (the math layer that converts the iter 15 stored sync into a
per-frame mesh-aligned timestamp):

  wifi-densepose-hardware:
    SyncPacket::apply_to_local(local_at_frame_us: u64) -> u64
      Pure integer math: offset = epoch - local; mesh = local_at_frame + offset.
      3 new unit tests (10 total, all green):
      - apply_to_local_recovers_packet_epoch (identity at the packet's local_us)
      - apply_to_local_preserves_inter_frame_delta (Δlocal == Δmesh)
      - apply_to_local_on_leader_is_near_identity (leader offset ≈ 0)

  wifi-densepose-sensing-server:
    NodeState::mesh_aligned_us(local_at_frame_us: u64) -> Option<u64>
      Returns the recovered mesh timestamp using the most-recent sync
      packet, or None if no sync seen or last one older than 9 s
      (3× firmware VALID_WINDOW_MS = 9 s staleness gate).
      cargo check -p wifi-densepose-sensing-server --no-default-features
        → green

ADR-110 substantial rewrite (per user "update adr 110 with details"):

  - Status line: P1-P10 complete, firmware-side substrate closed at v0.7.0.
  - Front matter now lists all 4 firmware releases + witness link.
  - Phase table grows a P10 row capturing the v0.6.8 / v0.6.9 / v0.7.0
    arc (EMA smoother + sync packet + bit-4 wire-fix + host crates).
  - New §4.1 — /loop 5m SOTA sprint summary table (iters 1-16, 4 releases,
    17 commits, 13 unit tests, what shipped each iter).
  - New §4.2 — measured numbers table with 99.56% RX, 104.1 µs smoothed
    stdev, 3.95x suppression, 1.4 ppm crystal skew, etc — every cell
    backed by a witness §A0.x entry and a preserved bench log.
  - New §4.3 — host-side production surface listing (sync_packet.rs +
    sensing-server NodeState + Python parser, with file paths).
  - §5 open question on 802.15.4 channel resolved (Kconfig, default ch26
    not ch15, with the witness §D1 rationale).
  - New §6 — explicit scope of what's outside this ADR (multistatic fusion
    math in ADR-029/030, hardware-gated measurements needing INA / 11ax AP,
    IDF upstream fixes pending).

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv
2026-05-23 13:16:11 -04:00
parent 23fd8ac371
commit df95360e52
3 changed files with 161 additions and 3 deletions
@@ -121,6 +121,34 @@ impl SyncPacket {
(self.local_us as i64) - (self.epoch_us as i64)
}
/// Given a CSI frame's node-local `esp_timer_get_time()` snapshot,
/// recover the mesh-aligned timestamp using this sync packet as the
/// reference point.
///
/// Math (all in node-local µs, see ADR-110 §A0.12):
///
/// ```text
/// offset = epoch_us - local_us (signed; this packet)
/// mesh_epoch(frame) = local_at_frame_us + offset
/// = local_at_frame_us + (epoch_us - local_us)
/// ```
///
/// On the leader this gives `≈ local_at_frame_us`. On a follower this
/// gives the mesh-aligned time aligned to the leader's clock within
/// the §A0.10 measured 104 µs stdev (the same EMA-smoothed offset
/// the firmware applied when it built this sync packet's `epoch_us`).
///
/// Use this on the host side whenever a CSI frame arrives with
/// ADR-018 byte 19 bit 4 set: look up the matching node's most-recent
/// `SyncPacket`, call `apply_to_local(frame.local_us)`, stamp the
/// result on the frame for downstream multistatic fusion.
pub fn apply_to_local(&self, local_at_frame_us: u64) -> u64 {
// Compute the offset as a signed delta in the µs domain. Adding it
// back to the frame's local snapshot recovers the mesh epoch.
let offset = (self.epoch_us as i64).wrapping_sub(self.local_us as i64);
(local_at_frame_us as i64).wrapping_add(offset) as u64
}
/// Serialize back to wire bytes (32 bytes, little-endian).
pub fn to_bytes(&self) -> [u8; SYNC_PACKET_SIZE] {
let mut out = [0u8; SYNC_PACKET_SIZE];
@@ -234,6 +262,53 @@ mod tests {
assert_ne!(SYNC_PACKET_MAGIC, crate::esp32_parser::ESP32_CSI_MAGIC);
}
/// Applying a sync packet to its own local_us must recover its own
/// epoch_us. Foundational identity for the math.
#[test]
fn apply_to_local_recovers_packet_epoch() {
let pkt = SyncPacket {
node_id: 9, proto_ver: 1,
flags: SyncPacketFlags { is_leader: false, is_valid: true, smoothed_used: true },
local_us: 28_798_450, epoch_us: 27_634_885, sequence: 20,
};
assert_eq!(pkt.apply_to_local(pkt.local_us), pkt.epoch_us);
}
/// A CSI frame's local timestamp arriving after the sync packet
/// gets the same offset applied — the µs delta between sync and frame
/// is preserved on both clocks.
#[test]
fn apply_to_local_preserves_inter_frame_delta() {
let pkt = SyncPacket {
node_id: 9, proto_ver: 1,
flags: SyncPacketFlags { is_leader: false, is_valid: true, smoothed_used: true },
local_us: 28_798_450, epoch_us: 27_634_885, sequence: 20,
};
// Frame arrives 100 ms after the sync packet on the follower's local clock.
let local_at_frame = pkt.local_us + 100_000;
let mesh_epoch = pkt.apply_to_local(local_at_frame);
// Mesh epoch should also be 100 ms after the sync packet's epoch.
assert_eq!(mesh_epoch, pkt.epoch_us + 100_000);
// Offset must equal local - epoch on both clocks.
assert_eq!(local_at_frame - mesh_epoch, pkt.local_us - pkt.epoch_us);
}
/// Leader sync packet has near-zero offset, so apply_to_local is
/// approximately identity (modulo the few µs call-stack delta).
#[test]
fn apply_to_local_on_leader_is_near_identity() {
let pkt = SyncPacket {
node_id: 12, proto_ver: 1,
flags: SyncPacketFlags { is_leader: true, is_valid: true, smoothed_used: false },
local_us: 28_864_932, epoch_us: 28_864_939, sequence: 20,
};
let frame_local = 30_000_000u64;
let mesh = pkt.apply_to_local(frame_local);
assert!((mesh as i64 - frame_local as i64).abs() <= 100,
"leader apply should be within 100 µs of identity, got {} delta",
mesh as i64 - frame_local as i64);
}
#[test]
fn wire_size_constant_is_correct() {
let pkt = SyncPacket {
@@ -419,6 +419,23 @@ const NOVELTY_HISTORY_CAPACITY: usize = 64;
const NOVELTY_SKETCH_VERSION: u16 = 1;
impl NodeState {
/// ADR-110 §A0.12 timestamp recovery: given a CSI frame's node-local
/// `esp_timer_get_time()` snapshot, return the mesh-aligned epoch
/// computed from this node's most recent sync packet — or `None`
/// if no sync has been received yet, or the last one is too stale
/// (older than 3 × VALID_WINDOW_MS = 9 s, matching the firmware's own
/// staleness gate).
pub(crate) fn mesh_aligned_us(&self, local_at_frame_us: u64) -> Option<u64> {
let sync = self.latest_sync.as_ref()?;
let seen_at = self.latest_sync_at?;
// Drop stale syncs — firmware emits at ~0.5 Hz default, anything
// older than 9 s likely means the mesh transport dropped.
if seen_at.elapsed() > std::time::Duration::from_secs(9) {
return None;
}
Some(sync.apply_to_local(local_at_frame_us))
}
pub(crate) fn new() -> Self {
Self {
frame_history: VecDeque::new(),