Skip to content

vital_signs.rs: linear stats on wrapped (atan2) phase values #593

@akhilesharora

Description

@akhilesharora

process_frame in vital_signs.rs uses arithmetic mean + arithmetic variance on phase values, but the phases come from q_val.atan2(i_val) (csi.rs:90, main.rs:855) so they're wrapped to (-pi, pi]. When two subcarriers happen to land on opposite sides of the wrap (e.g. 3.14 and -3.14, physically ~0.003 rad apart), the linear formula treats them as ~2*pi apart and you get a variance of ~pi^2 instead of ~1e-6. That value goes straight into the heart-rate FFT buffer.

Repro on a fresh clone:

git clone --depth 1 https://github.com/ruvnet/RuView.git && cd RuView
sed -n '142,148p' v2/crates/wifi-densepose-sensing-server/src/vital_signs.rs
python3 -c "
import math
phases = [math.pi - 0.001, -math.pi + 0.001]
m = sum(phases) / 2
print('linear:  ', sum((p - m) ** 2 for p in phases) / 2)
s = sum(math.sin(p) for p in phases); c = sum(math.cos(p) for p in phases)
print('circular:', 1 - (s * s + c * c) ** 0.5 / 2)
"

You'll see linear: 9.86... vs circular: 5e-07 on the same input.

Probably the cause of the very jumpy vitals reported in #519, and the +/-15 bpm jitter mentioned in #485's description. #485 routes around the buggy detector with an external pipeline but keeps VitalSignDetector as the low-confidence fallback, so the math is still live in that path.

Fix is the standard circular variance (1 - R, mean resultant length). PR coming.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions