Repository navigation
feat(spo2): nightly 5/MG SpO2 candidate as the mean of per-window medians (experimental) - #2825
LeonardSEO wants to merge 1 commit into
Conversation
…ians Byte 82 is measured in 30 s windows every 1,200 s during sleep (LeonardSEO/wearable5.0 FINDINGS §13). Each window with at least 5 in-band seconds yields its median; the night is the mean of those medians. Display-only and still default-off; not claimed as an improvement. Refs ryanbr#103
|
Independent data from an MG (fw 50.40.1.0) for the PR's premise: windows of 30 s every 1,200 s in sleep, a window needs 5 in-band seconds, in-band = 70 to 100. Labels as in #891. OBSERVED: R18 byte 82, 24 Sep to 24 Oct, all pulls merged so each window is counted once: 402 windows on 26 nights. 401 hold exactly 30 non-zero seconds (one holds 29). Consecutive windows start 1,200 s apart (1,199 to 1,200 within every night; the larger gaps are missing windows). Every non-zero second but 3 is in the strap's sleep state (12,056 sleep, 3 up, none awake or still). OBSERVED: with the PR's rule, 375 windows (93%) have 5 or more in-band seconds, 9 have 1 to 4 and 18 have none, so the rule drops 27 of 402 (6.7%). Window medians run from 90 to 100, and the nightly median of the window medians from 94 to 98. OBSERVED: 1,909 non-zero seconds (16% of all non-zero seconds) lie outside the band. In order of frequency, the 14 most frequent: 8, 32, 168, 160, 2, 128, 40, 136, 16, 3, 24, 152, 144, 4. All 14 are sums of the bits 0x01, 0x02, 0x04, 0x08, 0x10, 0x20 and 0x80, and none has 0x40. INFERRED: they are status flags, not readings. No reference oximeter, so no calibration claim. Checked: all counts from the full-corpus script output (402, 401, 375, 9, 18, 1,909, 12,056, 3) and recomputed on the 24 to 26 Oct records alone: 38 windows, every one exactly 30 non-zero seconds, 1,137 sleep seconds and 3 up, 36 windows with 5 or more in-band seconds, window medians 92 to 97, no code with 0x40. The 14 codes sum to 1,909 and the in-band rule is the PR's (70 to 100 inclusive, gap 2 s, 5 seconds). |
Experimental, like the candidate itself. The method follows one captured night; I do not expect it to be accepted as an improvement, and it is not described as one.
What this PR does
Changes how the nightly 5/MG SpO2 candidate (
spo2_candidate_82) is aggregated. Display-only, behind the existing default-off toggle; it never writesspo2Pctand feeds nothing.samplesis now the in-band seconds of the windows that counted.Why: in an overnight iOS capture, byte 82 is 0 in every record except 15 windows of exactly 30 s starting exactly every 1,200 s, all during sleep. Several windows hold only 1-4 valid seconds (one a lone 78 % among status codes). A per-second mean weighs those thin windows like full ones, which may explain part of the split cross-device evidence noted at the candidate. That is a hypothesis until a captured night is compared with the official app's own SpO2.
Source: LeonardSEO/wearable5.0, FINDINGS §13 (
spo2_windows).Validation: none beyond that one night (7 windows of 93-99 %). No held-out number, so per
docs/VALIDATION_PROTOCOL.mdthis is not claimed as an improvement.Type of change
How it was tested
Spo2CandidateNightlyTests/Spo2CandidateNightlyTest, rewritten with the same fixtures on both platforms: window median, mean of medians, status codes and thin windows ignored, a gap splitting windows, session bounds, band edges.swift testinPackages/StrandAnalytics: 2141 tests pass../gradlew testFullDebugUnitTest: 6686 tests, all pass apart from the 2RecoveryDriversTesthalf-tie cases that fail the same way on unmodifiedmain.Tools/i18n_audit.py --ci,Tools/doc_comment_lint.py,docs/protocol-exampleschecks andTools/parity_ledger.py --refresh-derived(derived snapshots included): pass.Toolsunit tests, the parity acceptance suite and the R-R contract: pass apart from the 4test_check_source_referencescases (/varvs/private/vartemp path on macOS) that fail the same way on unmodifiedmain.Strand) builds with this change on my development branch;StrandTestsand the iOS target were not run locally for this branch.app-build.ymlcovers both.Checklist
android/(apart from the 2RecoveryDriversTesthalf-tie cases that fail the same way on unmodifiedmain)StrandDesign/Palette/Metricstokensdocs/CONTRIBUTING.mdStrand.xcodeproj/) or any secrets/keystoresRelated issues
Refs #103
🤖 Generated with Claude Code