Files
ruvnet--RuView/v2/crates/wifi-densepose-sensing-server/Cargo.toml
T
ruv 2997165bc1 feat(adr-110): per-node measured CSI fps + EMA for mesh-time interpolation
Iter 18 (after recovery from a cross-branch slip — see commit-history
context below). Replaces the hardcoded 20 Hz CSI_FPS_HZ constant in
mesh_aligned_us_for_csi_frame with a per-node EMA of observed
inter-frame intervals, falling back to 20 Hz until ≥5 samples land.

Real bench data (§A0.12 captures) showed the actual CSI rate at ~10 fps
because the firmware's CSI_MIN_SEND_INTERVAL_US gate combined with
ruv.net's traffic level paces it to that. Using 20 Hz against actual
10 fps inflates Δus 2× and shifts the recovered mesh timestamp by up
to the inter-sync interval / 2 = ~1 s. Measured fps fixes that.

State on NodeState:
  csi_fps_ema:     f64    — EMA (seeded at 20.0)
  csi_fps_samples: u32    — counts inter-frame deltas observed

API:
  NodeState::observe_csi_frame_arrival(now)  — call once per CSI frame
                                               from udp_receiver_task
  update_csi_fps_ema(prev_fps, dt_sec) -> Option<f64>  — free fn,
                                                          testable

mesh_aligned_us_for_csi_frame now uses the measured fps when samples ≥ 5,
falls back to 20 Hz otherwise.

4 unit tests (fps_ema_tests module, all passing on the binary):
  * steady_10hz_converges_toward_10  — 40 samples at 100 ms converge to ±0.1 Hz
  * steady_20hz_stays_near_20        — 20 samples at 50 ms stay within 0.05 Hz
  * nonpositive_dt_rejected          — dt ≤ 0 returns None
  * long_gap_rejected_as_implausible — dt > 1 s rejected (likely a dropout)

Branch-coordination note: this iter's working tree was briefly applied
to feat/adr-115-ha-mqtt-matter by a `git checkout` between iter 17 and
iter 18. Stashed the ADR-115 agent's MQTT/Matter Cargo.toml work
(`stash@adr115-pending-work`) before switching back here. No code lost.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 13:30:02 -04:00

95 lines
3.7 KiB
TOML

[package]
name = "wifi-densepose-sensing-server"
version.workspace = true
edition.workspace = true
description = "Lightweight Axum server for WiFi sensing UI with RuVector signal processing"
license.workspace = true
authors = ["rUv <ruv@ruv.net>", "WiFi-DensePose Contributors"]
repository.workspace = true
documentation = "https://docs.rs/wifi-densepose-sensing-server"
keywords = ["wifi", "sensing", "server", "websocket", "csi"]
categories = ["web-programming::http-server", "science"]
readme = "README.md"
[lib]
name = "wifi_densepose_sensing_server"
path = "src/lib.rs"
[[bin]]
name = "sensing-server"
path = "src/main.rs"
[dependencies]
# Web framework
axum = { workspace = true }
tower-http = { version = "0.6", features = ["fs", "cors", "set-header"] }
tokio = { workspace = true, features = ["full", "process"] }
futures-util = "0.3"
ruvector-mincut = { workspace = true }
# Serialization
serde = { workspace = true }
serde_json.workspace = true
# Logging
tracing.workspace = true
tracing-subscriber = { workspace = true }
# Time
chrono = { version = "0.4", features = ["serde"] }
# CLI
clap = { workspace = true }
# Multi-BSSID WiFi scanning pipeline (ADR-022 Phase 3)
wifi-densepose-wifiscan = { version = "0.3.0", path = "../wifi-densepose-wifiscan" }
# Signal processing with RuvSense pose tracker (accuracy sprint).
# default-features = false drops the optional ndarray-linalg/BLAS chain so that
# `--no-default-features` at the workspace root can produce a Windows-friendly
# build without vcpkg/openblas (issue #366, #415).
wifi-densepose-signal = { version = "0.3.0", path = "../wifi-densepose-signal", default-features = false }
# Hardware crate — SyncPacket decoder for ADR-110 §A0.12 mesh-aligned timestamps.
wifi-densepose-hardware = { version = "0.3.0", path = "../wifi-densepose-hardware" }
# midstream — real-time introspection / low-latency tap (ADR-099 D1).
# Two crates only, on purpose: scheduler / neural-solver / strange-loop are
# explicitly out of scope of ADR-099 (D5).
midstreamer-temporal-compare = "0.2" # DTW / LCS / Edit-Distance pattern matching
midstreamer-attractor = "0.2" # Lyapunov + regime classification
# ADR-102: Edge Module Registry — fetch the canonical Cognitum cog catalog
# at `https://storage.googleapis.com/cognitum-apps/app-registry.json`,
# cache with TTL, surface via /api/v1/edge/registry. ureq is the smallest
# blocking HTTP client we can use without dragging a tokio HTTP stack in;
# rustls is enabled implicitly via the `tls` default feature.
ureq = { version = "2", default-features = false, features = ["tls", "json"] }
sha2 = "0.10"
thiserror = "1"
# ADR-115 §3.8 — MQTT publisher (HA-DISCO).
# Gated behind the `mqtt` feature so the default binary stays small for users
# who don't need Home Assistant integration. `rumqttc` is the chosen Rust MQTT
# client (ADR-115 §10 references). `rustls` is preferred over openssl on
# Windows to keep parity with the rest of the workspace (`ureq` above also
# uses rustls).
rumqttc = { version = "0.24", default-features = false, features = ["use-rustls"], optional = true }
[features]
default = []
# Enables the ADR-115 §2 MQTT auto-discovery publisher. Without this feature
# all `--mqtt-*` CLI flags still parse (cli.rs declares them unconditionally),
# but enabling `--mqtt` at runtime logs a `WARN` and the publisher is a no-op.
mqtt = ["dep:rumqttc"]
# ADR-115 §3.11 — Matter Bridge (HA-FABRIC). Same gating principle: flags
# parse unconditionally; the bridge is a no-op without this feature.
# matter-rs is added in P7; intentionally absent in P1 to keep the dep
# surface small until the SDK choice is validated.
matter = []
[dev-dependencies]
tempfile = "3.10"
# `tower::ServiceExt::oneshot` for in-process Router tests (bearer_auth).
tower = { workspace = true }