[fix](trx-frontend-http): show the map and statistics the whole picture #56

Merged
sjg merged 3 commits from fix/lazy-view-replay into main 2026-08-07 10:22:52 +02:00
Owner

Two commits, both about a view not showing everything the station heard.

1. Replay what arrived before a lazy view loaded

Opening the Map or Statistics page showed only what had been decoded since the moment it was opened; a reload — landing straight on the tab, so its module loads at startup ahead of the history — was the only way to see the rest.

Reproduced with history served at startup and no live stream, so nothing can re-arrive and paper over a lost replay. Sit on the radio page, then open Map and Statistics:

opened late:  decodes 0,  grids 0,  stations 4, vessels 5, locators 0
after reload: decodes 18, grids 9,  stations 4, vessels 5, locators 9

The Statistics page counted nothing. The decode log lives inside the map module (statsDecodeLog), and app.ts records into it through window.trx.modules.map?.statsRecordDecode(...). While that module is unloaded the ?. is a silent no-op, and — unlike the map markers — nothing replayed the log when it finally attached.

The map lost its grid squares. syncMapAll() does replay on attach, but only APRS, AIS and VDES implement syncMap, which is why stations and vessels were right at 4 and 5. The FTx family and WSPR plotted locators only as decodes arrived, with no replay at all.

Fixed by holding decode-stat records in the client until the module attaches (capped at the module's own 50k log), and giving the FTx family and WSPR a syncMap that replays through a shared plot helper, oldest first. A replayed WSPR spot carries the frequency it was heard on (_rfHz) rather than one recomputed against wherever the dial has moved to since. The same repro now reads identically late and after reload.

2. Put HF APRS on the map, under its own source

The HF APRS list never plotted anything. Its plugin ships in the map plugin group and its packets carry positions, but nothing ever handed one to the map, so a station heard on 30 m appeared in the panel and nowhere else — and unlike the replay gaps above, no reload brought it back.

It goes on as a source of its own rather than as more VHF APRS: different band, different path, and lumping them together would leave no way to tell them apart or to look at one without the other. It gets its own colour, its own chip in the map's Show filter, its own entry in the source legend, and its own clear.

Station entries are keyed by source and callsign rather than callsign alone, so a station worked on both bands keeps a marker for each, while popups, search text and tracks still show the callsign as heard.

Verification

  • 59 unit tests and the full browser suite pass.
  • decode-flow.mjs pins both halves: its history fixture gains FT8 and WSPR spots, and after a first visit the statistics must count every stored decode and every grid square (0 and 0 before). An HF beacon rides alongside the VHF one — both reach the map under their own sources, the Show row offers HF APRS next to APRS, and turning that chip off takes the HF station off the map while the VHF one stays.
  • The user manual notes HF APRS as a separate map source.
Two commits, both about a view not showing everything the station heard. ## 1. Replay what arrived before a lazy view loaded Opening the Map or Statistics page showed only what had been decoded since the moment it was opened; a reload — landing straight on the tab, so its module loads at startup ahead of the history — was the only way to see the rest. Reproduced with history served at startup and no live stream, so nothing can re-arrive and paper over a lost replay. Sit on the radio page, then open Map and Statistics: ``` opened late: decodes 0, grids 0, stations 4, vessels 5, locators 0 after reload: decodes 18, grids 9, stations 4, vessels 5, locators 9 ``` **The Statistics page counted nothing.** The decode log lives inside the map module (`statsDecodeLog`), and `app.ts` records into it through `window.trx.modules.map?.statsRecordDecode(...)`. While that module is unloaded the `?.` is a silent no-op, and — unlike the map markers — nothing replayed the log when it finally attached. **The map lost its grid squares.** `syncMapAll()` does replay on attach, but only APRS, AIS and VDES implement `syncMap`, which is why stations and vessels were right at 4 and 5. The FTx family and WSPR plotted locators only as decodes arrived, with no replay at all. Fixed by holding decode-stat records in the client until the module attaches (capped at the module's own 50k log), and giving the FTx family and WSPR a `syncMap` that replays through a shared plot helper, oldest first. A replayed WSPR spot carries the frequency it was heard on (`_rfHz`) rather than one recomputed against wherever the dial has moved to since. The same repro now reads identically late and after reload. ## 2. Put HF APRS on the map, under its own source The HF APRS list never plotted anything. Its plugin ships in the map plugin group and its packets carry positions, but nothing ever handed one to the map, so a station heard on 30 m appeared in the panel and nowhere else — and unlike the replay gaps above, no reload brought it back. It goes on as **a source of its own** rather than as more VHF APRS: different band, different path, and lumping them together would leave no way to tell them apart or to look at one without the other. It gets its own colour, its own chip in the map's Show filter, its own entry in the source legend, and its own clear. Station entries are keyed by source and callsign rather than callsign alone, so a station worked on both bands keeps a marker for each, while popups, search text and tracks still show the callsign as heard. ## Verification - 59 unit tests and the full browser suite pass. - `decode-flow.mjs` pins both halves: its history fixture gains FT8 and WSPR spots, and after a *first* visit the statistics must count every stored decode and every grid square (0 and 0 before). An HF beacon rides alongside the VHF one — both reach the map under their own sources, the Show row offers HF APRS next to APRS, and turning that chip off takes the HF station off the map while the VHF one stays. - The user manual notes HF APRS as a separate map source.
sjg added 1 commit 2026-08-07 08:57:56 +02:00
[fix](trx-frontend-http): replay what arrived before a lazy view loaded
CI / lint (pull_request) Successful in 2m23s
CI / test (pull_request) Successful in 8m36s
CI / frontend (pull_request) Failing after 1m23s
CI / reuse (pull_request) Successful in 5s
ae7df31d91
Opening the Map or Statistics page showed only what had been decoded since
the moment it was opened, and a reload — landing straight on the tab, so its
module loads at startup ahead of the history — was the only way to see the
rest.  Two things were being thrown away.

The decode log the Statistics page counts lives in the map module, which is
lazy.  Recording into a module that is not loaded yet is a no-op, and unlike
the map markers nothing replayed the log when it finally arrived, so every
decode heard before the first visit was simply never counted.  Hold those
records in the client and hand them over when the module attaches.

The map's own replay covered APRS, AIS and VDES, whose plugins implement
syncMap, but not the grid squares: the FTx family and WSPR plotted locators
as decodes arrived and had no replay at all, so everything they heard before
the map loaded was lost, and the unique-grid counter with it.  Both plot
through a helper now, which their syncMap replays oldest first.  A replayed
WSPR spot carries the frequency it was heard on rather than one worked out
against wherever the dial has moved to since.

Pinned in decode-flow, whose history fixture gains FT8 and WSPR spots: after
a first visit the statistics count every stored decode and every grid square,
which before this change were 0 and 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7
Signed-off-by: Stan Grams <sjg@haxx.space>
sjg added 1 commit 2026-08-07 09:05:54 +02:00
[fix](trx-frontend-http): put HF APRS on the map, under its own source
CI / lint (pull_request) Successful in 2m22s
CI / test (pull_request) Successful in 8m39s
CI / frontend (pull_request) Failing after 1m24s
CI / reuse (pull_request) Successful in 5s
e41c13917d
The HF APRS list never plotted anything.  Its plugin ships in the map plugin
group and its packets carry positions, but nothing ever handed one to the
map, so a station heard on 30 m appeared in the panel and nowhere else — and
unlike the replay gaps around it, no reload brought it back.

Plot it, and not as more VHF APRS.  HF is a different band and a different
path, and lumping the two together would leave no way to tell them apart or
to look at one without the other, so it goes on as a source of its own: its
own colour, its own chip in the map's Show filter, its own entry in the
source legend, and its own clear.

Station entries are keyed by source and callsign rather than callsign alone,
so a station worked on both bands keeps a marker for each while the popups,
the search text and the tracks still show the callsign as heard.

decode-flow feeds an HF beacon alongside the VHF one and checks all of it:
both reach the map under their own sources, the Show row offers HF APRS next
to APRS, and turning that chip off takes the HF station off the map while the
VHF one stays.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7
Signed-off-by: Stan Grams <sjg@haxx.space>
sjg changed title from [fix](trx-frontend-http): replay what arrived before a lazy view loaded to [fix](trx-frontend-http): show the map and statistics the whole picture 2026-08-07 09:06:11 +02:00
sjg added 1 commit 2026-08-07 09:43:44 +02:00
[test](trx-frontend-http): read the statistics counters past their formatting
CI / test (pull_request) Successful in 7m52s
CI / lint (push) Successful in 2m19s
CI / lint (pull_request) Successful in 2m23s
CI / frontend (pull_request) Successful in 4m31s
CI / reuse (pull_request) Successful in 5s
CI / test (push) Successful in 7m43s
CI / frontend (push) Successful in 3m41s
CI / reuse (push) Successful in 6s
17300170cc
The new assertion parsed the decode counter with Number() on its text.  The
counters are written with toLocaleString(), so the 1220 records the history
fixture serves arrive as "1,220" and the parse gave NaN.  It passed here and
failed on CI because the count in the local repro was 18 — below the point
where grouping appears — and the runner's locale groups where mine did not.

Read the digits and ignore the separator, whichever one the locale picks.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7
Signed-off-by: Stan Grams <sjg@haxx.space>
sjg merged commit 17300170cc into main 2026-08-07 10:22:52 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sjg/trx-rs#56