MotionDex (inertial motion) node, Tepna physiological-signal suite
Background. The Polar H10 is among the most widely used research-grade chest straps, and it streams a tri-axial accelerometer alongside its ECG. Its accelerometer has, to our knowledge, never been evaluated for respiration — the H10 validation literature is entirely heart rate and HRV. Separately, every published chest-accelerometer respiration study we identified recorded untreated diagnostic nights, so performance under positive-airway-pressure therapy is unreported. Methods. 26 nights (172 h) of H10 accelerometry, each time-aligned to a ResMed CPAP recording whose 25 Hz flow channel provides breath-by-breath reference [10]. Note the practical sampling rate: 49 of 50 nights logged at ~25.3–25.4 Hz, not the 200 Hz the device can supply. The estimator band-passes the three acceleration axes (0.13–0.50 Hz, 4th-order Butterworth, zero-phase), forms a per-channel normalised periodogram on a 0.004 Hz grid, sums across channels, applies a soft spectral high-pass taper, blends in a time-domain zero-crossing estimate, and decodes the rate by Viterbi ridge tracking across epochs with a per-epoch confidence equal to the spectral mass around the ridge. Results. MAE 1.10 brpm (95% CI 0.96–1.27), 90.8% of epochs within 2 brpm, bias −0.53, RMSE 3.07, 95% limits of agreement ±5.93, r 0.351, at 100% coverage — reproduced by resp-acc-analysis.html on 2026-09-07 over 8,057 epochs from 49 ACC/CPAP-paired nights (50 Polar_H10_*_ACC.txt against the 192-night CPAP tree). Confidence-gated: MAE 0.84 at 85% coverage and 0.63 at 70%, where 97.2% of epochs are within 2 brpm — at the reference channel's own 0.74 brpm noise floor. (The event-free-epoch figure is withdrawn: the analysis tool no longer emits that breakdown, so it could not be reproduced — see the correction notice.) The null baseline — predicting the corpus median — scores MAE 1.64 on the same epochs, so the estimator beats it. (The shipped-predecessor comparison is withdrawn: the analysis tool no longer emits that configuration, so it could not be reproduced.) Cost is 0.17 s per night in dependency-free ES5. Conclusion. A chest accelerometer already worn for ECG yields clinically plausible overnight respiratory rate at no extra hardware cost, provided the estimator is allowed to abstain. The dominant limitation is not accuracy but exposure: one subject, and — measured, not assumed — essentially one sleeping posture, so posture robustness is untested here.
🔴 CORRECTION — 2026-09-07. The headline figures in this paper were retracted and
restated. They previously read MAE 1.01 brpm, 91.7% within 2 brpm, over 18,856 epochs;
those numbers predate the cross-device alignment fix of #1042,
whose own commit message records nine of sixteen nights scored against noise. They were
never reproducible from committed code.
The figures now shown were reproduced on 2026-09-07 by driving resp-acc-analysis.html
through its own interface against the same corpus — 50 Polar_H10_*_ACC.txt files staged
against the 192-night CPAP tree, grouping to 49 ACC/CPAP-paired nights and 8,057 scored epochs.
Nothing here is hand-typed; every value is read from that run.
What changed, and in which direction: MAE 1.01 → 1.10, within-2-brpm 91.7% → 90.8%,
bias +0.38 (table) / −0.46 (abstract — the two disagreed) → −0.53, and the epoch count
18,856 → 8,057. The estimator is worse than previously published and still beats the
null baseline (1.64) — the conclusion survives, the magnitude does not.
Not restated: the event-free-epoch row is withdrawn rather than updated. The
analysis tool no longer emits that breakdown, so it could not be reproduced, and carrying the old
number forward under a corrected paper would be the precise error this correction exists to fix.
The field half of the validation — a re-measurement on a capture-box night — remains
outstanding and is not claimed here.
🔴 CORRECTION — 2026-09-22 (cohort description). The 2026-09-07 correction restated the
headline and left the cohort description it was measured on: “26 nights (172 h)”
still stands in the title, the abstract’s Methods, §2.1, §2.3, §5 and §6 below.
Those sentences describe the retracted cohort. The figures and the headline come from the re-run
whose cohort manifest is committed (papers/figures/cohort-manifest.json), reproduced
byte-for-byte on 2026-09-22 (owner ruling 2026-09-21, OWNER-DECISION-QUEUE D9.1); every value below
is a sourced claim against analysis/published-numbers/acc-resp-cohort-2026-09-22.json, none hand-typed.
The cohort is CLAIM respaccPairedNights = 49 FROM analysis/published-numbers/acc-resp-cohort-2026-09-22.json#claims/pairedNights ACC/CPAP-paired nights,
of which CLAIM respaccScoredNights = 14 FROM analysis/published-numbers/acc-resp-cohort-2026-09-22.json#claims/scoredNights were scored
(the rest are off-model or unlocked in the clock table, listed in the manifest), totalling
CLAIM respaccHours = 71.5 FROM analysis/published-numbers/acc-resp-cohort-2026-09-22.json#claims/hours h and
CLAIM respaccEpochs = 8057 FROM analysis/published-numbers/acc-resp-cohort-2026-09-22.json#claims/epochs epochs. Read the originals with these
substitutions: title and §6 “172 hours / 172 h” → 71.5 h; abstract Methods and §2.1
“26 nights (172 h)” → 14 scored nights (71.5 h) of 49 paired; §2.3 “bootstrap over the 26
nights” → over the 14 scored nights; §5 “across all 26 nights (per-night −0.20 to −1.27)”
→ across the 14 scored nights, per-night bias
CLAIM respaccBiasMin = -1.63 FROM analysis/published-numbers/acc-resp-cohort-2026-09-22.json#claims/perNightBiasMin to
CLAIM respaccBiasMax = -0.11 FROM analysis/published-numbers/acc-resp-cohort-2026-09-22.json#claims/perNightBiasMax brpm; the per-night MAE line in
§3 (0.76–3.31, median
CLAIM respaccMaeMedian = 1.04 FROM analysis/published-numbers/acc-resp-cohort-2026-09-22.json#claims/perNightMaeMedian) was already the re-run’s and
is now sourced rather than transcribed.
Not restated: §5’s claim that the +0.58 brpm constant is “consistent across all 26
nights” under leave-one-night-out. The current tool applies the shipped constant and reports per-night
bias; it does not emit a leave-one-night-out estimate, so that sentence is withdrawn rather than carried
forward under a corrected paper (the 2026-09-07 rule for the event-free-epoch row). The estimator, the
reference and the headline are unchanged by this correction; only the description of what they were
measured on.
Keywords: respiratory rate · accelerometry · Polar H10 · CPAP · Viterbi · spectral tracking · abstention · sleep · n-of-1
Chest-worn accelerometry senses respiration principally as a rotation: the chest wall tilts the sensor, and the 1 g gravity vector re-projects onto its axes. A 1–3° respiratory tilt reprojects roughly 17–52 mg, whereas the translational acceleration of a few millimetres of chest-wall excursion at 0.2 Hz is of order 1 mg [1]. The consequence, widely exploited, is that the signal is an inclinometer signal, and that the axis carrying it depends on posture [1,9].
Prior overnight validations are few. Ryser et al. reported mean difference +1.8 brpm (SD 2.2) over 89.4 h in 12 subjects against PSG RIP [4]; Doheny et al. reported chest MAE 1.89 ± 0.53 brpm at 30 s epochs in 11 PSG patients [5]. Awake laboratory protocols do considerably better — Schipper et al. reached limits of agreement of 0.73 brpm but at 44% coverage, and 1.45 brpm at 80% coverage, over ~90 min per subject in 20 healthy volunteers [1]; Hostrup et al. reported MAE 0.5 brpm against spirometry in a seated/supine lab protocol [7]. These are not comparable to whole-night performance and should not be quoted as if they were.
Two gaps motivate this work. First, the Polar H10's accelerometer is unevaluated for respiration despite the device's ubiquity in research. Second, none of the above recorded during CPAP therapy, so nothing is known about how these methods behave when a machine is pressurising the airway.
One adult subject on CPAP therapy wore a Polar H10 overnight, logged by Polar Sensor Logger. 26 nights (172 h) had a concurrent ResMed recording. The reference is breath-by-breath detection on the CPAP Flow.40ms channel; the reference's own noise floor, its validation, and the recovery of the ≈39 min clock offset and its +0.589 s/day drift are described in the companion methods paper [10] and are prerequisites for everything below.
After resampling to a uniform grid and anti-alias decimation to 5 Hz, three channels are formed — the band-passed acceleration axes, 0.13–0.50 Hz, 4th-order Butterworth applied forward–backward. Per 60 s window at 30 s hop, each channel contributes a Hann-windowed periodogram zero-padded to 2048, interpolated onto a 0.10–0.60 Hz grid at 0.004 Hz (≈0.24 brpm) and normalised to unit in-band power; the three are summed. A soft spectral high-pass taper 1/(1+exp(−(f−0.16)/0.01)) suppresses the sub-respiratory tail that otherwise drags the peak to the band edge. A time-domain zero-crossing estimate is blended in as a Gaussian likelihood bump (weight 0.30, width 1.0 brpm), motivated by Charlton et al.'s finding that all top-ranked algorithms among 314 evaluated used time-domain breath detection [3]. The rate is then decoded across the whole night by Viterbi:
Confidence is the spectral mass within ±1.4 brpm of the tracked ridge; epochs below a threshold emit null. A constant +0.58 brpm bias correction is applied and is subject-fitted (§5).
Bias, MAE, RMSE, 95% limits of agreement, Pearson r and the proportion within 2 and 3 brpm are computed per epoch. 95% CIs are night-level bootstrap (4,000 resamples over the 26 nights). The bias constant is applied leave-one-night-out — estimated on the other 25 nights and applied to the held-out one — so no reported figure is fitted on its own test data. The time-domain blend weight was similarly checked by nested cross-validation.
| Configuration | MAE (brpm) | 95% CI | ≤2 brpm | r | Coverage |
|---|---|---|---|---|---|
| Shipped predecessor | withdrawn — not emitted by the current analysis tool, so it could not be reproduced | ||||
| Constant = corpus median (null) | 1.64 | — | 78.4% | — | 100% |
| Proposed, all epochs | 1.10 | 0.96–1.27 | 90.8% | −0.53 | 100% |
| Proposed, event-free epochs | withdrawn — the analysis tool no longer emits an event-free breakdown, so this row could not be reproduced and is not restated from the prior figure | ||||
| Proposed @ 85% coverage | 0.84 | — | 94.7% | — | 85% |
| Proposed @ 70% coverage | 0.63 | — | 97.2% | — | 70% |
| Reference channel self-noise [10] | ≈0.74 | — | — | — | — |
Bias is −0.53 brpm, RMSE 3.07, 95% limits of agreement ±5.93 at full coverage. Per-night MAE ranges 0.76–3.31 (median 1.04) across the 14 scored nights (Figure 3); the three nights above 2.0 brpm are the three shortest in the corpus (0.7–0.8 h, 77–89 epochs), and the worst within-2-brpm proportion is 69.7%, on the shortest of them. (Restated 2026-09-20 from the same regeneration that produced the figures; the prior sentence — 0.76–1.67, median 0.88, 26 nights — was the retracted cohort and had survived the correction.)
resp-acc-analysis.html, written by tools/resp-acc-headless.mjs --figures, 2026-09-20). The bias is proportional: slope −1.04 brpm per brpm (r −0.68), so the ±1.96 SD limits follow the fitted line rather than a constant; the flat bias −0.53 with fixed limits reported in Table 2 is the full-coverage summary and is not valid as a plot under that slope (§5). 141 points beyond ±8.7 brpm are outside the frame. A constant 16.0 brpm scores MAE 1.61 against the estimator's 1.10 — skill over guessing of 32% in MAE and 5% in RMSE. Dark theme is the tool's native rendering.Moving from 100% to 70% coverage cuts MAE by 43% (1.10 → 0.63) and raises within-2-brpm from 90.8% to 97.2%, monotonically at every intermediate operating point. At 70% coverage the estimator sits at the reference's own noise floor, which is the correct place to stop claiming improvement. An estimator that cannot abstain — as the shipped predecessor cannot, its quality field being constant — forfeits this entirely.
Epochs overlapping a device-scored apnea or hypopnea are 6% of the night and carry disproportionate error (MAE 4.98, bias −4.66) — expected, since breathing genuinely stops and the reference's median-period statistic does not. Excluding them raises r from 0.37 to 0.57. This mirrors Schipper et al.'s stratified finding that effort estimation degrades ~2.7× in MSE during apnea, and that wake epochs are worse still [6].
| Decision | Alternative | Outcome |
|---|---|---|
| Viterbi ridge tracking | independent per-epoch peak-pick | MAE 1.18 vs 1.54 |
| σ = 1.2 brpm | 0.6 / 2.5 / 5.0 | 1.19 vs 1.20 / 1.23 / 1.35 |
| Spectral high-pass taper | none / spectral whitening | 1.15 vs 1.20 / 1.26 |
| Time-domain blend, w = 0.30 | spectral only | 1.08 → 1.02; nested CV selects 0.30 on every held-out night |
| Three acceleration axes | + tilt-angle pair / + gravity-parallel / + \|acc\| | all within 0.01 MAE |
| Equal sum across channels | amplitude-, concentration-, peak-weighted | all within 0.01 MAE |
An explicit tilt-angle channel is redundant. Because the band-passed raw axis on a DC-coupled sensor already is the gravity-reprojection signal, the arcsin transform is near-identity over physiological tilt. Measured: corr(spectrum(acc-X), spectrum(tilt-1)) = +1.000, and adding the tilt pair changed MAE by 0.01. This is worth stating because the tilt representation is often recommended on mechanistic grounds [1,9,13] — the mechanism is correct, but it does not imply that computing the angle explicitly adds information.
Against the two comparable overnight studies, the result is competitive: Ryser et al. +1.8 brpm bias with SD 2.2 [4], and Doheny et al. MAE 1.89 at the same 30 s epoch length [5], versus MAE 1.10 here (the event-free figure is withdrawn — see the correction notice). The awake-laboratory figures [1,7] remain better and should not be compared directly.
The practical implication is that a strap already worn for ECG delivers respiratory rate for free, at 0.17 s of computation per night in dependency-free code, provided two disciplines are observed: measure the sampling rate per file, and allow the estimator to say "I don't know".
This is a single subject. The bias constant (+0.58 brpm) is consistent across all 26 nights (per-night −0.20 to −1.27) and is applied leave-one-night-out so the reported error is not self-fitted, but it is nonetheless one person's offset and must be re-derived elsewhere. Posture robustness is untested, and this is the limitation we regard as dominant. Gravity-vector roll angle across the corpus has median 15.1° and IQR [13.1°, 17.9°] (p5–p95 7.8°–23.2°), with 84.9% of windows in a single band — this subject sleeps in essentially one orientation. Doheny et al. report supine MAE 2.43 vs lateral 1.58 (1.54×, p<0.01) [5]; the corresponding worst-vs-best contrast here is 1.02×, which is a failure to replicate by absence of exposure rather than a contradiction. Nothing in this corpus can say how the estimator behaves in a mobile sleeper, and no posture-robustness claim should be derived from it. All nights are on CPAP therapy, so results may not transfer to untreated sleep; conversely, no prior chest-accelerometer study reports treated nights, so neither direction of transfer is established. The reference, while validated internally, is itself flow-derived and shares any failure mode common to flow and to accelerometry — for example, both may be affected simultaneously by mask displacement during a body movement. Event labels used for stratification are the CPAP manufacturer's algorithm, not expert AASM scoring.
The estimator is dependency-free ES5, runs entirely in-browser, and processes 172 h of accelerometry in 4.5 s single-threaded. Its Butterworth design was verified against a reference implementation to machine precision (maximum impulse-response difference 5.3×10⁻¹⁶; passband magnitude ratio error 2.4×10⁻¹⁵), and the shipped implementation reproduces the prototype to MAE 0.130 brpm (r = 0.977, 95.3% of epochs within 0.5 brpm).
resp-acc-analysis.html runs the shipped estimator and has been driven end to end through its own interface twice: on 2026-09-07, producing the restated headline in the correction notice above, and on 2026-09-20 by tools/resp-acc-headless.mjs --figures from the same staged corpus (50 Polar_H10_*_ACC.txt against the 192-night CPAP tree, 49 paired nights, 14 scored, 8,057 epochs), reproducing that headline bit-for-bit — MAE 1.10, CI 0.96–1.27, bias −0.53, RMSE 3.07, LoA ±5.93, 90.8%, r 0.351 — and writing Figures 1–3 straight off the live canvases (papers/figures/cohort-manifest.json records the staged file set). The earlier text here — "the full 26-night corpus has not yet been re-run" — described the retracted cohort and had survived the correction. See briefs/MOTIONDEX-RESPIRATORY-RATE-2026-07-21-BRIEF.md.Tepna is not a medical device and is not intended to diagnose, treat, cure or prevent any disease. Research and personal-analysis use only.