14  Cardio API Reference

Complete reference for scicloj.ripple.cardio — the public API for cardiovascular signal processing (ECG and PPG).

Setup

Shared parameters for the examples below.

(def sampling-rate 256.0)
(def duration 10.0)
(def expected-samples (long (* sampling-rate duration)))

Synthetic Signal Generation

synthetic-ecg

[opts]

Generate a synthetic ECG signal using the ECGSYN dynamical model.

The model uses three coupled ODEs on a limit cycle to produce realistic PQRST morphology with beat-to-beat variability (McSharry et al. 2003). Output is normalized to [-0.4, 1.2] mV.

Args:

  • opts: map with optional keys:
    • :sampling-rate (default 256.0 Hz)
    • :duration (default 10.0 seconds)
    • :heart-rate (default 60.0 BPM)
    • :heart-rate-std (default 1.0 BPM — HR variability std)
    • :lfhf-ratio (default 0.5 — LF/HF power ratio)
    • :noise (default 0.0 — Gaussian noise amplitude; 0 for clean)
    • :seed (default nil — RNG seed for reproducibility)

Returns: double-array of ECG samples

(def ecg (cardio/synthetic-ecg {:sampling-rate sampling-rate
                                :duration duration
                                :heart-rate 72.0
                                :seed 42}))
(count ecg)
2560

synthetic-ppg

[opts]

Generate a synthetic PPG signal with systolic peaks and dicrotic notches.

Uses Gaussian-stamped waveform components with physiologically realistic RR interval variability from the same HRV model as synthetic-ecg.

Args:

  • opts: map with optional keys:
    • :sampling-rate (default 256.0 Hz)
    • :duration (default 10.0 seconds)
    • :heart-rate (default 75.0 BPM)
    • :heart-rate-std (default 1.0 BPM — HR variability std)
    • :noise (default 0.0 — Gaussian noise amplitude; 0 for clean)
    • :seed (default nil — RNG seed for reproducibility)

Returns: double-array of PPG samples

(def ppg (cardio/synthetic-ppg {:sampling-rate sampling-rate
                                :duration duration
                                :heart-rate 72.0
                                :seed 42}))
(count ppg)
2560

Butterworth Filters

butterworth-highpass

[signal sampling-rate opts]

Apply a Butterworth high-pass filter.

Args:

  • signal: double-array of samples
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys:
    • :cutoff (default 0.5 Hz)
    • :order (default 5)

Returns: filtered double-array

(def ecg-hp (cardio/butterworth-highpass ecg sampling-rate {:cutoff 0.5 :order 5}))
(count ecg-hp)
2560

butterworth-bandpass

[signal sampling-rate opts]

Apply a Butterworth band-pass filter.

Args:

  • signal: double-array of samples
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys:
    • :low-cutoff (default 0.5 Hz)
    • :high-cutoff (default 8.0 Hz)
    • :order (default 3)

Returns: filtered double-array

(def ecg-bp (cardio/butterworth-bandpass ecg sampling-rate
              {:low-cutoff 0.5 :high-cutoff 40.0 :order 3}))
(count ecg-bp)
2560

butterworth-bandstop

[signal sampling-rate opts]

Apply a Butterworth band-stop (notch) filter.

Useful for removing powerline interference at 50 or 60 Hz.

Args:

  • signal: double-array of samples
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys:
    • :low-cutoff (default 49.0 Hz)
    • :high-cutoff (default 51.0 Hz)
    • :order (default 5)

Returns: filtered double-array

(def ecg-bs (cardio/butterworth-bandstop ecg sampling-rate
              {:low-cutoff 49.0 :high-cutoff 51.0 :order 5}))
(count ecg-bs)
2560

ECG Processing

ecg-clean

[signal sampling-rate opts]

Clean an ECG signal by removing noise and drift.

Methods:

  • :neurokit (default) — 0.5 Hz highpass + powerline notch filter
  • :hamilton — 8-16 Hz bandpass, isolates QRS complex

Args:

  • signal: double-array of raw ECG samples
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys:
    • :method (default :neurokit)
    • :powerline (default 50 Hz)

Returns: cleaned double-array

(def ecg-cleaned (cardio/ecg-clean ecg sampling-rate {:method :neurokit}))
(count ecg-cleaned)
2560

ecg-findpeaks-neurokit

[signal sampling-rate opts]

Detect R-peaks using NeuroKit2’s gradient-based method.

Args:

  • signal: double-array of cleaned ECG signal
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys (see impl for full list)

Returns: int-array of R-peak sample indices

(def r-peaks-nk (cardio/ecg-findpeaks-neurokit ecg-cleaned sampling-rate {}))

At 72 BPM over 10 s we expect roughly 12 R-peaks.

(<= 8 (count r-peaks-nk) 16)
true

ecg-findpeaks-hamilton

[signal sampling-rate opts]

Detect R-peaks using Hamilton (2002) adaptive threshold method.

Args:

  • signal: double-array of cleaned ECG signal
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys (see impl for full list)

Returns: int-array of R-peak sample indices

(def r-peaks-ham (cardio/ecg-findpeaks-hamilton ecg-cleaned sampling-rate {}))
(<= 8 (count r-peaks-ham) 16)
true

ecg-findpeaks

[signal sampling-rate opts]

Detect R-peaks in a cleaned ECG signal.

Args:

  • signal: double-array of cleaned ECG signal
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys:
    • :method (default :neurokit) — :neurokit or :hamilton
    • additional method-specific options are passed through

Returns: int-array of R-peak sample indices

The default method is :neurokit, so this should match ecg-findpeaks-neurokit.

(def r-peaks (cardio/ecg-findpeaks ecg-cleaned sampling-rate {:method :neurokit}))
(java.util.Arrays/equals ^ints r-peaks-nk ^ints r-peaks)
true

PPG Processing

ppg-clean

[signal sampling-rate opts]

Clean a PPG signal by bandpass filtering.

Methods:

  • :elgendi (default) — 0.5-8 Hz Butterworth bandpass (order 3)

Args:

  • signal: double-array of raw PPG samples
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys:
    • :method (default :elgendi)

Returns: cleaned double-array

(def ppg-cleaned (cardio/ppg-clean ppg sampling-rate {:method :elgendi}))
(count ppg-cleaned)
2560

ppg-findpeaks-elgendi

[signal sampling-rate opts]

Detect systolic peaks using Elgendi et al. (2013) method.

Args:

  • signal: double-array of cleaned PPG signal
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys (see impl for full list)

Returns: int-array of systolic peak sample indices

(def ppg-peaks-elg (cardio/ppg-findpeaks-elgendi ppg-cleaned sampling-rate {}))
(<= 8 (count ppg-peaks-elg) 16)
true

ppg-findpeaks

[signal sampling-rate opts]

Detect systolic peaks in a cleaned PPG signal.

Args:

  • signal: double-array of cleaned PPG signal
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys:
    • :method (default :elgendi)
    • additional method-specific options are passed through

Returns: int-array of systolic peak sample indices

(def ppg-peaks (cardio/ppg-findpeaks ppg-cleaned sampling-rate {:method :elgendi}))
(java.util.Arrays/equals ^ints ppg-peaks-elg ^ints ppg-peaks)
true

Peak Correction and Analysis

fix-peaks

[peak-indices sampling-rate opts]

Correct peak detection artifacts.

Removes extra beats (interval < interval-min) and interpolates missed beats (interval > interval-max-factor x median).

Args:

  • peak-indices: int-array or seq of peak sample indices (sorted)
  • sampling-rate: sampling rate in Hz
  • opts: map with optional keys:
    • :interval-min (default 0.3 seconds)
    • :interval-max-factor (default 1.5)

Returns: int-array of corrected peak sample indices

(def r-peaks-fixed (cardio/fix-peaks r-peaks sampling-rate
                     {:interval-min 0.3 :interval-max-factor 1.5}))

Fixed peaks are sorted and have a reasonable count.

(let [sorted? (every? true?
                (map <= (butlast (seq r-peaks-fixed)) (rest (seq r-peaks-fixed))))]
  (and sorted? (<= 8 (count r-peaks-fixed) 16)))
true

peaks->rr-intervals

[peak-indices sampling-rate]

Convert peak indices to RR intervals in milliseconds.

Args:

  • peak-indices: int-array or seq of peak sample indices (sorted)
  • sampling-rate: sampling rate in Hz

Returns: double-array of RR intervals in ms

(def rr (cardio/peaks->rr-intervals r-peaks-fixed sampling-rate))

All RR intervals are positive and in a physiological range (300–1500 ms).

(and (> (dfn/reduce-min rr) 300.0)
     (< (dfn/reduce-max rr) 1500.0))
true

rr->heart-rate

[rr-intervals-ms]

Convert RR intervals (ms) to instantaneous heart rate (BPM).

Args:

  • rr-intervals-ms: double-array of RR intervals in milliseconds

Returns: double-array of heart rate values in BPM

(def hr (cardio/rr->heart-rate rr))

Mean heart rate should be close to the 72 BPM target.

(let [mean-hr (/ (dfn/sum hr) (count hr))]
  (< (Math/abs (- mean-hr 72.0)) 10.0))
true

hrv-time

[rr-intervals-ms]

Compute time-domain HRV indices from RR intervals.

Args:

  • rr-intervals-ms: double-array of RR intervals in milliseconds

Returns: map of HRV indices including :mean-nn, :sdnn, :rmssd, :sdsd, :pnn50, :pnn20, :cvnn, :cvsd, :median-nn, :mad-nn, :min-nn, :max-nn, :mean-hr, :sd-hr, :min-hr, :max-hr

(def hrv (cardio/hrv-time rr))

HRV result contains all expected keys.

(every? #(contains? hrv %)
  [:mean-nn :sdnn :rmssd :sdsd :pnn50 :pnn20 :cvnn :cvsd
   :median-nn :mad-nn :min-nn :max-nn :mean-hr :sd-hr :min-hr :max-hr])
true
source: notebooks/ripple_book/cardio_api_reference.clj