10 KiB
ESPectre Gap Analysis (full Pace Part-2 vs. RuView as of 2026-05-17)
Companion to espectre-techniques.md. That
doc is the technique catalogue; this one is the what's still
missing breakdown, structured exactly along the sections of Pace's
How I Turned My Wi-Fi Into a Motion Sensor — Part 2.
Problem #1: NBVI subcarrier selection
| Pace step | Status in RuView |
|---|---|
Formula α·σ/μ² + (1-α)·σ/μ, α = 0.5 |
✅ ADR-102 |
| Step 1: quiet-window finder | ✅ ADR-102 v2 |
| Step 2: 25 %-percentile dead-zone gate | ✅ ADR-102 |
| Step 3: rank + validate | ✅ ADR-104 D4 (commit 6212b17e) — K ∈ {6,8,10,12,16,20} sweep, smallest-FP wins, ties by smallest total-NBVI |
| Step 4: pick top-K (K=12) | ✅ ADR-102 |
| Amplitude only (no phase) | ✅ same |
All four NBVI steps shipped. If a noisy neighbour energy-overlaps the top-K, the validator counts FPs over the quiet window and a tighter (or different) K wins.
Problem #2: Gain Lock (AGC + FFT)
✅ All done — ADR-100. Median over 300 packets, MIN_SAFE_AGC=30
skip-on-strong-signal safety, ESP32-S3/C3/C6 platform guards.
Problem #3: Universal threshold via baseline-variance normalization
✅ Done — ADR-103 D3. Pace's scale = 0.25 / baseline_variance
implemented as norm_cv = cv / baseline_cv with universal gates
3× (moving) / 6× (active). Falls back to absolute gates when no
calibration loaded.
Two-phase boot calibration (~10 s total)
Pace runs both phases as a single atomic boot sequence on the device:
PHASE 1 (3 s) collect AGC/FFT → median → lock
PHASE 2 (7 s) rank subcarriers with gain locked → save top-K to NVS
| Phase | Status in RuView |
|---|---|
| Phase 1 in FW | ✅ ADR-100 (csi_collector.c::rv_gain_lock_process) |
| Phase 2 in FW after Phase 1 | ⏳ NBVI intentionally in server as rolling refresh (adapts to slow channel drift). Not planned in FW. |
| NVS save of gain-lock | ✅ ADR-108 (commit 3779bb76) — csi_cfg/gl_agc + gl_fft |
| NVS save of NBVI selection | ⏳ NBVI lives server-side, doesn't apply |
After ADR-108 the FW boots → CSI ready in ~0.5 s (NVS restore) instead of ~10 s (full 300-packet calibration). Adapting to room changes without recalibration is now a "clear NVS keys" operation — open item ADR-108 #1 will surface that as a REST endpoint.
Persisted calibration (NVS on the sensor)
Pace stores everything the algorithm needs in NVS on first boot, so post-reboot the sensor is back in detect mode in well under a second:
- AGC lock value
- FFT lock value
- Selected subcarrier indices
- Baseline variance
- User-tuned threshold
| Item | Status in RuView |
|---|---|
| WiFi creds + collector IP in NVS | ✅ csi_cfg namespace |
| Gain lock NVS persistence | ✅ ADR-108 (csi_cfg/gl_agc + gl_fft) |
| NBVI selection NVS persistence | ⏳ server-side rolling, intentional |
| Baseline NVS persistence | ✅ on host disk via ADR-103 (data/baseline.json); not on sensor — server is required |
| Threshold NVS persistence | ✅ derives from baseline_cv loaded by ADR-103 |
If we ever ship to operators who don't run the Rust server (pure FW
- HA), the server-side bits (NBVI / baseline / threshold) would have to migrate to the sensor's NVS. Not on the current roadmap.
The Game (Web Serial calibration UI)
❌ Not done. Pace ships a browser-based reaction game at
espectre.dev/game that talks to the ESP32 directly over Web Serial
API (USB-CDC). The game shows a live motion bar, lets the user tune
threshold while playing, and persists the chosen threshold to NVS.
Our closest analogue is the read-only raw.html calibration console
(per-node amplitude bars + RSSI traces + classification badges)
served by sensing-server on /static/raw.html. No interactive
threshold tuning; no Web Serial path; no game.
Testing
| Pace ships | RuView has |
|---|---|
| 500+ unit tests | small smoke tests in some crates |
| 90 % code coverage | not tracked |
| Fixed 2 000-packet reference capture (1 000 idle + 1 000 motion) | none — we test live on the operator's deployment |
| PlatformIO + pytest + ESPHome + Codecov on every push | partial — Rust cargo test only; 2 parser regression tests added by parallel agent (csi.rs:751) |
This is the largest reliability gap. A 2 000-packet replay against the classifier would protect against silent regressions when we re-tune thresholds or refactor NBVI.
Native Home Assistant integration via ESPHome
❌ Not done. Pace's sensor shows up in HA the moment it's
flashed — binary_sensor.motion_<room> entity with attributes.
ESPHome handles MQTT / native API / device discovery automatically.
RuView publishes via WebSocket and REST only; would need either an ESPHome component, an MQTT bridge, or a custom HA integration.
Hardware support
- Pace supports ESP32-S3, ESP32-C3, ESP32-C5, ESP32-C6. Gain-lock is guarded on these targets only; ESP32 + ESP32-S2 fall back to no gain lock.
- RuView gain-lock code has the same
#ifguard so the same hardware list works — but we only have hands-on test data for ESP32-S3.
What Pace announces for Part 3 (not yet shipped, not yet on our
radar either)
- Gesture recognition
- Fall detection
- Person vs. pet classification
Priority for RuView — current state
✅ Done in this session
| Item | Where |
|---|---|
| NVS persistence of gain-lock | ADR-108 (3779bb76) |
| FP-rate validation of NBVI (Step 3) | ADR-104 D4 (6212b17e) |
POST /api/v1/baseline/calibrate + UI button |
ADR-107 (0f373467, 45c1464c) |
| Auto-recalibrate on long-quiet periods | ADR-107 (0f373467) |
| Per-subcarrier baseline comparison | ADR-104 (6212b17e) |
| Full complex CSI in WS (amp+phase+meta) | ADR-106 (4daa2c9b) |
| Sensor µs timestamp from FW | ADR-106 (b787f40a) |
| Managed-ping CSI keepalive (no ручной ping) | ADR-106 (8489efe9) |
| No synthetic data in production runtime | ADR-105 (9aa027e9, 30244d27) |
| OTA flash via WiFi (8032 port) | ota-pipeline.md (274984d3) |
⏳ Still open / deferred, by impact
Updated 2026-05-17 — Most of the original "still open" items shipped during this session. The list below is now only items that are out of session scope (HA / ESPHome / Web Serial / channel hopping per operator constraints), or items that need operator action (camera-side training capture).
| # | Item | Net benefit | Estimate | Status |
|---|---|---|---|---|
| 1 | HA via MQTT | sensor as HA entity, ecosystem reach | 1 day | Deferred (operator said: no new integrations) |
| 2 | regression protection over the classifier + NBVI | ✓ Done — ADR-114 (96225e27); F1 = 1.000 on 1000 idle + 1000 motion fixtures |
||
| 3 | raw.html |
operator sees off-axis drift channel firing in real time | ✓ Done — ADR-104 (eec3ca6c) drift sparkline + ADR-107 D6 progress bar (432753e1) |
|
| 4 | POST /ota/recalibrate (clear NVS gain-lock) |
reset gain-lock without USB after AP swap or relocation | ✓ Done — ADR-109 (f92807cd) |
|
| 5 | auto-invalidate stale gain-lock on AP change | ✓ Done — folded into ADR-109 (gl_ap_mac key, same commit) |
||
| 6 | MultistaticFuser |
physically real spatial map | ✓ Done — ADR-112 (c8ac60f6); 320/400 cells non-zero on two live sensors |
|
| 7 | flag for re-calibration when channel slowly drifts | ✓ Done — ADR-104 staleness watch (eec3ca6c) — warns when baseline > 14400 s AND drift > 0.15 for ≥3 ticks |
||
| 8 | sub-mm chest-wall motion detection for vitals | ✓ Done — ADR-104 phase channel (47dafab4); requires empty-room re-record to activate (per_subcarrier_phase_mean not in current baseline.json v1 schema) |
||
| 9 | Tailscale-target in NVS | sensor stream keeps working when Mac roams networks | 30 min provision + reflash | Deferred (Mac stable on TP-Link, low ROI). Alternative shipped: ADR-115 /ota/set-target lets operator repoint via REST without USB/Tailscale. |
| 10 | ESPHome native component (instead of MQTT bridge) | tighter HA integration than #1 | 2-3 days | Deferred (operator said: no new integrations) |
| 11 | Web Serial calibration game | playful threshold tuning | 1 day | Deferred (operator said: no new integrations) |
| 12 | Boot-time NBVI freeze in FW | trade-off vs adaptive: don't adopt unless FP issues in real homes | 2 h | Deferred (server-side rolling NBVI working; no observed FP problem) |
| 13 | Per-channel NVS cache for gain-lock | only needed if channel hopping (ADR-029) re-activated | 1 h | Deferred (channel hopping not active) |
| 14 | DensePose model train + load | unlock pose estimation | 1-3 days | Mostly done — model loader shipped in ADR-116 (7cdd8f69) with ruv/ruview/wiflow-v1. Output requires per-deployment fine-tune (camera-supervised capture) — operator-side work, scoped as Pack B / Pack E. |
| 15 | /ota/set-target REST (new this session) |
repoint CSI aggregator without USB after Mac-IP / router change | — | ✓ Done — ADR-115 (7d3e0c2d) |
| 16 | Process-hygiene + audit follow-ups (new this session) | UDP loopback filter, ping pre-reap, / redirect, wiflow zero-pad, lock-clone optim, sensing-tab container, test-isolation guard, ADR/CHECKLIST consistency |
— | ✓ Done — ADR-117 (this PR) |
References
espectre-techniques.md— technique catalogueota-pipeline.md— WiFi-OTA recipe (port 8032)- ADR-100 — gain lock
- ADR-101 — classifier
- ADR-102 — NBVI
- ADR-103 — baseline persistence
- ADR-104 — per-sub drift + NBVI FP-validation
- ADR-105 — no synthetic data
- ADR-106 — full complex CSI + keepalive
- ADR-107 — REST + auto-recalibrate
- ADR-108 — FW NVS persist gain-lock
- Pace, How I Turned My Wi-Fi Into a Motion Sensor — Part 2, Dec 2025
francescopace/espectreon GitHub (GPLv3)