fix(ycbt): label hourly SpO2 derived from heart rate as estimated (#103) by foureight84 · Pull Request #129 · foureight84/PulseLoopAndroid · GitHub
Skip to content

fix(ycbt): label hourly SpO2 derived from heart rate as estimated (#103) - #129

Merged
foureight84 merged 1 commit into
mainfrom
fix/103-estimated-hourly-spo2
Oct 20, 2026
Merged

foureight84 merged 1 commit into
mainfrom
fix/103-estimated-hourly-spo2

Conversation

@foureight84

Copy link
Copy Markdown
Owner

Refs #103 (the "estimated" label; app-scheduled spot SpO2 stays open).

On the reporter's YCBT ring the hourly SpO2 in the 05 18 record is a fixed function of heart rate (304/304 records, no exceptions), while spot SpO2 is a real measurement. The app presented the hourly values as measured.

Detection, per ring rather than per family

EstimatedSpo2 decides from the ring's own data, not "every YCBT ring":

  1. Hourly vs spot: an SpO2 history row is hourly only if a respiration row exists at the same timestamp. Spot records log respiration 0, which is never stored, so spot readings are never labelled.
  2. Derived: each hourly row is paired with the nearest HR history row within 2 min over the last 7 days. Labelled only with ≥24 pairs, ≥12 repeated heart rates, more than one SpO2 value, and no heart rate ever paired with two SpO2 values.

The record's own HR byte isn't stored, so pairing is against 05 15 HR history. If those don't line up on hardware, nothing is labelled — the same as today.

Surfaces

Same pattern as derived stress (#67): the ESTIMATED chip and a note on the Vitals card, an amber caption on the Today tile, and the amber disclaimer card on the detail chart. Widgets aren't labelled (they carry no estimated flag for BP/glucose either). Stored data is unchanged.

Tests

./gradlew --no-daemon testDebugUnitTest: 1538 pass, including 11 new EstimatedSpo2Test cases.

Not checked on a ring.

🤖 Generated with Claude Code

On a YCBT ring (Ale-Hop2211) the hourly SpO2 in the 05 18 composite record
is a fixed function of the heart rate in the same record (304/304 records),
while spot SpO2 is a real measurement logged with HR and respiration 0. The
app presented the hourly values as measured.

EstimatedSpo2 decides it from this ring's own history, not its family:
- hourly vs spot: an SpO2 history row with a respiratory-rate history row at
  the identical timestamp came from a composite record; spot records log
  respiration 0, which is never stored, so a spot reading cannot qualify.
- derived: pair hourly rows with the nearest HR history row within 2 min over
  the last 7 days; with >= 24 pairs, >= 12 repeated heart rates, more than one
  SpO2 value and no heart rate ever paired with two SpO2 values, the ring is
  judged to compute SpO2 from heart rate.

Labelled on the Vitals card (ESTIMATED chip + note), the Today tile and the
detail chart's disclaimer card, mirroring derived stress (#67). Stored data
is unchanged.
@foureight84
foureight84 merged commit 6bf4bd9 into main Oct 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant