Iter 19 — without this call, iter 18's EMA fps tracking was dead code
because csi_fps_samples stayed 0 forever and mesh_aligned_us_for_csi_frame
always fell back to the 20 Hz constant.
In udp_receiver_task's parse_esp32_frame branch, replace the bare
last_frame_time assignment with NodeState::observe_csi_frame_arrival,
which computes dt vs last_frame_time, feeds update_csi_fps_ema (α=1/8),
bumps csi_fps_samples, and sets last_frame_time as a side effect (same
value the bare assignment did).
Effect: after ~5 CSI frames arrive from any node, mesh_aligned_us_for_csi_frame
returns interpolated timestamps using the node's actually-observed frame
rate instead of the 20 Hz default. Real bench rate was ~10 fps, so this
halves the per-frame timestamp error in §A0.12-style multistatic alignment.
cargo check -p wifi-densepose-sensing-server --no-default-features → green.
Co-Authored-By: claude-flow <ruv@ruv.net>