Skip to content

fix(vital_signs): use circular variance for wrapped phase values#605

Closed
yahyasaqban-lab wants to merge 1 commit into
ruvnet:mainfrom
yahyasaqban-lab:main
Closed

fix(vital_signs): use circular variance for wrapped phase values#605
yahyasaqban-lab wants to merge 1 commit into
ruvnet:mainfrom
yahyasaqban-lab:main

Conversation

@yahyasaqban-lab
Copy link
Copy Markdown

Summary

Phase values from atan2(i\_val, q\_val) are wrapped to (-\pi, \pi]). Linear variance treats values near (+\pi) and (-\pi) as (\sim 2\pi) apart when they are physically (\sim 0.003) rad apart, producing variance of (\sim \pi^2) instead of (\sim 10^{-6}).

Replace with circular variance (1 - R̄, where R̄ is the mean resultant length), which correctly handles wrapped circular data.

Repro

import math
phases = [math.pi - 0.001, -math.pi + 0.001]
# Linear: ~9.87  (broken)
m = sum(phases) / 2
print('linear:  ', sum((p - m) ** 2 for p in phases) / 2)
# Circular: ~5e-7  (correct)
s = sum(math.sin(p) for p in phases)
c = sum(math.cos(p) for p in phases)
n = len(phases)
print('circular:', 1 - (s*s + c*c)**0.5 / n)

Impact

Fixes the (\pm 15) bpm jitter in heart-rate detection and jumpy vitals reported in issues #485 and #519.

Fixes #593

Phase values from atan2(i_val, q_val) are wrapped to (-pi, pi]. Linear
variance treats values near +pi and -pi as ~2pi apart when they are
physically ~0.003 rad apart, producing variance of ~pi^2 instead of ~1e-6.

Replace with circular variance (1 - R, mean resultant length) which
correctly handles wrapped circular data. This fixes the +/-15 bpm jitter
in heart-rate detection and the jumpy vitals reported in issues ruvnet#485, ruvnet#519.

Fixes ruvnet#593
@ruvnet
Copy link
Copy Markdown
Owner

ruvnet commented May 17, 2026

Closing as a duplicate of #595, which was filed by the original reporter (@akhilesharora) of #593 on 2026-05-16 — three minutes after the bug report. #595 ships the same circular-variance formula plus 4 dedicated tests (including a regression test that explicitly asserts the old linear formula's pi² output on the wraparound input, so any future regression will fail the suite). #595 was merged in 60d1f… just now.

Thanks for the contribution. For next time, please check existing open PRs before opening a fix and open feature branches from your fork rather than main — that keeps your fork's main pristine for upstream sync.

@ruvnet ruvnet closed this May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants