Previously only WiFi-CSI produced breathing/HR estimates. With the
HLK-LD2402 radar wired up we can compute a second, physically
independent breathing estimate from chest-induced cm flicker in the
distance time-series — a useful cross-check that catches the case
when one modality is blind (e.g. WiFi-CSI when nodes are offline,
or mmWave when nothing's in the radar's field of view).
mmwave.rs:
- Plumb a per-reading VitalSignDetector tuned for the module's 6 Hz
Normal-Mode cadence (Nyquist 3 Hz comfortably covers the 0.1-0.5
Hz breathing band).
- Distance (cm) feeds the detector as the "amplitude" channel;
phase is empty so heartbeat falls back to amplitude residual.
- Gate `current_vitals()` on data freshness so a disconnected radar
doesn't return stale cached BPMs.
main.rs:
- New GET /api/v1/mmwave/vitals returning the same shape as
/api/v1/vital-signs plus buffer status for UI warm-up feedback.
ui/raw.html:
- Each vital pill now shows both 📶 (WiFi-CSI) and 📡 (mmWave)
values side-by-side, separated by `|`. mmWave HR is labelled
"n/a" — cm precision at 6 Hz puts heartbeat below the noise
floor. Buffer fill (e.g. "120/180") shown while detector is
warming up so the operator knows BPM is on the way.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The breathing/HR pills carried raw BPM with no context. An operator
glancing at "94 BPM" can't tell if that's normal or tachycardia
without external reference.
Add inline "норма 12–20" / "норма 60–100" hints (dimmed so they
don't compete with the live value), and tint the number amber when
it falls outside the adult-at-rest range. Tooltip carries the
medical terminology (bradypnea/tachypnea, bradycardia/tachycardia).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
ADR-021 already publishes `vital_signs` inside SensingUpdate but the
raw calibration console had no readout — the operator had to curl
/api/v1/vital-signs to see breathing/HR. Add two pills (🫁 + 💓)
next to the mmWave one and update them on every WS tick.
Confidence < 20 % dims the pill so noise-floor estimates don't read
as real values. Missing/zero rates fall back to "— BPM".
Mirrored ui/raw.html → static/raw.html so both deployment paths
serve the same console.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a hidden-by-default 📡 mmWave pill next to the global badge + CV
stat. Polls /api/v1/mmwave/latest at 5 Hz (~200 ms) — well above the
HLK-LD2402's 6 Hz native cadence so no information is lost. Pill shows:
📡 mmWave 152 cm · 60 ms
Distance + age (ms since last reading). Fades to 50% opacity when age
>1.5 s, hides entirely when the server reports `available: false`
(port absent or stale >2 s).
Synced both copies — ui/raw.html (deploy mirror) + static/raw.html
(canonical source referenced by ADR-104 / ADR-107).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previously raw.html lived only at v2/crates/wifi-densepose-sensing-server/static/raw.html.
When the server is started with --ui-path /Users/arsen/Desktop/RuView/ui
(the SPA path) the calibration console returns 404 on /ui/raw.html.
Copy the file into ui/ alongside index.html so a single --ui-path
covers both the SPA and the engineer-facing raw view. The static/
copy in the crate stays as the canonical source (referenced by ADRs
104/107); ui/raw.html is a deploy mirror.
Live at http://localhost:8080/ui/raw.html.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>