Skip to content
cccskills
FreePublished on GitHub

radar-vital-signs

End-to-end pipeline for extracting heart rate (HR) and breathing rate (BR) from raw short-range radar I/Q captures — both continuous-wave (CW, 24 GHz clinical boards) and FMCW mmWave (60/77 GHz, TI IWR/AWR). Use when Claude needs to parse interleaved I/Q binary, do a Range FFT on FMCW chirps, remove static clutter, pick a subject range bin, extract phase with unwrapping, design HR/BR bandpass filters, pick a peak frequency via PSD, reject the HR second harmonic that often dominates the fundamental, or handle respiration-harmonic leakage on slow breathers. Not for pulse/UWB range gating, MIMO beamforming, Doppler-only gesture radar, arrhythmia detection, or multi-subject source separation.

The English summary is being prepared. Showing the original description.

Run score Not tested

See how to install

Files included (6)

  • SKILL.md6.2 KB
  • references/band-rationale.md2.6 KB
  • references/debugging.md2.8 KB
  • references/harmonic-pitfalls.md3.9 KB
  • references/iq-formats.md1.9 KB
  • references/range-bin.md2.5 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Radar Vital-Sign Extraction

End-to-end pipeline: raw radar I/Q → cleaned phase signal → HR and BR in bpm.

Full pipeline (every step, in order)

  1. Parse binary I/Q into a complex 1-D array (CW) or 2-D range matrix (FMCW). Use the JSON/YAML sidecar to determine format — never assume. See references/iq-formats.md.

  2. (FMCW only) Range FFT across fast-time samples of each chirp → range matrix R[n_chirp, n_range_bin]. CW skips this step.

  3. Remove static clutter. Subtract the temporal mean:

    • CW: iq -= iq.mean()
    • FMCW: R -= R.mean(axis=0, keepdims=True)
  4. (FMCW only) Pick the subject range bin within a physical prior window (e.g., 0.3–1.5 m for a seated subject). See references/range-bin.md.

  5. Extract phase with unwrap:

    phase = np.unwrap(np.angle(iq_or_bin))
    phase -= phase.mean()
    
  6. Decimate to ~50 Hz if fs >= 500 Hz (sub-Hz filtering at kHz is numerically unstable):

    from scipy.signal import decimate
    phase_ds = decimate(phase, q=int(fs/50), ftype='iir', zero_phase=True)
    fs_new = fs / int(fs/50)
    
  7. Two separate bandpasses — BR and HR:

    b_br, a_br = butter(4, [0.08, 0.5], btype='band', fs=fs_new)
    b_hr, a_hr = butter(4, [0.7, 3.0],  btype='band', fs=fs_new)
    br_sig = filtfilt(b_br, a_br, phase_ds)
    hr_sig = filtfilt(b_hr, a_hr, phase_ds)
    
  8. Peak frequency via zero-padded Welch PSD (each band):

    nperseg = min(len(x), int(fs_new * 25))
    f, p = welch(x, fs=fs_new, nperseg=nperseg, noverlap=nperseg//2,
                 nfft=8*nperseg, detrend='constant')
    mask = (f >= lo) & (f <= hi)
    peak_hz = f[mask][np.argmax(p[mask])]
    
  9. HR harmonic rejection — always run:

    f_sub = f_peak_hr / 2.0
    if 0.7 <= f_sub <= 3.0:
        p_sub = np.interp(f_sub, f, p)
        p_top = np.interp(f_peak_hr, f, p)
        if p_sub > 0.5 * p_top:
            f_peak_hr = f_sub   # peak was the 2nd harmonic
    hr_bpm = f_peak_hr * 60
    

    See references/harmonic-pitfalls.md for why this matters and mitigations for slow-breather respiration harmonics leaking into the HR band.

  10. Cross-check with autocorrelation (optional but recommended):

    ac = np.correlate(x - x.mean(), x - x.mean(), mode='full')
    ac = ac[len(ac)//2:] / ac[len(ac)//2]
    lag = int(fs_new/f_hi) + np.argmax(ac[int(fs_new/f_hi):int(fs_new/f_lo)])
    bpm_ac = 60 * fs_new / lag
    

    If abs(bpm_ac - bpm_psd) > 5, flag as low confidence.

Critical rules

rulewhy
Use phase, not magnitude1 mm motion at 24 GHz ≈ 1 rad; magnitude costs ~40 dB of SNR. np.abs(iq) is almost always wrong for mm-scale motion
Clutter removal before np.angleDC offset anchors phase off zero, eats the ±π unwrap budget
Decimate before sub-Hz bandpassSciPy biquad silently NaNs at very-low normalized cutoffs
Two separate BR / HR bandpassesHR is 10×–100× smaller than BR; single wide filter can't separate them
Zero-pad Welch PSD (nfft=8*nperseg)Raw bin spacing fs/nperseg is often coarser than tolerance
Always run HR sub-harmonic check2nd harmonic of cardiac pulse frequently dominates fundamental
Never argmax(magnitude) across all range bins (FMCW)DC bin and static reflectors dominate; restrict to subject-range window

Band edges: use these, not textbook 0.1–0.5 / 0.8–2.5

bandusetextbookwhy
BR lower0.08 Hz (4.8 bpm)0.1 Hz (6 bpm)slow breathers (supine, meditation, sleep) routinely below 6 bpm
HR lower0.7 Hz (42 bpm)0.8 Hz (48 bpm)bradycardia (athletes, post-tilt-down, β-blockers) below 48 bpm
HR upper3.0 Hz (180 bpm)2.5 Hz (150 bpm)post-exercise and children exceed 150 bpm

Widen only with specific justification. See references/band-rationale.md.

Decision rules

IfThen
f_peak/2 in HR band and p_sub > 0.5 × p_topPick sub-harmonic (fundamental)
PSD and autocorrelation disagree > 5 bpmFlag low confidence; don't commit to one value
BR estimate < 10 bpm (slow breather)Expect HR-band contamination — notch 2·BR, 3·BR. See harmonic-pitfalls.md
HR > 150 bpm (tachycardia)Widen HR band upper to 3.3 Hz, re-estimate
Clip < 15 sPSD resolution > tolerance — prefer autocorrelation, or flag inconclusive

Sanity checks before reporting

  • BR < HR always for a live adult at rest. Violated ⇒ swapped bands.
  • HR × duration_minutes ≈ peak count in find_peaks(bandpassed_hr). Off by 2× ⇒ harmonic error slipped through.
  • Resting adult plausibility: HR 50–90 bpm, BR 10–20 bpm. Way outside ⇒ re-check band edges, decimation, and harmonic rejection.

When things go wrong

If output looks like garbage, walk through references/debugging.md — fast checks that catch most ingestion and SNR bugs.

Not in scope

  • Pulse / UWB range-gated radar (different pipeline entirely).
  • MIMO angle-of-arrival — needs beamforming first.
  • Doppler-only gesture radar — use slow-time FFT, not bin phase.
  • Arrhythmia / irregular rhythms — use R-peak / foot detection + RR-interval analysis, not PSD.
  • Multiple subjects in one signal — run source separation first.
  • Rapidly non-stationary rate (exercise ramp) — use a spectrogram, not single-window PSD.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Perform various data analysis on SEC 13-F and obtain some insights of fund activities such as number of holdings, AUM, and change of holdings between two quarters.

The English summary is being prepared. Showing the original description.

benchflow-ai/skillsbench1.9KUpdated Oct 23, 2026

AC branch pi-model power flow equations (P/Q and |S|) with transformer tap ratio and phase shift, matching `acopf-math-model.md` and MATPOWER branch fields. Use when computing branch flows in either direction, aggregating bus injections for nodal balance, checking MVA (rateA) limits, computing branch loading %, or debugging sign/units issues in AC power flow.

The English summary is being prepared. Showing the original description.

benchflow-ai/skillsbench1.9KUpdated Oct 23, 2026

Redact text from PDF documents for blind review anonymization

The English summary is being prepared. Showing the original description.

benchflow-ai/skillsbench1.9KUpdated Oct 23, 2026

Use when checking simplified ADA-derived plan-view bathroom accessibility constraints such as turning space, door clear width, toilet centerline, grab bars, and lavatory knee/toe clearance.

The English summary is being prepared. Showing the original description.

benchflow-ai/skillsbench1.9KUpdated Oct 23, 2026

Analyze failed GitHub Action jobs for a pull request.

The English summary is being prepared. Showing the original description.

benchflow-ai/skillsbench1.9KUpdated Oct 23, 2026

Use when extracting plan-view architectural geometry from DXF files with semantic CAD layers, especially when outputs must normalize rooms, doors, fixtures, clearances, and grab bars into machine-checkable JSON.

The English summary is being prepared. Showing the original description.

benchflow-ai/skillsbench1.9KUpdated Oct 23, 2026

See all skills by benchflow-ai

Report a problem with this skill