1 The Science
NoopApp edited this page 2026-06-10 16:08:18 +01:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

The Science

NOOP computes recovery, strain, HRV, and sleep locally on your device using transparent, published physiological methods. Every metric is an approximation of common exercise-science and cardiology literature — not a reproduction of any proprietary algorithm, and not a medical device or medical advice.

Not affiliated with WHOOP. NOOP is independent interoperability software for your own device and your own data. The methods described here are grounded in peer-reviewed literature and are the same class of tools used in sports-science research labs — they are estimates, not clinical measurements. Do not use them to diagnose, treat, or make health decisions. Consult a qualified professional.


Overview: The analytics pipeline

All mathematics in NOOP is pure, deterministic, and database-free: same inputs always produce the same outputs, which makes every analyzer straightforward to unit-test. The analytics live in the cross-platform StrandAnalytics Swift package, and they run on-device against:

  • Live BLE streams (heart rate, R-R intervals, respiration, gravity/accelerometer) arriving from the strap in real time.
  • Imported history from WHOOP CSV exports and Apple Health exports.
  • Historical offload from the strap's on-device store (~14 days) each time you connect.

The computed metrics are persisted in SQLite under the "<deviceId>-noop" source, but WHOOP's own imports (when you import a CSV) always take precedence — NOOP's approximations win only for data WHOOP doesn't cover.


Heart-Rate Variability (HRV)

RMSSD — the root-mean-square of successive differences

HRV is the beat-to-beat variation in your heart rate, measured as R-R intervals (the time between consecutive heartbeats in milliseconds). NOOP computes RMSSD — the classic Task Force (1996) metric:

RMSSD = sqrt( (1/(N-1)) · Σ(NN[i+1]  NN[i])² )

Where NN is a sequence of normal R-R intervals (in ms), and each term is the squared difference between consecutive intervals.

Why RMSSD? It captures the parasympathetic tone (vagal activity) of your autonomic nervous system — the "rest and digest" branch that keeps you calm and recovered. Higher RMSSD means more variability, which usually correlates with better recovery and lower stress.

Cleaning: removing noise and ectopic beats

Raw R-R data can contain artifacts — missed beats, extra beats (ectopics), electrical noise. NOOP applies a deterministic cleaning pipeline:

  1. Range filter — drop intervals outside the plausible [300, 2000] ms band (~30200 bpm).
  2. Ectopic rejection (Malik et al., 1989) — drop any beat deviating more than 20% from a local median over a 5-beat centered window. This is a simpler substitute for the Kubios algorithm (which isn't available on-device) and catches physiologically impossible jumps.
  3. Sufficiency gate — require at least 20 clean intervals before returning a result; otherwise return nil rather than guessing.

Honest substitution: The reference method used neurokit2's Kubios/LipponenTarvainen artifact classifier, which requires external Python libraries. NOOP substitutes the classical Malik local-median rule — fully deterministic, works on-device, and achieves the same intent: removing impossible beat-to-beat swings before computing HRV.

Other HRV metrics

  • SDNN — the sample standard deviation of NN intervals (Task Force 1996). A broader measure of variability over the whole recording.
  • pNN50 — the percentage of successive differences > 50 ms. A marker of parasympathetic dominance.

Recovery Scoring

The 0100 composite

NOOP's recovery score is an HRV-dominant, baseline-normalized composite that blends several drivers into a single 0100 number. It is explicitly approximate — the key insight is that recovery is personal: your good recovery looks different from someone else's, so comparing you to yourself matters more than comparing to a population average.

Driver Direction Weight
HRV vs your baseline Higher → better recovery 60% (dominant)
Resting HR vs your baseline Lower → better 20%
Respiration rate vs your baseline Lower → better 5%
Sleep performance Better sleep → better recovery 15%

Baseline normalization: z-scores

Each metric is standardized against your personal baseline using a robust z-score:

z = (value  mean) / (1.253 · spread)

The 1.253 factor converts an EWMA mean-absolute-deviation (MAD) into an approximate Gaussian standard deviation (E[|Xμ|] ≈ σ / 1.253). For "lower is better" metrics (resting HR, respiration), the z is inverted so high values still push recovery down.

Sleep performance is centered directly: (sleepPerf 0.85) / 0.12 — anchoring on 85% efficiency as a typical target.

The logistic squash to 0100

The weighted-mean z-score is then squashed to 0100 with a logistic curve:

score = 100 / (1 + exp(k · (z  z0)))
  where k = 1.6, z0 = 0.20

The z = 0 point (your personal baseline, across all metrics) maps to approximately 58% — matching WHOOP's published population-average recovery. This anchoring means: when your HRV, RHR, and respiration are all exactly at your typical baseline, your score is ~58%, not 50%. Z = +2 drives toward 95%; Z = 2 drives toward 5%.

Bands

Band Range
Red (needs recovery) < 34
Yellow (balanced) 3467
Green (ready to push) ≥ 67

Cold-start: when you don't have a baseline yet

If your HRV baseline isn't trustworthy yet (fewer than 4 valid nights), NOOP returns nil rather than a fabricated number. This is intentionally honest — more painful than a guess, but more truthful. Callers can fall back to the population mean (58%), but the screen flags it as provisional.

Once you have 4+ nights, the baseline becomes "provisional" (413 nights), then "trusted" (14+).


Strain Scoring

The 021 cardiovascular load scale

Strain is the cumulative physiological load from exercise during a day. NOOP uses a published, scalable method: the Heart-Rate Reserve (Karvonen) intensity model combined with TRIMP (Training Impulse) accumulation, mapped to a 021 logarithmic scale.

Step 1: Heart-Rate Reserve (HRR)

HRR = HRmax  Resting HR
%HRR = (HR  RHR) / HRR × 100, clamped [0, 100]

This expresses your current heart rate as a fraction of your reserve — what percentage of your maximum capacity you're using right now.

Step 2: TRIMP accumulation

NOOP supports two methods; the default is Edwards (1993):

Edwards 5-zone summation (default)

Each heart-rate sample contributes its zone weight multiplied by duration:

Zone %HRR range Weight
Zone 1 < 50% 1
Zone 2 5060% 2
Zone 3 6070% 3
Zone 4 7080% 4
Zone 5 ≥ 80% 5
TRIMP = Σ(duration_in_zone × weight)

Banister exponential (alternative)

An older but well-cited model: duration × x × 0.64 × e^(b·x) where x = %HRR/100 and b = 1.92 (men) or 1.67 (women).

Step 3: Logarithmic compression to 021

strain = 21 · ln(TRIMP + 1) / ln(D)
  where D = 7201

The denominator D is calibrated so that the Edwards daily ceiling — maximum zone weight (5) sustained for 24 hours = 5 × 1440 = 7200 — maps to exactly 21.0. This makes 21 a true physiological ceiling for the day: you'd need to stay in zone 5 all night to reach it.

HRmax estimation

NOOP estimates your maximum heart rate using two methods:

  • Observed (if you have ≥600 HR samples on record): the 99.5th percentile of your recorded HR, used as the starting point.
  • Tanaka (2001): HRmax = 208 0.7 × age. A gender-independent population estimate that serves as a floor — if Tanaka is higher than your observed max, Tanaka is used (to avoid underestimating if you haven't hit your true max yet).
  • Manual override: you can set your own max HR in Settings.

If you supply no age and have no data, the scorer returns nil.


Sleep Staging

The 4-class hypnogram

NOOP detects sleep/wake and attempts to classify four stages: Wake, Light, Deep (slow-wave), and REM (rapid eye movement). This is the same classification used in clinical sleep studies, but without EEG — the ceiling for 4-class accuracy (without brain waves) is ~6573% epoch-by-epoch agreement (Walch et al., 2019).

Honest hedging: These stages are approximations, not validated against polysomnography (PSG, the clinical gold standard). Light/deep separation is the weakest link. Deep-minute estimates are the least reliable output. Use these trends over time, not individual-night predictions.

Stage 0: Gravity-based sleep/wake spine

The foundation is stillness. NOOP tracks the magnitude of gravity changes (accelerometer) and identifies periods where motion is minimal:

  1. Per-sample motion proxy = L2 norm of the gravity-vector change from the previous sample.
  2. A sample is "still" if this delta is < 0.01 g.
  3. Roll forward in a 15-minute window: if ≥70% of samples are still, the center is marked "sleep".
  4. Contiguous still runs ≥60 minutes (merging gaps < 15 min) are candidates.
  5. HR confirmation: the run's mean HR must be ≤1.05× the day's median HR (the sleep baseline). This rejects false sleep from long stillness at rest.

Additionally, NOOP computes the te Lindert 30-second ColeKripke sleep index per epoch as a cross-check:

SI = 0.001 · Σ w_i · A_i    (sleep iff SI < 1)
weights = [106, 54, 58, 76, 230, 74, 67]

(This is a classical 7-channel electroencephalographic index adapted for actigraphy.)

Stage 1: Cardiorespiratory features per 30-second epoch

Over a rolling 5-minute window, NOOP extracts:

  • Mean heart rate — averaged over the window.
  • HR variability (Walch difference-of-Gaussians) — two Gaussian convolutions of HR (σ = 120 s and 600 s) subtracted; this captures medium-scale HRV oscillations.
  • RMSSD and SDNN — computed from range-filtered R-R intervals (cleaned via HRVAnalyzer).
  • Respiration rate and RRV — extracted from the raw 1 Hz respiration channel via a peak detector (detrend → find local maxima ≥2 s apart → bin into breath intervals 1.512 s → median interval → rate = 60 / interval).

Omission: Frequency-domain HRV (high-frequency HRV, LF/HF ratio) is not computed because FFT and signal processing require libraries unavailable on-device. The parasympathetic-tone signal is RMSSD only. The respiration peak-finder is a faithful port of the reference method (which also avoided scipy).

Stage 2: Percentile-band classifier

The session's sleep-period epochs (ColeKripke = sleep) define reference distributions for each feature. Each epoch is then classified by comparing its features to these percentiles:

Class Rule
Wake Sustained motion (≥15% of epoch) AND activated cardiac (high HR or high DoG-HR), OR no HR data
Deep Still (≤10% motion) AND high parasympathetic tone (RMSSD ≥70th percentile) AND low HR (≤25th percentile) AND regular respiration
REM Still body AND activated cardiac AND irregular respiration (RRV ≥65th percentile); fallback requires both cardiac signals if respiration is unavailable
Light Everything else (the default)

Stage 3: Smoothing and physiology re-imposition

  1. 5-epoch median smoothing of the label sequence — smooths out single-epoch noise.
  2. No REM in the first 15 minutes — REM sleep doesn't appear right at sleep onset; demote to light.
  3. No deep after the first third — deep sleep is front-loaded; demote later deep epochs to light.
  4. Force pre-onset and post-final-wake to wake.

Consecutive same-stage epochs are merged into segments tiling the session.

Outputs

  • SleepSession: start, end, in-bed efficiency (asleep / in-bed), per-session resting HR, average HRV (RMSSD over 5-min windows), and a reconstructed hypnogram (stage durations).
  • AASM-style roll-up: time-in-bed (TIB), total sleep time (TST), sleep-period time (SPT), sleep onset latency (SOL), REM latency, wake after sleep onset (WASO), efficiency, disturbances, and deep/REM/light minutes.

Baselines: Your personal rolling averages

Recovery and strain scoring depend on personal baselines — your own typical HRV, resting HR, and respiration — so NOOP learns them as you wear the strap. Two interchangeable approaches produce the same result:

Winsorized EWMA (production)

A robust, recency-weighted center with exponential-moving-average spread tracking:

  • Half-life → smoothing factor: HRV center uses 14-night half-life; spread uses 21-night (slower, more conservative).
  • Sanity gate: values outside the per-metric range (e.g., HRV 5250 ms) are skipped and held.
  • Hard outlier rejection: values > 5× spread away are recorded but not folded into the baseline.
  • Winsor clamp: fold only within ±3× spread of the current baseline, so a single exceptional night can't yank the center. The spread itself uses the unclamped deviation so real physiological change is still tracked.
let clamped = max(lo, min(hi, value))                    // ±3·spread
let newBaseline = lb * clamped + (1 - lb) * state.baseline
let newSpread   = max(floorSpread, ls * abs(value - newBaseline) + (1 - ls) * state.spread)

Trailing-window mean (auditable alternative)

Plain mean and sample SD (ddof = 1) over the last 30 valid nights. The σ floor is applied and converted back to absolute-deviation space so the two methods recover the same Gaussian σ.

Baseline status lifecycle

Status Condition
calibrating Fewer than 4 valid nights (no score yet)
provisional 413 valid nights (usable, higher uncertainty)
trusted 14+ valid nights
stale Trusted but no update for > 14 days

Workouts and Exercise Detection

Automatic detection from HR and motion

NOOP detects exercise periods without manual logging — a sustained window (≥5 minutes) where both gates hold:

  1. Elevated HR: above RHR + 15 bpm (the resting HR of your session + margin).
  2. Sustained motion: gravity-derived intensity (10-second trailing mean) above 0.20.

Active samples are grouped into runs, merged if gaps are < 150 seconds, then qualified: ≥50% of the bout must be in Edwards zone 2+ to count.

Metrics per bout

Per workout, NOOP reports:

  • Average and peak heart rate.
  • Duration, Edwards zone distribution, mean %HRR.
  • Strain (computed via the same StrainScorer as daily strain).
  • Calories (see below).

Calories: Keytel + revised HarrisBenedict

Per-second blend with sex-specific coefficients:

  • Below RHR + 0.30 × HRR: use revised HarrisBenedict (resting metabolic rate).
  • Above: use Keytel (2005) active expenditure model, sex-specific.

The blend provides a smooth transition. Approximate — not laboratory calorimetry.


Interactive: Correlations, Behavior Effects, and Comparisons

NOOP provides three on-device interrogation engines for your own data:

Correlation analysis (Pearson r)

r = Σ(xx̄)(yȳ) / sqrt( Σ(xx̄)² · Σ(yȳ)² )
slope = Σ(xx̄)(yȳ) / Σ(xx̄)²
t-statistic = r · sqrt( (n2) / (1r²) )
p-value ≈ 2·(1  Φ(|t|))    [normal approximation]
  • Returns nil for fewer than 3 pairs or zero variance.
  • The normal approximation slightly understates p for small n (true Student-t tails are heavier), but is fully deterministic on-device.
  • Supports lagged correlations (e.g., today's strain vs tomorrow's recovery) via day-aligned and shifted series.

Behavior effects (Welch t-test)

Splits days where you logged a behavior (Alcohol, Meditation, etc.) from days you didn't, and compares an outcome (Recovery, HRV, Sleep) between groups:

sp = sqrt( ((n11)·s1² + (n21)·s2²) / (n1+n22) )     [pooled SD]
d = (m1  m2) / sp                                      [Cohen's d]
t = (m1  m2) / sqrt(s1²/n1 + s2²/n2)                  [Welch t]
  • significant requires p < 0.05 and min(nWith, nWithout) ≥ 5 (guards against spurious significance from a handful of days).
  • Results are ranked by effect size (|d|), significant findings first.
  • Plain-English summary: "On days you logged 'Alcohol', Recovery was 12% lower (avg 61 vs 69, n=140 vs 498)."

Period comparison

Month-over-month, quarter-over-quarter, or custom window comparison of a single metric:

SeriesStat: mean, median, min, max, sample SD (ddof=1), n, and OLS slope-per-day
PeriodComparison: signed delta, % change, direction (-1/0/+1)

Advanced: Readiness and Training Load

NOOP's Readiness screen synthesizes several sports-science signals from your own history into a single readout ("Primed / Balanced / Strained / Run down"):

Drivers

  • HRV vs baseline (Plews/Buchheit) — parasympathetic tone shift.
  • Resting-HR drift (Lamberts) — orthostatic stress rising.
  • Respiratory-rate drift — autonomic load indicator.
  • Acute:chronic workload ratio (ACWR) (Gabbett) — training volume balanced against your typical load.
  • Training monotony (Foster) — variability of your training over the past week.

All signals are z-scored against your own baselines and combined with a logistic curve into a single readiness band. Approximate, not medical advice — this is the same class of analysis used in sports-science labs for coaching, not a clinical screen.


Illness and Strain Early-Warning

NOOP watches for the classic early-illness/elevated-strain signature on-device by comparing your last ~2 days against a ~28-day baseline (ending 3 days ago, to avoid contamination):

Signal Baseline shift Anomaly condition
Resting HR ↑ Elevated Recent mean ≥ baseline mean + 5 bpm
HRV ↓ Depressed Recent mean ≤ baseline mean × 0.80 (20%)
Skin temp ↑ Elevated Recent mean deviation ≥ +0.6 °C
Respiration ↑ Elevated Recent mean ≥ baseline mean + 1.5 bpm

A banner appears only when two or more anomalies fire together (e.g., RHR up + HRV down + skin-temp up), the classic early-illness signature. Requires at least 14 days of history.

Important: This is a wellness nudge from your own baselines, not a clinical screen. It is informational only — not medical advice, not a diagnosis.


Design principles & honesty notes

Approximate by design

Every metric — recovery, strain, sleep stages, workout intensity, calories — is an explicit approximation of published methods. The source code documents exactly where it substitutes (Malik local-median instead of Kubios, RMSSD-only parasympathetic tone instead of frequency-domain HRV, normal-approximation p-values instead of Student-t tabled values).

Deterministic and testable

No randomness, no wall-clock dependence inside the math, no database access. Same inputs always produce the same outputs, making the whole package unit-testable against fixed vectors.

Robust statistics

  • Z-scores use EWMA mean-absolute-deviation (×1.253 to Gaussian σ); rounding resistant.
  • Resting HR uses 5-minute bin minima; single-beat dips are rejected.
  • Heart-rate display uses windowed medians; outliers don't spike the plot.
  • Baselines use Winsor clamping; a single exceptional night doesn't yanked the center.

Cold-start honesty

When a baseline isn't trustworthy yet, the scorer returns nil rather than a fabricated number. More painful than guessing, but more truthful.

Not a medical device, not medical advice

None of this is diagnostic or medically actionable. HRV, recovery, strain, sleep stages, and the illness early-warning are wellness approximations, not clinical measurements. Consult a qualified professional for health decisions.


References and further reading

The methods implemented in NOOP draw from peer-reviewed literature:

  • Task Force (1996). Heart rate variability: standards of measurement, physiological interpretation and clinical use. Circulation, 93(5), 10431065.
  • Karvonen, M. J. (1957). Effects of vigorous exercise on the heart. Work and Stress, 9, 39.
  • Edwards, S. (1993). The heart rate monitor book. Polar Electro (Oy).
  • Banister, E. W. (1991). Modeling elite athletic performance. In Physiological testing of the high-performance athlete (pp. 403424). Human Kinetics.
  • Tanaka, H., Monahan, K. D., & Seals, D. R. (2001). Age-predicted maximal heart rate revisited. Journal of the American College of Cardiology, 37(1), 153156.
  • Keytel, L. R., et al. (2005). Prediction of energy expenditure from heart rate monitoring during submaximal exercise. Journal of Sports Sciences, 23(3), 289297.
  • Walch, O., et al. (2019). Sleep stage prediction with raw acceleration and photoplethysmography heart rate data derived from a consumer wearable device. Sleep, 42(12), zsz180.
  • Plews, D. J., Laursen, P. B., & Buchheit, M. (2017). Training adaptation and heart-rate variability: A systematic review. Sports Medicine, 47(8), 15211538.
  • Gabbett, T. J. (2016). The training-injury prevention paradox: should athletes be training smarter and harder? British Journal of Sports Medicine, 50(5), 273280.
  • Foster, C., et al. (1995). A new approach to monitoring exercise training. Journal of Strength and Conditioning Research, 12(3), 109115.

See also