Repository navigation
SpO₂ strap estimate (W3): per-reading nightly figures and truthful copy - #32
Merged
Merged
Conversation
The strap does not report byte 82 every second. It measures in windows of 30 records, 1200 s apart while the band scores SLEEP, and inside a window its value is a rolling figure that moves a few points and can blip for a single second. The night's low, its dips and its evidence count were resolved per second, so a one-second blip inside a steady window became the night's low and a dip that no window showed, and the count was seconds of one measurement. nightlySpo2CandidateNight now groups nonzero seconds into windows (no two more than 30 s apart, none spanning 60 s or more, so a strap that reported the byte continuously is still cut into readings) and values each window by the median of its in-band seconds, the lower middle value for an even count. The mean, low, high and dips are per window. `windows` and `windowsAttempted` are new, stored under two new metricSeries keys whose presence also marks a night resolved this way. The dip properties are renamed `dipSamples` and `dipSpanSeconds`: they are the dip readings' seconds and measured span, not time below the threshold. The Blood Oxygen card shows readings as valid of attempted, counts dip readings without claiming a duration, and shows dashes for the low and dips of a night scored before this change. The strap-log line names the windows. validate_spo2_candidate.py values a window the same way, so the comparison tool and the app give one nightly figure. PROTOCOL_SENSORS.md records the window facts measured on one MG: the schedule predicts 175 of 175 windows, and the status words at 77 and 79 mark every window with no exception. Also corrects the Polish "%lld of %lld" (it read "or") and adds the Italian "Readings". Replayed on seven stored nights against main: in-band seconds unchanged, the displayed mean moves on one night by one point, the low rises on six, and dips fall from ten to one, the one left being a whole window below the threshold. On all eleven backups the resolver matches an independent Python implementation night by night. The oracle is re-pinned and matched line for line by the same implementation. W03-003; W03-005 recorded from its review.
The Blood Oxygen card's footnote and the Settings toggle said the strap sends the byte 82 candidate every second. On the straps measured so far it takes a 30-second reading about every 20 minutes once the band scores the wearer asleep, and some readings fail without a value. Both now say that, scoped to the straps measured, and the card says a reading counts once, by its middle value, so a one-second blip inside it is not a dip. The night chart's caption says each point is one second the strap reported, and when a reading is a dip. WHOOP5_DEEP_DATA.md described per-second runs; it now describes the readings. Translated for the nine catalogue languages. W03-004.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
W3 Analytics, taken out of phase at the owner's request after the BLE-dossier fact-check. The WHOOP 5/MG SpO₂ strap estimate (byte 82 of the v18 record) is now resolved per strap reading instead of per second, and the copy stops saying the strap reports it every second. Finding rows are in
docs/review/workstreams/W03-analytics.md, one commit per fix. Nothing here changes what the app sends to the strap or what it stores inspo2Pct; the estimate stays behind the default-off Experimental toggle.What the strap actually does
Measured on one MG (
WS50_r00, firmware 50.39.1.0), 588,883 records over seven nights, now indocs/PROTOCOL_SENSORS.md("Byte 82 measurement windows"):Fixes
nightlySpo2CandidateNightnow groups nonzero seconds into windows (no two more than 30 s apart, none spanning 60 s or more) and values each window by the lower median of its in-band seconds. The mean, low, high and dips are per window. NewwindowsandwindowsAttemptedare stored under two new metricSeries keys, which also mark a night resolved this way. The card shows readings as valid of attempted, counts dip readings without claiming a duration, and shows dashes for the low and dips of a night scored before this change. The strap-log line names the windows.validate_spo2_candidate.pyvalues a window the same way, so the tool and the app give one nightly figure.WHOOP5_DEEP_DATA.mdno longer describes per-second runs.Recorded, not changed
Verification
testASingleSecondBlipInsideAWindowIsNotADipfails on the old resolver: one dip and a low of 77 inside a 95–96 window./code-review high. Fixed from them: no cap on window length, then a cap with no margin for a clock step (now 60 s); a dip caption that claimed seconds below the threshold; per-second figures of pre-change nights under per-reading copy; the tool valuing a window by its mean; stale comments and copy.main: in-band seconds unchanged on all seven, the displayed mean moves on one night by one point, the low rises on six, and dips fall from ten to one, the one left being a whole window below the threshold.swift testin StrandAnalytics: 2,197 run, 2 skipped, 0 failures.StrandTests: 1,992 run, 2 skipped, 0 failures. Strand and NOOPiOS build; NOOPiOS was built without the watch app. Tools/linux-capture: 235 tests pass. The doc-comment, protocol-example, source-reference and i18n gates pass. The four coreTools/failures are W11-001 (macOS/private/var), unchanged onmain.