feat(gps): track map, lap detection, and map tiles (2.13.0) - #84
Merged
Conversation
Copilot review: - GPS alias fallback is now independent of the registry canonical-ID result, so a refreshed spec cannot break detection of exact aliases - data-panel icon buttons register an accessible button label (AccessKit) and remain keyboard-activatable - map hover now scrubs the chart cursor/timeline while playback is stopped, matching the promised hover synchronization - track coloring gains editable min/max range controls with an Auto reset, matching the promised configurable ranges - removed the unused TrackMapState::height_frac - the opacity slider no longer writes settings.json every drag frame Maintainer review: - OSM tile fetches are capped at 2 concurrent connections per the OSM tile usage policy, via per-provider FetchPermits - tile/widget preferences follow the settings persistence contract as live UltraLogApp fields synced by eframe::App::save; the UI never saves to disk directly - removed the duplicate src/bin fake-GPS injector and its [[bin]] registration; the examples/ copy survives with the stray canlogger reference removed - dropped the three binary fixtures no test exercises (~7 MB); the diagnostic example now defaults to the remaining MegaSquirt fixture - TrackMapWidget::is_available is a pure query again - first tile enable shows a one-time privacy toast, localized in all 15 languages, noting the track location is shared with the provider CLAUDE.md documents the new modules, the tile fetching contract, and the expanded settings persistence list.
…degrees The Track Map assumed parsers deliver decimal degrees; a format storing NMEA DDMM.mmmm, scaled-integer degrees, or 0-360 longitude would have plotted garbage. GpsCoordSpec (src/laps.rs) now inspects each file's lat/lon channels once and normalizes before projection, lap detection, and tooltips: - NMEA degrees-decimal-minutes (DDMM.mmmm, sign carries hemisphere), gated on a valid minutes field (< 60) across >=95% of samples - fixed-point integer degrees at 1e-3, 1e-6, and 1e-7 scales - 0-360 longitude wrapped to signed degrees Detection is conservative: values already inside +/-90 / +/-180 are never transformed, unrecognized encodings fall through to the existing sanitize-and-drop path, and radians are deliberately not detected (numerically ambiguous with genuine near-equator degree tracks). The hover tooltip converts its raw channel reads through the cached spec so displayed coordinates match the plotted track.
There was a problem hiding this comment.
Pull request overview
This PR prepares the 2.13.0 release by adding a GPS Track Map feature (including lap detection and optional map tiles), along with supporting infrastructure (data-panel widget host, tile cache/fetching, coordinate normalization) and related hardening/docs/tests.
Changes:
- Adds a right-side data panel + widget registry, with a Track Map widget that appears when GPS channels are detected.
- Introduces lap detection + conservative GPS coordinate-encoding normalization, plus an opt-in map tile system with caching and per-provider concurrency limits.
- Updates parser behavior/docs/tests/examples/i18n/settings persistence to support the GPS feature set (including hex MLG bitfield type IDs).
Reviewed changes
Copilot reviewed 40 out of 42 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/parsers/speeduino_tests.rs | Adds a regression test for MegaSquirt GPS MLG parsing/offset assumptions. |
| tests/parsers/romraider_tests.rs | Replaces a non-assertive unit test with concrete unit extraction assertions. |
| src/ui/widgets/mod.rs | Introduces DataWidget trait and a static widget registry. |
| src/ui/mod.rs | Wires new data_panel and widgets UI modules. |
| src/ui/data_panel.rs | Implements the right-side data panel host (rail, header actions, hide/restore). |
| src/ui/chart.rs | Tightens scroll/pinch guarding so chart-only interactions don’t trigger over side panels. |
| src/tiles.rs | Adds tile providers, worker pool, validation, and disk/GPU caching for map tiles. |
| src/state.rs | Adds track map/data panel state types and a stable per-tab persistent id. |
| src/settings.rs | Persists tile/widget preferences (provider/cache limit/opacity/grayscale/hidden widgets/privacy notice). |
| src/parsers/speeduino.rs | Accepts MLG bitfield type IDs as either legacy decimal (10–12) or hex (0x10–0x12). |
| src/lib.rs | Exposes new modules: colormap, laps, tiles. |
| src/laps.rs | Adds GPS sanitization, lap detection, and coordinate-encoding detection/normalization. |
| src/ipc/handler.rs | Clears track-map color selection on “deselect all channels”. |
| src/colormap.rs | Adds Viridis/Turbo LUTs for track coloring. |
| src/app.rs | Integrates data panel rendering, settings sync, and tile-source maintenance in the app loop. |
| src/adapters/registry.rs | Adds a monotonic spec_generation for cache invalidation after background spec refresh. |
| src/adapters/mod.rs | Re-exports spec_generation. |
| scripts/build-release.sh | Forces macOS --install to always rebuild the app bundle to avoid stale installs. |
| i18n/en.yaml | Adds data panel + track map localized strings (including privacy notice). |
| i18n/de.yaml | Adds data panel + track map localized strings. |
| i18n/es.yaml | Adds data panel + track map localized strings. |
| i18n/fr.yaml | Adds data panel + track map localized strings. |
| i18n/it.yaml | Adds data panel + track map localized strings. |
| i18n/ja.yaml | Adds data panel + track map localized strings. |
| i18n/pt-BR.yaml | Adds data panel + track map localized strings. |
| i18n/pt-PT.yaml | Adds data panel + track map localized strings. |
| i18n/ru.yaml | Adds data panel + track map localized strings. |
| i18n/ur.yaml | Adds data panel + track map localized strings. |
| i18n/zh-CN.yaml | Adds data panel + track map localized strings. |
| i18n/ar.yaml | Adds data panel + track map localized strings. |
| i18n/bn.yaml | Adds data panel + track map localized strings. |
| i18n/hi.yaml | Adds data panel + track map localized strings. |
| i18n/id.yaml | Adds data panel + track map localized strings. |
| examples/inject_fake_gps_mlg.rs | Adds a utility to generate a GPS-enabled MLG fixture with synthetic coordinates. |
| examples/check_gps_lookup.rs | Adds a diagnostic utility to validate GPS canonical-ID lookup resolution. |
| examples/check_gps_in_log.rs | Adds an end-to-end diagnostic to replicate GPS channel detection in a parsed log. |
| docs/FORMAT_SPECIFICATIONS.md | Updates MLG field type documentation to show hex bitfield IDs. |
| CLAUDE.md | Updates repository documentation to include new modules/contracts and settings persistence items. |
| Cargo.toml | Enables JPEG support in image for Esri tile decoding. |
| Cargo.lock | Locks new transitive deps for JPEG decoding. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+642
to
+652
| let msg = { | ||
| // Brief lock to pop the next message; release before doing the | ||
| // network/disk work so siblings can pull in parallel. | ||
| let guard = rx.lock().expect("rx poisoned"); | ||
| guard.recv_timeout(TILE_WORKER_POLL_INTERVAL) | ||
| }; | ||
| let msg = match msg { | ||
| Ok(msg) => msg, | ||
| Err(RecvTimeoutError::Timeout) => continue, | ||
| Err(RecvTimeoutError::Disconnected) => break, | ||
| }; |
Comment on lines
+234
to
+236
| | `0x10` | U08 Bitfield | 1 | | ||
| | `0x11` | U16 Bitfield | 2 | | ||
| | `0x12` | U32 Bitfield | 4 | |
Comment on lines
+425
to
+430
| let data = read_example_binary(file_path); | ||
| let info_data_start = u32::from_be_bytes([data[12], data[13], data[14], data[15]]) as usize; | ||
| let field_count = u16::from_be_bytes([data[22], data[23]]) as usize; | ||
| let fields_end = 24 + field_count * 89; | ||
| assert!(info_data_start >= fields_end); | ||
|
|
- docs: the MLG bitfield table now shows both the hex spec IDs and the legacy decimal IDs the parser accepts, with a note explaining why - test: the MegaSquirt GPS fixture test validates the MLG magic, format version, and header/field-table lengths before indexing, and asserts the fixture actually contains a hex bitfield type ID so the test cannot pass without exercising the 0x10..=0x12 acceptance - tiles: documented why holding the rx mutex across recv_timeout is sound - the producer never touches that mutex, the waiter wakes immediately on message arrival, and the per-dequeue handoff is microseconds against hundreds of milliseconds of fetch work, so an MPMC channel dependency would buy nothing
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.
Description
Release branch for 2.13.0, centered on the GPS Track Map contributed in #83 plus the follow-up hardening from its two review rounds.
feat: add GPS track map (#83, @irudoy)
fix: address PR #83 review feedback (Copilot + maintainer rounds)
FetchPermits)settings.jsonwrites from the opacity sliderfeat(gps): auto-detect coordinate encodings
GpsCoordSpecnormalizes NMEA DDMM.mmmm, milli/micro/1e-7-scaled integer degrees, and 0-360 longitude to decimal degrees before projection, lap detection, and tooltipsType of Change
Related Issues
Closes #83 follow-ups.
Testing
cargo test --all-targets- 1,016 tests, 0 failurescargo test --doccargo clippy --all-targets -- -D warningscargo fmt --all -- --checkgit diff --checkgps_latitude/gps_longitude; new integration test proves a DDM-encoded log renders with a correct decimal-degree bounding box