Skip to content

feat(gps): track map, lap detection, and map tiles (2.13.0) - #84

Merged
SomethingNew71 merged 4 commits into
mainfrom
release/2.13.0
Aug 18, 2026
Merged

feat(gps): track map, lap detection, and map tiles (2.13.0)#84
SomethingNew71 merged 4 commits into
mainfrom
release/2.13.0

Conversation

@SomethingNew71

Copy link
Copy Markdown
Collaborator

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)

  • Extensible right-side data panel with a Track Map widget that appears when a parsed log contains recognized latitude/longitude channels
  • GPS detection via OECUA canonical IDs plus common aliases; invalid-fix sanitization; automatic lap detection with lap-focused rendering
  • Track coloring by any selected channel (Viridis/Turbo) with legend; map hover/click synchronized with the chart cursor and timeline
  • Optional Esri World Imagery / OpenStreetMap tile backgrounds (off by default) with a bounded worker pool, in-memory texture cache, and size-limited disk cache
  • MLG bitfield type IDs accepted in hex (0x10-0x12) alongside the legacy decimal interpretation

fix: address PR #83 review feedback (Copilot + maintainer rounds)

  • OSM tile fetches capped at 2 concurrent connections per the OSM tile usage policy (per-provider FetchPermits)
  • Tile/widget preferences moved onto the documented settings persistence contract; no more per-frame settings.json writes from the opacity slider
  • GPS alias fallback made independent of the registry canonical-ID result
  • Accessible (AccessKit-labeled, keyboard-activatable) data-panel icon buttons
  • Hover now scrubs the chart cursor while playback is stopped; editable color min/max range controls with Auto reset
  • One-time localized privacy notice on first tile enable; duplicate fake-GPS injector and ~7 MB of unused binary fixtures removed

feat(gps): auto-detect coordinate encodings

  • GpsCoordSpec normalizes NMEA DDMM.mmmm, milli/micro/1e-7-scaled integer degrees, and 0-360 longitude to decimal degrees before projection, lap detection, and tooltips
  • Conservative by design: valid decimal degrees are never transformed; radians deliberately undetected (ambiguous with near-equator degree tracks)

Type of Change

  • New feature (non-breaking change that adds functionality)

Related Issues

Closes #83 follow-ups.

Testing

  • cargo test --all-targets - 1,016 tests, 0 failures
  • cargo test --doc
  • cargo clippy --all-targets -- -D warnings
  • cargo fmt --all -- --check
  • git diff --check
  • End-to-end: MegaSquirt GPS fixture parses and resolves gps_latitude/gps_longitude; new integration test proves a DDM-encoded log renders with a correct decimal-degree bounding box

irudoy and others added 3 commits August 18, 2026 09:00
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.
Copilot AI lite review requested due to automatic review settings August 18, 2026 13:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 thread src/tiles.rs
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 thread docs/FORMAT_SPECIFICATIONS.md Outdated
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
@SomethingNew71
SomethingNew71 merged commit b1d3a2f into main Aug 18, 2026
4 checks passed
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.

3 participants