Skip to content

Surface the HRV panel the YCBT rings were already sending - #123

Open
ak710 wants to merge 1 commit into
saksham2001:mainfrom
ak710:feat/autonomic-hrv-metrics
Open

Surface the HRV panel the YCBT rings were already sending#123
ak710 wants to merge 1 commit into
saksham2001:mainfrom
ak710:feat/autonomic-hrv-metrics

Conversation

@ak710

@ak710 ak710 commented Aug 3, 2026

Copy link
Copy Markdown

Closes #114

Merge order: 4 of 9. Branches off main, independent of #120#122.

What

The body-data record (05 33) carries SDNN, pNN50, RMSSD and an LF/HF pair alongside the HRV scalar. The offsets were already documented in the decoder's doc comment; there was just no MeasurementKind to put them in, so they were walked past.

Neither Oura nor Ultrahuman surfaces this breakdown, so on a YCBT ring this is a straight win over a $349 device.

The LF/HF decision

The record has its own lfHf at @24 — I don't use it. It is one byte standing in for a ratio whose real range is ~0.5–3, so it carries an implicit scale the SDK never states. The ratio is recomputed from the LF and HF powers instead, where whatever scale those two share cancels in the quotient.

The captured hardware record in the test suite settles it: @24 reads 0x0d = 1.3 at a ÷10 scale, against a derived 1200 ÷ 900 = 1.33. They agree — which also corroborates every offset in the panel, since a misread would land these in the thousands rather than in ordinary adult resting ranges. testLfHfRatioIsDerivedFromThePowersAndAgreesWithTheRecordsOwnByte pins both.

Capability gating

New .hrvDetail rides IS_HAS_PRESSURE (byte 22, bit 6) — the bit the vendor SDK gates the whole 05 33 query on, and therefore the same bit .stress and .fatigue already ride.

Deliberately not ISHASHRV: that governs the scalar, which arrives from 05 09 and the 06 03 live stream, so a ring can have HRV and still have nothing to break it down.

  • jring, Colmi QRing and LuckRing have no equivalent record and never claim it.
  • The real R99 leaves the bit clear and NAKs 05 33, so it resolves to scalar-only.
  • YCBTDriver.isSupported drops the history at the source too, so it's gated at decode and at render.

Four existing tests pinned exact capability sets and needed .hrvDetail added — including testEveryGatedCapabilityIsDerivableFromTheBitmap, the invariant that forbids gates no bit can satisfy.

Placement

The panel's kinds have no MetricKey, which makes a Today or Vitals card structurally impossible rather than merely absent today. It lives at Vitals → HRV → HRV detail.

No zone colouring: RMSSD alone spans an order of magnitude across healthy adults, so a green/amber/red band would be inventing a threshold.

What is deliberately still dropped

Load index and sympathetic tone (proprietary composites, no stated scale or definition), and body fat / uric acid / ketones / lipids from the neighbouring records — no optical ring can measure those at all. docs/project/hrv-metrics.md has a section saying so.

Testing

HrvDetailCapabilityTests (8) + new YCBTHealthRecordsTests cases. Full suite green (884). Two SwiftLint complexity warnings my switch additions introduced are fixed in the same change.

⚠️ I have a Colmi QRing, so I can't see this on my own hardware — the offsets are corroborated by the captured record in the suite, but a YCBT owner confirming against the SmartHealth app would be worth having.

The body-data record (05 33) carries SDNN, pNN50, RMSSD and an LF/HF pair
alongside the HRV scalar. YCBTHealthRecords walked straight past all of them —
the offsets were documented in the doc comment, but there was no MeasurementKind
to put them in, so they were decoded past and dropped. Neither Oura nor
Ultrahuman surfaces this breakdown, so on a YCBT ring it is a straight win.

LF/HF is computed rather than read. The record has its own lfHf at @24, but it
is one byte standing in for a ratio whose real range is ~0.5-3, so it carries an
implicit scale the SDK never states. The ratio is recomputed from the LF and HF
powers instead, where whatever scale those two share cancels in the quotient.
The captured hardware record settles it: @24 reads 0x0d = 1.3 at a /10 scale
against a derived 1200/900 = 1.33. They agree — which also corroborates every
offset in the panel, since a misread would land these in the thousands rather
than in ordinary adult resting ranges.

Gated on a new .hrvDetail capability riding IS_HAS_PRESSURE (byte 22 bit 6) —
the bit the vendor SDK gates the whole 05 33 query on, and therefore the same
bit stress and fatigue already ride. Not ISHASHRV: that governs the scalar,
which arrives from a different record and the live stream, so a ring can have
HRV and still have nothing to break it down. The jring, Colmi QRing and LuckRing
families have no equivalent record and never claim it; the real R99 leaves the
bit clear and NAKs 05 33, so it resolves to the scalar and no panel. YCBTDriver
drops the history at the source for any ring that doesn't declare it.

Kept off the dashboard deliberately: the panel's kinds have no MetricKey, which
makes a Today or Vitals card structurally impossible rather than merely absent.
It lives at Vitals > HRV > HRV detail — two taps, since these are read
occasionally to understand a trend, not glanced at. No zone colouring either:
RMSSD alone spans an order of magnitude across healthy adults, so painting a
green/amber/red band would be inventing a threshold.

Load index and sympathetic tone stay on the floor — proprietary composites with
no stated scale or definition — as do body fat, uric acid, ketones and blood
lipids from the neighbouring records, which no optical ring can measure at all.
docs/project/hrv-metrics.md documents every offset, formula and range, and says
plainly what is refused and why.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ak710
ak710 requested a review from saksham2001 as a code owner August 3, 2026 06:24
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.

[Feature]: The YCBT HRV panel (SDNN/RMSSD/pNN50/LF-HF) is decoded past and dropped

1 participant