docs(whoop5): SpO2 sleep path, SLEEPFLAG, skin-temp wire scale#807
docs(whoop5): SpO2 sleep path, SLEEPFLAG, skin-temp wire scale#807dnesdan wants to merge 1 commit into
Conversation
… scale Clarify WHOOP 5.0/MG protocol facts that already guide the decoders but were easy to miss in the docs: no GET_SPO2 command, on-device sleep-gated SpO2, v18 @82 as instrumentation-only, band sleep_state (wake/still/asleep/up), skin_temp_raw °C = raw/100 vs AS6221 chip LSB, and GOOSE/MAVERICK model ids. Helps #103 and #761 by giving owners a validation checklist and an honest capability story without inventing metrics or changing decode behaviour.
There was a problem hiding this comment.
Field map checks out — all 14 offsets match our decoder. Useful addition: I got @82 wrong in a doc earlier today because this summary did not exist yet.
Three things before merge:
- Where do the model-id ranges come from?
0…37 = MG,48…85 = 5.0is not corroborated anywhere in our code, and the wording reads app-derived. If it is from your own captures, say so and it stands. If it came from a decompile we cannot take it — clean-room rule, regardless of accuracy. - It conflicts now —
WHOOP5_DEEP_DATA.mdchanged under you today. One hunk, small rebase. - Add a row for
dynamic_acceleration@41— landed today, sits between@36and@45, so the table has a hole.
Happy to carry it in with the fixes on top and you as co-author, like #808, if the fork rebase is a hassle.
|
Heads-up: the conflict is now partly my doing. #814 rewrote the Two things from my review still open:
Also worth knowing, since it may change what you thought was submittable: #822 wrote down that decompile-sourced facts are admissible here with attribution, as unvalidated candidates. I'd told a contributor the opposite earlier and it wasn't our rule. Implementations and literals still stay out. The rest of this is good — the "no |
… for the export shape Two claims re-checked against the tree rather than left as inherited assertions. "There is no GET_SPO2-style opcode" was an overclaim. What we can actually say is that our CommandNumber catalogue has 80 commands and none reads oxygen, and that independent RE reports none. That is not proof about the strap's command space: §6 is explicitly a "safe subset", and the 98-vs-87 battery dispute (#818, #592) shows the map is incomplete. Restated as "nobody has found one", which still supports the useful conclusion — hunting a missing opcode is the wrong lead. The per-cycle export shape came from #807 and I had passed it through unverified. Now confirmed from our own code and cited: bloodOxygenPct is read on the physiological-cycles row beside recovery_score_pct and day_strain, keyed on cycleStart/cycleEnd (WhoopExportImporter.swift:272). So "one value per recovery cycle" is ours to assert, not a claim borrowed on trust.
Final pass over the two sections, checking each assertion rather than the ones I happened to doubt. Verified and now cited: the DIS characteristics are ours — BLEManager.swift:473-474 declares disSerialChar 2A25 "Serial Number String" and disHwRevChar 2A27 "Hardware Revision String". And the sleep-gating claim has in-tree corroboration: our own decode observes aux_byte_82 nonzero only while the band sleep flag reads asleep, so the doc cites that instead of asserting it bare. Marked, because it cannot be verified here: "naps and incomplete nights carry no SpO2 in the export" is a contributor observation from #807 about WHOOP's export behaviour, not something this repo can check from the wire. Kept because "my SpO2 is missing" otherwise reads as a decode failure, but labelled so nobody later cites it back as established. Every claim in these sections is now either checked against this tree or explicitly attributed as unverified.
…ty signals (#824) Re-implements the landable parts of #807 (@dnesdan) against current main. Their PR picked up a conflict when #814 rewrote the same @82 paragraph; writing fresh avoided moving a bigger conflict onto them. KEPT, AND THEIRS — the SpO2 protocol facts, recorded nowhere else: - No SpO2 read opcode is known. Our CommandNumber catalogue carries 80 commands and none reads oxygen. - Computed on-device during sleep — our own decode corroborates it, aux_byte_82 is nonzero only while the band sleep flag reads asleep. - The CSV blood_oxygen_pct is a per-cycle aggregate, read on the physiological-cycles row beside recovery_score_pct and day_strain (WhoopExportImporter.swift:272), so it will not equal a mean of wire samples. - A night with no export value is a real gap, not a decode failure. DROPPED — their model-id range table (0…37 = MG, 48…85 = 5.0). Not for provenance: #822 settled today that decompile-sourced facts are admissible with attribution, so the original objection no longer applies. Dropped because we already resolve identity a better way and use model-ids nowhere. Whoop5Variant reads the DIS serial prefix (5AM/5AG) and the WG50 hardware-revision token — shipped in #781, sourced from #520 — and that is documented here from our own code, including that contradictory signals return .unknown and that .unknown is not MG. CHANGED — the v18 field map is a pointer, not a copy. Their §8.1 duplicated the authoritative table in BLE_REVERSE_ENGINEERING.md, and drift had already started: theirs predates the dynamic_acceleration@41 row added earlier today. One table, one place to keep correct. FOUR REVIEW PASSES, ALL FINDING MY OWN MISTAKES - Wrong heading level and wrong parent: ## 7.9/7.10 under a section whose subsections are ### 7.1..7.5, and neither topic belongs under "Historical-data offload". Moved to top-level 9 and 10, File map renumbered to 11 after checking nothing cross-references §8/§9 and that the file has no TOC. - Overclaimed negative: "there is no GET_SPO2 opcode" restated as "none is known". §6 is explicitly a safe SUBSET and the 98-vs-87 dispute shows the map is incomplete; the catalogue is evidence, not proof about the strap. - Two inherited claims passed through unverified, now sourced: the per-cycle export shape (cited to our importer) and the DIS characteristics (cited to BLEManager.swift:473-474). - One claim that cannot be checked here — naps/incomplete nights carrying no export SpO2 — kept but explicitly marked a contributor observation, so it is not later cited back as established. Every claim in these sections is now either checked against this tree or labelled unverified. Docs only. Co-authored-by: Dan Urbanek <abc@dnesdan.cz>
|
Updating the ask — this got smaller, and the provenance question I was blocking on is now moot. #824 landed the What's still worth landing is the
Two mechanical things: rebase on current Drop the |
What this PR does
Docs-only: expands WHOOP 5.0/MG protocol documentation with facts that already guide the decoders (and community RE) but were easy to miss when reading
PROTOCOL.md/WHOOP5_DEEP_DATA.md.docs/PROTOCOL.mdfd4bfamilysleep_state@81) — wake / still / asleep / up; explicitly not Light/SWS/REMGET_SPO2; link to deep-data docdocs/WHOOP5_DEEP_DATA.md@82promotion checklist for multi-device validation before any shippedspo2Pctcorrelate_ground_truth.pywithout posting health valuesNo code, decode, or metric behaviour changes.
Type of change
How it was tested
Interpreter.swift(skin_temp_raw/100,sleep_state@81,spo2_candidate_82@82) and the project's derived-biosignal rulesChecklist
docs/CONTRIBUTING.md(facts only; no proprietary firmware/code)Related issues