Skip to content

feat: replace terminal GUI with an interactive Tachikoma dashboard#108

Open
zsoerenm wants to merge 8 commits into
mainfrom
polish-tui
Open

feat: replace terminal GUI with an interactive Tachikoma dashboard#108
zsoerenm wants to merge 8 commits into
mainfrom
polish-tui

Conversation

@zsoerenm

Copy link
Copy Markdown
Member

What

Replaces the Term/UnicodePlots redraw-loop terminal GUI with an interactive Tachikoma.jl dashboard, showing what the current TUI already implements plus a map:

  • CN0 — a bar per tracked satellite (UnicodePlots barplot), RINEX-style labels, green/red by health.
  • Direction of arrival — a UnicodePlots polarplot sky plot, now labelled with readable numeric PRNs coloured by constellation (dropping the old circled-number glyph markers), with a legend.
  • Position / Velocity / Time — time (UTC), coordinates, altitude, speed, heading, and a collapsible diagnostics section (GDOP, inter-system & inter-frequency biases, pseudorange-residual RMS), toggled with d.
  • Map — the fix on an OpenStreetMap tile via UnicodeMaps.jl, rendered on a background task, with +/- zoom and hjkl pan. Falls back to coordinates + a Google Maps link when offline or before the first fix.

Design: the existing UnicodePlots plots are kept and painted into Tachikoma panels via parse_ansi; Tachikoma provides only the layout, panel frames, interactivity, footer and map. Inspired by (not copied from) the JuliaCon 2026 presentation.

Keys

q/Ctrl-C quit · d toggle diagnostics · +/- zoom map · hjkl pan map · 0 recenter.

Dependencies

  • Add Tachikoma (registered) and UnicodeMaps (git dep via [sources], from JuliaGNSS/UnicodeMaps.jl).
  • Drop Term and the now-unused REPL. Keep UnicodePlots.

⚠️ API note (possible breaking)

The non-exported GNSSReceiver.gui signature changed from gui(channel, io=stdout; construct_gui_panels=...) to gui(channel; fps=12). The exported gnss_receiver_gui entry point is unchanged. Committed as feat: (not feat!:) so it doesn't trigger a major release automatically — retag/adjust if you'd prefer a major bump.

Testing

  • Pkg.test() passes (GUI tests 36/36; the rendering tests now draw one frame into an off-screen Tachikoma buffer and assert on the text).
  • Aqua clean (stale-deps / compat / piracy / persistent-tasks).

Follow-up

  • media/output.png / docs/src/assets/gui.png still show the old UI — regenerating needs a live/replay screenshot run.

🤖 Generated with Claude Code

Rebuild the live terminal UI on Tachikoma.jl as a single-screen dashboard:
CN0 bars and a direction-of-arrival sky plot on top, a Position/Velocity/Time
block and an OpenStreetMap map below.

- Keep the UnicodePlots visuals (barplot / polarplot) and paint them into
  Tachikoma panels via parse_ansi; Tachikoma provides the layout, panel frames,
  interactivity, footer and the map.
- Sky plot now labels satellites with readable numeric PRNs coloured by
  constellation (dropping the old circled-number glyph markers).
- PVT panel shows the core fix with a collapsible diagnostics section
  (GDOP / inter-system & inter-frequency biases / pseudorange-residual RMS),
  toggled with `d`.
- Map rendered in the background via UnicodeMaps.jl with `+`/`-` zoom and `hjkl`
  pan, falling back to coordinates + a Google Maps link when offline or pre-fix.
- Deps: add Tachikoma and UnicodeMaps (git via [sources]); drop Term and the
  now-unused REPL; keep UnicodePlots.

Note: the (non-exported) `GNSSReceiver.gui` signature changed from
`gui(channel, io=stdout; construct_gui_panels=...)` to `gui(channel; fps=12)`.
The exported `gnss_receiver_gui` entry point is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.89362% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.87%. Comparing base (a80d8b2) to head (39e5c71).

Files with missing lines Patch % Lines
src/gui.jl 91.16% 16 Missing ⚠️
src/read_file.jl 66.66% 2 Missing ⚠️
src/GNSSReceiver.jl 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #108      +/-   ##
==========================================
+ Coverage   89.26%   89.87%   +0.61%     
==========================================
  Files           9        9              
  Lines         736      830      +94     
==========================================
+ Hits          657      746      +89     
- Misses         79       84       +5     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…eout

- Bump UnicodeMaps compat to "1" (UnicodeMaps is being registered at 1.0.0; the
  [sources] git entry stays until it lands in General).
- Drop the `[`GUIData`](@ref)` cross-reference in the `gui` docstring — GUIData has
  no docstring target, which failed the Documenter build; refer to it as plain code
  (matching `get_gui_data_channel`).
- Aqua: raise the persistent_tasks `tmax` to 60 s. No dependency actually spawns a
  persistent task (find_persistent_tasks_deps is empty) and the receiver only spawns
  tasks at runtime inside `gui`/`receive`; the heavier UI dependency graph just needs
  longer than the 10 s default to settle on slow (Windows) CI.
- Add `update!` interaction tests (quit, diagnostics toggle, map zoom/pan/recenter).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zsoerenm

Copy link
Copy Markdown
Member Author

Dependency status: UnicodeMaps has been bumped to 1.0.0 and submitted for registration in General — JuliaRegistries/General#162134. Since it's a new package, General AutoMerge holds it for the mandatory new-package waiting period (~3 days), so the benchmark checks here (which resolve deps from the registry via AirspeedVelocity) will stay red until UnicodeMaps 1.0 lands and propagates. Tests and docs build from source ([sources] git entry), so they're unaffected; the [sources] entry can be dropped once UnicodeMaps is in General.

Fixes pushed for the other failures: Documenter cross-ref, and Aqua persistent_tasks timeout (no dep actually spawns a persistent task — the heavier UI dep graph just needed a longer settle window on Windows CI).

zsoerenm and others added 2 commits July 24, 2026 06:45
Released AirspeedVelocity 0.6 only honours a package's [sources] deps for the
--path code path, not --url (which this workflow uses), so the benchmark fails
to resolve our unregistered UnicodeMaps [sources] dep ("has no known
versions"). Temporarily install benchpkg from the fork branch that fixes URL
[sources] resolution (MilesCranmer/AirspeedVelocity.jl#133); revert to the
registered 0.6 once that lands or once UnicodeMaps is in General.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Benchmarks.yml workflow runs as pull_request_target, so GitHub reads it
from the default branch, not the PR head — changing the install step in this PR
has no effect on its own benchmark check. It is also unnecessary: once
UnicodeMaps 1.0 is registered in General (~2 days), the default-branch
workflow's stock `AirspeedVelocity@0.6` install resolves UnicodeMaps and the
benchmark goes green with no workflow change. Reverting to avoid dead churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zsoerenm

Copy link
Copy Markdown
Member Author

Correction on the benchmark: pointing this workflow at the AirspeedVelocity fork branch (previous commit) turned out to be inert hereBenchmarks.yml runs as pull_request_target, so GitHub reads it from the default branch, not this PR's head. I've reverted that change.

The benchmark will resolve on its own once UnicodeMaps 1.0 registers in General (~2 days): the default-branch workflow's stock AirspeedVelocity@0.6 will then resolve UnicodeMaps from the registry and go green, with no workflow change needed. (The fork's [sources]-for---url fix — MilesCranmer/AirspeedVelocity.jl#133 — is still a valid upstream improvement for unregistered git deps in general; it just isn't required for this PR.)

Remove the UnicodeMaps.jl dependency and the in-terminal OpenStreetMap map for
now, so GNSSReceiver has no unregistered git dependency. The Location panel
shows the fix coordinates and a ready-to-click Google Maps link instead;
removes the map background task, map model state and map keybindings.

The rendered map view returns as a stacked follow-up PR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (minimum time) — macos-14

Reporting the minimum over all samples (robust to shared-runner contention), not the median. Ratio = a80d8b2… / 39e5c71…: >1 means the PR is faster. ✅ ≥ 5 % faster, ⚠️ ≥ 5 % slower. A blank cell means that benchmark exists on only one revision (🆕 = new on the PR, 🗑 = removed).

Time benchmarks
a80d8b2 39e5c71 a80d8b2… / 39e5c71
acquisition (1.0 s signal, 10.0 ms coherent integration)/1-ant Int16 30.1 ms 31.0 ms 0.972
acquisition (1.0 s signal, 10.0 ms coherent integration)/1-ant float 37.1 ms 40.4 ms 0.918 ⚠️
receive cold-start (1.0 s signal)/1-ant Int16 non-threaded 22.4 ms 21.2 ms 1.06 ✅
receive cold-start (1.0 s signal)/1-ant Int16 threaded 17.2 ms 18.6 ms 0.928 ⚠️
tracking + PVT (1.0 s signal)/1-ant Int16 9.06 ms 9.56 ms 0.948 ⚠️
tracking + PVT (1.0 s signal)/1-ant float 16.4 ms 16.5 ms 0.993
tracking pre-decode (1.0 s signal)/1-ant Int16 8.49 ms 8.95 ms 0.949 ⚠️
tracking pre-decode (1.0 s signal)/1-ant float 15.4 ms 15.5 ms 0.992
time_to_load 418.0 μs 229.0 μs 1.82 ✅
Memory benchmarks

Ratio = a80d8b2… / 39e5c71memory: >1 means the PR allocates less. ✅ ≥ 5 % less, ⚠️ ≥ 5 % more.

a80d8b2 39e5c71 a80d8b2… / 39e5c71
acquisition (1.0 s signal, 10.0 ms coherent integration)/1-ant Int16 7683 allocs: 1010.0 KiB 7683 allocs: 1010.0 KiB 1.0
acquisition (1.0 s signal, 10.0 ms coherent integration)/1-ant float 7683 allocs: 805.0 KiB 7683 allocs: 805.0 KiB 1.0
receive cold-start (1.0 s signal)/1-ant Int16 non-threaded 6899 allocs: 2.28 MiB 6899 allocs: 2.28 MiB 1.0
receive cold-start (1.0 s signal)/1-ant Int16 threaded 8216 allocs: 2.61 MiB 8216 allocs: 2.61 MiB 1.0
tracking + PVT (1.0 s signal)/1-ant Int16 18087 allocs: 2.02 MiB 18087 allocs: 2.02 MiB 1.0
tracking + PVT (1.0 s signal)/1-ant float 18087 allocs: 1.82 MiB 18087 allocs: 1.82 MiB 1.0
tracking pre-decode (1.0 s signal)/1-ant Int16 7261 allocs: 790.0 KiB 7261 allocs: 790.0 KiB 1.0
tracking pre-decode (1.0 s signal)/1-ant float 7261 allocs: 579.0 KiB 7261 allocs: 579.0 KiB 1.0
time_to_load 196 allocs: 13.7 KiB 196 allocs: 13.7 KiB 1.0

@github-actions

github-actions Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (minimum time) — ubuntu-latest

Reporting the minimum over all samples (robust to shared-runner contention), not the median. Ratio = a80d8b2… / 39e5c71…: >1 means the PR is faster. ✅ ≥ 5 % faster, ⚠️ ≥ 5 % slower. A blank cell means that benchmark exists on only one revision (🆕 = new on the PR, 🗑 = removed).

Time benchmarks
a80d8b2 39e5c71 a80d8b2… / 39e5c71
acquisition (1.0 s signal, 10.0 ms coherent integration)/1-ant Int16 48.1 ms 47.0 ms 1.02
acquisition (1.0 s signal, 10.0 ms coherent integration)/1-ant float 53.4 ms 52.3 ms 1.02
receive cold-start (1.0 s signal)/1-ant Int16 non-threaded 43.8 ms 43.2 ms 1.01
receive cold-start (1.0 s signal)/1-ant Int16 threaded 55.8 ms 99.5 ms 0.561 ⚠️
tracking + PVT (1.0 s signal)/1-ant Int16 15.9 ms 15.7 ms 1.02
tracking + PVT (1.0 s signal)/1-ant float 21.2 ms 21.5 ms 0.988
tracking pre-decode (1.0 s signal)/1-ant Int16 14.9 ms 14.8 ms 1.01
tracking pre-decode (1.0 s signal)/1-ant float 20.2 ms 20.1 ms 1.0
time_to_load 107.0 μs 104.0 μs 1.03
Memory benchmarks

Ratio = a80d8b2… / 39e5c71memory: >1 means the PR allocates less. ✅ ≥ 5 % less, ⚠️ ≥ 5 % more.

a80d8b2 39e5c71 a80d8b2… / 39e5c71
acquisition (1.0 s signal, 10.0 ms coherent integration)/1-ant Int16 7683 allocs: 1010.0 KiB 7683 allocs: 1010.0 KiB 1.0
acquisition (1.0 s signal, 10.0 ms coherent integration)/1-ant float 7683 allocs: 801.0 KiB 7683 allocs: 801.0 KiB 1.0
receive cold-start (1.0 s signal)/1-ant Int16 non-threaded 6921 allocs: 2.35 MiB 6921 allocs: 2.35 MiB 1.0
receive cold-start (1.0 s signal)/1-ant Int16 threaded 8258 allocs: 2.7 MiB 8258 allocs: 2.7 MiB 1.0
tracking + PVT (1.0 s signal)/1-ant Int16 18087 allocs: 2.02 MiB 18087 allocs: 2.02 MiB 1.0
tracking + PVT (1.0 s signal)/1-ant float 18087 allocs: 1.82 MiB 18087 allocs: 1.82 MiB 1.0
tracking pre-decode (1.0 s signal)/1-ant Int16 7261 allocs: 790.0 KiB 7261 allocs: 790.0 KiB 1.0
tracking pre-decode (1.0 s signal)/1-ant float 7261 allocs: 579.0 KiB 7261 allocs: 579.0 KiB 1.0
time_to_load 145 allocs: 11.0 KiB 145 allocs: 11.0 KiB 1.0

zsoerenm and others added 3 commits July 24, 2026 12:52
Add a 'Watch it live in the terminal' section to the worked example that feeds
the downloaded ION recording into GNSSReceiver.gui, and make the file-replay
snippet in the GUI page correct for that recording (pvt_approximate_year) with
a cross-link to the worked example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- read_uint8_iq_file / read_files gain a `sample_rate` keyword: when set, the
  reader paces replay to real time by sleeping one chunk of signal time between
  chunks, so a recording feeds the dashboard at the speed it was recorded
  instead of racing past and closing instantly.
- The GUI no longer quits when the data channel closes; it holds the last frame
  (header shows "stream ended (press q to quit)") until the user quits with q.
- Docs: the worked-example and GUI-page replay snippets pass `sample_rate` so
  the live dashboard is actually watchable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Show the diagnostics (DOP + biases + residual RMS) by default (press d to
  hide), and expand the DOP line to the full GDOP/PDOP/HDOP/VDOP/TDOP breakdown
  — restoring/extending what the old GUI showed.
- Make the Google Maps link a real OSC 8 hyperlink (via the style's hyperlink
  field) so it is click-to-open in supporting terminals, using a full https URL.
- Size the direction-of-arrival polarplot canvas 2:1 (columns:rows) with margin
  for the axis labels so it renders as a round circle instead of a clipped/
  stretched one.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant