Skip to content

feat(cq): per-channel pulse-validity criterion (beat coverage >75%) - #127

Draft
boringethan wants to merge 7 commits into
feature/124-pulse-waveform-analysisfrom
feature/126-cq-pulse-validity
Draft

feat(cq): per-channel pulse-validity criterion (beat coverage >75%)#127
boringethan wants to merge 7 commits into
feature/124-pulse-waveform-analysisfrom
feature/126-cq-pulse-validity

Conversation

@boringethan

Copy link
Copy Markdown
Contributor

Refs #126.

Adds a per-camera pulse-validity criterion to ContactQualityWorkflow: a channel that passes the existing signal-level checks is marked no_pulse unless a valid cardiac pulse train covers >75% of the CQ scan.

What's in it

  • PulseWaveformAnalyzer.beat_coverage(total_duration_s, *, min_coverage=0.75) -> PulseCoverage — additive; reuses the Pulse-waveform analysis pipeline extension (BFI cardiac pulse: ensemble template, envelope, morphology) #124 band-limit + autocorrelation + foot segmentation, skips per-beat morphology QC (that QC is right for the live view but wrongly rejects beats on noisier channels for a presence gate). Noise rejection is delegated to the autocorrelation periodicity gate.
  • CQ sink buffers per-(side,cam) bfi_live and runs one analyzer per channel at result(). Coverage denominator = global light-frame span (so a channel that drops out mid-scan scores low, not spuriously high).
  • CamCQResult gains pulse_valid / pulse_coverage / pulse_hr_bpm / pulse_periodicity; reason gains no_pulse. Precedence: no_signal → ambient_light → poor_contact → no_pulse → ok.
  • check(evaluate_pulse=False, pulse_min_coverage=0.75)default off = fully backward-compatible (existing 1 s quick-check + all prior tests unchanged).

Criterion

pulse_valid = coverage > min_coverage AND periodicity >= 0.45

Validation

Bench capture scan_owC27EHALL (16 ch, ~85 bpm), vendored as tests/data/cq_pulse_reference_bfi.csv:

  • all 16/16 known-good channels pass at 0.75 (coverage 0.90–0.94, periodicity 0.80–0.88)
  • flat/noise references fail (periodicity 0.10/0.17)

Tests: 55 pass in the touched suite (pulse-coverage + live-pulse regression + CQ); 251 pass / 29 skipped in the pipeline suite. No hardware required.

Base

Targets feature/124 because it reuses the #124 PulseWaveformAnalyzer. Retarget to next if #124 merges first.

Scope / follow-up

SDK only. Bloodflow-app wiring (config keys, longer CQ capture, surface no_pulse in the CQ modal) is a separate follow-up ticket/PR.

Design: docs/superpowers/specs/2026-07-04-cq-pulse-validity-design.md

🤖 Generated with Claude Code

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.

1 participant