Skip to content

[master] Resting heart rate too low and HRV too high #550

Open

Description

@pilleuspulcher-blip

NOOP version

9.0.1-staging

Platform

Android

OS version

16

WHOOP strap model

WHOOP 4.0

Where the data is coming from

Live Bluetooth (strap connected)

Test Centre profile

HRV & Autonomic

What happens

Even though live heart rate in Noop and Whoop is equal, the RHR in Noop is about 5 bpm lower (compared to Whoop and Polar H10).

HRV (activated deep sleep window doesn't change anything) is too high in Noop (49) vs Whoop and Polar (both 30).

Something seems to be wrong with the calculation.

Image Image

noop-strap-log-260717-0949.txt

What you expected instead

There should be equal data to calculate effort, rest and strain in the correct way.

Steps to reproduce

Strap log

<details><summary>Strap log (last 40 lines, redacted)</summary>


09:39:13  [display] frameSummary frames=60 mean=19.6ms p95=16.6ms hitches=2 worst=149.1ms threshold=33.0ms
09:39:14  [display] frameSummary frames=60 mean=19.3ms p95=16.6ms hitches=2 worst=99.4ms threshold=33.0ms
09:39:14  Get Data Range raw frame (#451 — for offset analysis): aa1800ff280222dc596a90512f0000000000000000000104a2e54ab4
09:39:14  Strap newest banked record: 2026-07-17 09:39 (from data range)
09:39:14  [universal] clockDrift newest=2026-07-17 07:39:14 wall=2026-07-17 07:39:14 newestVsWall=+0s clockOk
09:39:15  [display] frameSummary frames=60 mean=18.5ms p95=16.6ms hitches=1 worst=132.6ms threshold=33.0ms
09:39:16  [display] frameSummary frames=60 mean=16.0ms p95=33.1ms hitches=3 worst=198.8ms threshold=33.0ms
09:39:17  [display] frameSummary frames=60 mean=12.3ms p95=11.1ms hitches=2 worst=55.3ms threshold=33.0ms
09:39:18  [display] frameSummary frames=60 mean=14.7ms p95=11.1ms hitches=2 worst=121.6ms threshold=33.0ms
09:39:19  [display] frameSummary frames=60 mean=17.7ms p95=18.4ms hitches=1 worst=121.6ms threshold=33.0ms
09:39:20  [display] frameSummary frames=60 mean=20.1ms p95=16.5ms hitches=2 worst=148.9ms threshold=33.0ms
09:39:21  [display] frameSummary frames=60 mean=21.5ms p95=49.6ms hitches=3 worst=165.5ms threshold=33.0ms
09:39:22  [display] frameSummary frames=60 mean=17.1ms p95=16.5ms hitches=1 worst=49.6ms threshold=33.0ms
09:39:24  [display] frameSummary frames=60 mean=21.8ms p95=16.5ms hitches=2 worst=182.0ms threshold=33.0ms
09:39:25  [display] frameSummary frames=60 mean=20.1ms p95=16.5ms hitches=2 worst=132.4ms threshold=33.0ms
09:39:26  [display] frameSummary frames=60 mean=19.9ms p95=16.5ms hitches=2 worst=182.0ms threshold=33.0ms
09:39:27  [display] frameSummary frames=60 mean=21.8ms p95=49.6ms hitches=3 worst=182.0ms threshold=33.0ms
09:39:29  [display] frameSummary frames=60 mean=19.6ms p95=16.5ms hitches=2 worst=115.8ms threshold=33.0ms
09:39:29  [display] frameSummary frames=60 mean=15.0ms p95=19.8ms hitches=2 worst=148.9ms threshold=33.0ms
09:39:30  [display] frameSummary frames=60 mean=12.3ms p95=12.3ms hitches=2 worst=66.3ms threshold=33.0ms
09:39:31  [display] frameSummary frames=60 mean=12.7ms p95=11.1ms hitches=2 worst=66.3ms threshold=33.0ms
09:39:32  [display] frameSummary frames=60 mean=12.0ms p95=11.1ms hitches=1 worst=66.3ms threshold=33.0ms
09:39:33  [display] frameSummary frames=60 mean=17.0ms p95=28.2ms hitches=2 worst=165.8ms threshold=33.0ms
09:39:34  [display] frameSummary frames=60 mean=22.1ms p95=49.8ms hitches=3 worst=165.8ms threshold=33.0ms
09:39:35  [display] frameSummary frames=60 mean=19.6ms p95=16.6ms hitches=2 worst=132.6ms threshold=33.0ms
09:39:36  [display] frameSummary frames=60 mean=19.6ms p95=16.6ms hitches=2 worst=132.6ms threshold=33.0ms
09:39:38  [display] frameSummary frames=60 mean=21.6ms p95=16.6ms hitches=2 worst=182.4ms threshold=33.0ms
=== Capture check ===
universal: present (dayOwner day=)
sleep: present (gate run=)
connection: present (clockDrift )
workouts: MISSING (expected session event=)
display: present (dataVolume dbRows=)
import: MISSING (expected import parser=)
steps: present (stepsEst )
battery: present (battery series=)
recovery: MISSING (expected charge score=)
hrv: present (via hrv nightSummary)
INCOMPLETE: missing workouts, import, recovery
bundle ok files=3 report=present meta=present screenshot=present crash=present leaks=0

</details>

Before you submit

  • I attached my exported NOOP .zip (not a screenshot of the Live screen). If my bug is a clearly reproducible UI issue I described exactly how to see it instead.
  • I attached or pasted a strap log above — or my bug is a clearly reproducible UI issue that doesn't need one (e.g. a typo, a button that does nothing) and I've described exactly how to see it.
  • I checked the Troubleshooting guide — especially "history syncs but no data" (most often the strap isn't banking history to flash; charge it to 100% and reconnect).
  • I checked the FAQ and — if I'm on a WHOOP 5.0 / MG — the 5.0/MG status page, and this isn't an already-known limit (e.g. SpO₂, or recovery/sleep on the 5/MG).
  • I've left out anything personal (strap serial, account email, raw export).

Activity

  1. added 2 commits that reference this issue on Jul 19, 2026
  2. added a commit that references this issue on Jul 22, 2026
  3. ryanbr commented on Jul 25, 2026

    @ryanbr
    Owner

    First real evidence on the HRV half, from @pilleuspulcher-blip's log in #803 (WHOOP 4.0, 9.1.1-staging):

    day=2026-07-16  coverage=2.54  collapsedCov=1.99  dupBeats=62   rmssd=52ms
    day=2026-07-15  coverage=0.89  collapsedCov=0.88  dupBeats=34   rmssd=48ms
    

    The diagnostic's own rule is that collapsedCov well below coverage means the over-count is same-second and a de-dup fixes it; still high means cross-second overlap. 07-16 only drops 2.54 → 1.99, so it is cross-second — the same-second de-dup previewed here would not be sufficient on this data. 07-15 is clean at 0.89, so it is night-specific rather than constant.

    Worth noting the two nights produce similar RMSSD (52 vs 48) despite very different coverage, so the relationship between over-count and inflated HRV is not simply linear. More logs would help before committing to a fix shape.

    On the RHR half: that reporter's ~7 bpm gap versus the WHOOP app is fully explained by floor-vs-mean (floor=44 nightMean=50, floor=40 nightMean=48), which is by design. The recurring problem is discoverability — the explanation lives in docs/ANALYTICS.md and a v7.0.0 release note saying it is "answerable straight from the log", so a user has to export and read a log to learn their RHR is not broken. Surfacing it where RHR is displayed would close most of these reports; noting it here rather than opening a separate issue since this is the master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions