Repository navigation
Support custom source fields from GPX extensions and FIT developer fields - #42
Merged
Merged
Conversation
Cyclemetry models a fixed set of telemetry attributes, so any other numeric channel a rider's device records — lean angle, g-force, core temperature, a Connect IQ developer field — is dropped at parse time no matter that the file carries it. Adding one has meant editing ~16 hardcoded sites across the parsers, the resampler, the accessors and the editor's metric lists, which is why the list has stayed short. Capture them generically instead. Each numeric GPX/TCX <extensions> leaf and each FIT record field outside the modelled profile (developer fields included) becomes a `custom:<field name>` metric backed by a series in Activity::custom, discovered per activity rather than declared in code. They flow through valid_attributes like any built-in, so they reach the editor's value, plot, meter and color-by pickers with no per-metric wiring, and render with the identity unit conversion — what the file recorded is what shows, with the element's manual suffix. Guards on what becomes a metric: names already feeding a built-in attribute are skipped so a channel can't appear twice, non-numeric and container elements are rejected, and the field count is capped (per point, and again over the union across points) so an open-ended namespace can't turn into hundreds of series and an unusable dropdown. A field missing from individual points — a sensor sampling slower than the GPS track — holds its last reading rather than dropping to zero. Resampling previously duplicated the same series list across sample_at_index and sample_between. Both collapse into SamplePos, which resolves a frame's grid position once and then reads any series from it, so the open-ended custom map rides the existing interpolate/trim path without a per-frame allocation. Addresses the generalisation half of #38; no built-in metric changes behaviour. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkMQneMjCeBXkpdCpTucTE
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This branch was successfully deployed
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.
Summary
Adds support for capturing and rendering numeric fields from GPX
<extensions>and FIT developer fields that don't map to built-in attributes. These custom fields are discovered per-activity, surfaced ascustom:<field_name>metrics, and can be used in value readouts, plots, meters, and color-by bands just like built-in metrics.Key Changes
Activity parsing and storage:
custom: BTreeMap<String, Vec<f64>>field toActivityto store per-sample custom field seriescustom: BTreeMap<String, f64>field toTrackPointto accumulate readings during parsinginsert_custom()andinsert_custom_text()helpers to validate and insert custom field readings with safeguards:MAX_CUSTOM_FIELDS(32) and per-name length atMAX_CUSTOM_NAME_LEN(48)<extensions>as custom fieldsMetric access and rendering:
custom_metric_key()andis_custom_metric()helpers to identify and extract field names fromcustom:tokenswall_clock_sample()→wall_clock_pos()+sample_at_pos()to resolve position once per frame, allowing both fixed attributes and open-ended custom series to read the same instantSamplePosenum to represent position on the sample grid (exact or interpolated) withvalue(),step(), andcourse()methods for reading different series typesget_scalar()to resolve custom metrics viacustom_metric_key()plot_data()to resolve custom metrics viacustom_metric_key()Scene resampling:
resample()via linear interpolation, consistent with other continuous attributesFrontend integration:
CUSTOM_PREFIXconstant andisCustomMetric()helper in ElementProperties.sveltecustomMetricLabel()to humanize device-written field names (split snake/kebab/camelCase, sentence-case)Testing:
Implementation Details
get_scalar()andplot_data()build_from_points()bounds what actually becomes metricshttps://claude.ai/code/session_01BkMQneMjCeBXkpdCpTucTE