Surface the HRV panel the YCBT rings were already sending - #123
Open
ak710 wants to merge 1 commit into
Open
Conversation
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>
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.
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 noMeasurementKindto 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
lfHfat@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:
@24reads0x0d= 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.testLfHfRatioIsDerivedFromThePowersAndAgreesWithTheRecordsOwnBytepins both.Capability gating
New
.hrvDetailridesIS_HAS_PRESSURE(byte 22, bit 6) — the bit the vendor SDK gates the whole05 33query on, and therefore the same bit.stressand.fatiguealready ride.Deliberately not
ISHASHRV: that governs the scalar, which arrives from05 09and the06 03live stream, so a ring can have HRV and still have nothing to break it down.05 33, so it resolves to scalar-only.YCBTDriver.isSupporteddrops the history at the source too, so it's gated at decode and at render.Four existing tests pinned exact capability sets and needed
.hrvDetailadded — includingtestEveryGatedCapabilityIsDerivableFromTheBitmap, 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.mdhas a section saying so.Testing
HrvDetailCapabilityTests(8) + newYCBTHealthRecordsTestscases. Full suite green (884). Two SwiftLint complexity warnings my switch additions introduced are fixed in the same change.