Decode GET_DATA_RANGE pagesBehind for sync diagnostics (#689) - #694
Merged
Conversation
Adds DataRange.pagesBehind (Swift + Kotlin byte-parity twins) — the ring-buffer page backlog the WHOOP app computes from three u32s in the GET_DATA_RANGE command-response inner payload: write page W=V(2), read pointer U=V(3), ring capacity T=V(5) at frame offsets cmdOff+10/14/22, backlog W<U ? W+(T-U) : W-U. Read u32 LE (matching the frame's other words); guarded on length + a capacity sanity ceiling so a short/garbage frame returns null. Logged as 'Strap backlog pages behind: N' at the existing GET_DATA_RANGE response site — DIAGNOSTIC ONLY: the layout is RE'd from the app and NOT yet confirmed against real 4.0/5-MG captures, so it never gates sync or backfill (existing oldest/newest + backfill policy untouched). No new BLE command. Unit tests both platforms: normal, wraparound, too-short, implausible. Facts RE'd, reimplemented in NOOP's own code (ATTRIBUTION.md); documented in PROTOCOL.md. Reported-by: tigercraft4 (#689). Verified: swift test (WhoopProtocol 307) + compileFullDebugKotlin + DataRangeScanTest. iOS log-site via app-build. Author: ryanbr <mp3geek@gmail.com>
DX23876
pushed a commit
to DX23876/noop
that referenced
this pull request
Jul 22, 2026
…yanbr#694) DataRange.pagesBehind (Swift+Kotlin byte-parity twins) decodes the ring-buffer page backlog from three u32s in the GET_DATA_RANGE response (W=V(2)/U=V(3)/T=V(5) at cmdOff+10/14/22; W<U?W+(T-U):W-U), logged as 'Strap backlog pages behind: N' at the existing response site. Diagnostic ONLY — RE'd/unconfirmed layout (LE + offset assumptions flagged), guarded on length + a capacity ceiling, never gates sync/backfill; no new BLE command. Unit-tested both platforms (normal/wraparound/too-short/implausible). Log emission follows each platform's existing GET_DATA_RANGE handler scope (Swift 4.0-only, Android both) — 5/MG-on-Swift gap tracked as ryanbr#695. Facts reimplemented in NOOP's own code (ATTRIBUTION.md). Reported-by: tigercraft4.
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.
Fixes #689. Adds tigercraft4's ring-buffer page-backlog diagnostic, extending the existing
DataRangehelpers.What it does
DataRange.pagesBehind(Swift + Kotlin byte-parity twins) decodes the backlog the WHOOP app computes from three u32s in theGET_DATA_RANGEcommand-response inner payload: write pageW=V(2), read pointerU=V(3), ring capacityT=V(5)(V(i)= u32 @ inneri·4+1= framecmdOff+10/14/22), backlogW<U ? W+(T−U) : W−U. Logged asStrap backlog pages behind: Nat the existingGET_DATA_RANGEresponse site (both platforms) — no new BLE command; the reply is already received + dumped (#451).Scope / safety
Diagnostic only. The layout is RE'd from the app and not yet confirmed against real 4.0/5-MG captures, so it never gates sync or backfill — existing oldest/newest timestamp behavior and backfill policy are untouched. Guarded on frame length + a capacity sanity ceiling; returns null on a short/garbage frame so it can't log a nonsense number.
One unconfirmed assumption, flagged: u32 read little-endian (matching the frame's own unix words; the app's ByteBuffer default is big-endian). A fixture will settle it — a flip is one line. This is exactly why it stays log-only.
Provenance
Facts (offsets, formula) RE'd from the WHOOP app and reimplemented in NOOP's own code — facts, not copied expression (
ATTRIBUTION.md). Documented indocs/PROTOCOL.md.Verification
swift test(WhoopProtocol, 307 pass incl. 5 new pagesBehind) + KotlinDataRangeScanTestgreen — cases: normal (W>U), wraparound (W<U), 5/MG cmdOff=10, too-short→nil, implausible→nil (capacity 0 / timestamp / U≥T).compileFullDebugKotlin✓. iOS log-site (BLEManager, app-target) via app-build — dispatched.Reported-by: tigercraft4
Log-site scope (honest note)
The
pagesBehinddecode is full byte-parity on both platforms. The log emission rides the existingGET_DATA_RANGEresponse handler, whose family scope already differs: Android detects the reply via a computedcmdOff(6 on 4.0, 10 on 5/MG) so it logs on both families; Swift keys onframe[6](the 4.0 offset) so its wholeGET_DATA_RANGEblock — newest/oldest/span/clock-drift and now pagesBehind — runs on 4.0 only. So on a 5/MG strap Android logs pagesBehind and iOS/macOS does not. This is a pre-existing Swift limitation this PR inherits, not one it introduces; closing it (a Swift 5/MGGET_DATA_RANGEhandler) also moves the newest-record sync path and needs 5/MG hardware to validate, so it is tracked separately (#695).