Repository navigation
Add heart rate to a workout file: merge strap HR into an HR-less GPX for re-upload (#2679 option 1) - #2817
Closed
andre-fu wants to merge 1 commit into
Closed
Add heart rate to a workout file: merge strap HR into an HR-less GPX for re-upload (#2679 option 1)#2817andre-fu wants to merge 1 commit into
andre-fu wants to merge 1 commit into
Conversation
A run or ride recorded in another app without an HR sensor exports as a GPX with position and times but no heart rate. The new Data Sources card takes that file, splices the strap's stored per-second HR into each timed trackpoint as Garmin TrackPointExtension (gpxtpx:hr) via text surgery that leaves every other byte of the document untouched, and hands the merged file back through the share sheet / save panel. Uploading it to the originating service shows the route AND heart rate / zones there. Fully offline, file-in/file-out: the store is only read (through the hrSamples union funnel), nothing is imported or persisted, and every refusal reports its numbers (no silent unchanged re-export). This is the ryanbr#2679 option-1 direction: automating the capability without any third-party credential or network scope change. - GpxHrInjector (Packages/StrandImport): pure, Linux-safe, 14 tests incl. round-trip through ActivityFileImporter and byte-preservation. - GpxHrInject (android com.noop.ingest): byte-identical Kotlin twin, pinned by oracle (Swift stdout over shared fixtures pasted as expected literals). The Android Compose entry point is deliberately not in this change and is left to a follow-up; the byte-contract layer ships on both platforms together. - DataSourcesView: the merge card, gated file picking, per-failure copy; 12 new catalog keys translated for all nine shipped locales. Refs ryanbr#2679 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Author
|
Withdrawing this for now — needs more local testing first. Will resubmit when it's ready. |
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.
What this adds
A run or ride recorded in another app without an HR sensor exports as a GPX carrying position and times but no heart rate. This adds a Data Sources card — Add heart rate to a workout file — that takes such a file, splices the strap's stored per-second HR into each timed trackpoint as Garmin TrackPointExtension (
gpxtpx:hr), and hands the merged file straight back out through the share sheet / save panel. Uploading it to the originating service (Strava included) shows the route and heart rate / zones there.This is the #2679 option-1 direction: automating the capability that already exists, with no third-party credential, no network, and no scope change. Refs #2679.
How it works
GpxHrInjector(Packages/StrandImport, pure, Linux-safe): deliberate TEXT SURGERY on the original document, not a parse-and-reserialize — position, elevation, times and everything else survive byte-for-byte; the only changes are the inserted extension blocks and (when missing) thexmlns:gpxtpxdeclaration on the root tag. HR matches by nearest stored sample within 5 s (ties take the earlier sample).repo.hrSamples(from:to:)— the existing union/dedup funnel — and nothing is imported or persisted.Cross-platform parity
GpxHrInject(androidcom.noop.ingest) is the byte-identical Kotlin twin, pinned by oracle per AGENTS.md: the Swift implementation's stdout over shared fixtures is pasted verbatim as the Kotlin test's expected literals (GpxHrInjectTest), covering the Strava-shaped file, the existing-<extensions>case, and the tolerance boundary, plus the same refusal cases.Verification
Packages/StrandImport: full suite passes locally (263 tests incl. the 14 new — round-trip throughActivityFileImporter, byte-preservation by stripping the inserted blocks, tolerance/tie rules, every refusal).GpxHrInjectTest(oracle + behaviour pins) passes undertestFullDebugUnitTest.xcodebuild -scheme Strand … build) andStrandTestsrun locally; iOS compiles via this PR'sapp-build.ymlrun (local Xcode here has no iOS 26 SDK).Tools/i18n_audit.py --ci maingreen: the 12 new catalog keys are translated for all nine shipped locales.Tools/doc_comment_lint.pygreen.🤖 Generated with Claude Code