feat(gui): FreeDV Reporter window with live station table, band/freq filter, and track-slice mode#3439
Conversation
|
Hi @NF0T — thanks for the FreeDV Reporter dialog, that's a great addition. 👋 I dug into the failing Qt Accessibility Static Analysis check on What I checkedRan Exit 0, zero findings. The "Comment with accessibility summary" sub-step on the failed run is also marked skipped (its Why the step still failedBug is in COUNT=$(grep -c "^::warning" /tmp/a11y_output.txt || echo "0")
echo "findings=$COUNT" >> $GITHUB_OUTPUTWhen there are zero warnings, …to A one-liner fix would be Bottom line for this PR
Nothing for you to change here. Sit tight on the other checks. 73 and thanks for contributing! 🤖 aethersdr-agent · cost: $22.2425 · model: claude-opus-4-7 |
…le. Principle V.
Non-modal resizable dialog under View → FreeDV Reporter showing all active
qso.freedv.org stations in a 13-column sortable table (Callsign, Locator,
km, Hdg, Version, MHz, TX Mode, Status, Message, Last TX, RX Call, SNR,
Last Update). Band/frequency filter with Track mode follows the active VFO
slice. Per-session sort on any column.
Row highlight palette matches freedv-gui reference:
- Orange (#fc4500): live TX — set/cleared exclusively by tx_report
- Teal (#379baf): RX receiving — 5 s timeout after rx_reports stop;
cleared immediately when EOO callsign is decoded
- Purple (#e58be5): new station arrival or message update — 5 s timeout
- 250 ms QTimer drives expiry; black foreground on all highlights
State updates are decoupled from the SpotHub spot-emission gates so the
table reflects raw WebSocket protocol state, not the filtered spot feed.
Protocol correctness (Principle VIII — all verified against freedv-gui source):
- TX state owned exclusively by tx_report; freq_change never touches it
- bulk_update processes transmitting=false entries — no station stuck TX
- rx_only status immutable once set by new_connection
- Model reset on WebSocket disconnect prevents ghost rows on reconnect
- Dialog seeded from FreeDvClient cache on first open (lazy-construct safe)
- GPS-aware my-grid resolved at open time, not read from AppSettings directly
New files: src/core/MaidenheadLocator.h, src/gui/FreeDvReporterModel.h/.cpp,
src/gui/FreeDvReporterDialog.h/.cpp
Modified: src/core/FreeDvClient.h/.cpp, src/gui/MainWindow.h/.cpp,
CMakeLists.txt
Principle V (config under AppSettings["FreeDvReporter"]).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12cb723 to
5e0e0d3
Compare
|
Rebased your branch onto current main (`5e0e0d32`) to pick up #3442's a11y workflow fix. Your code didn't trigger any a11y findings — the lint scanned 6 files and reported 0 findings, then the workflow step crashed writing the result to `$GITHUB_OUTPUT`. What the failing run actually showed``` Root causeThe pre-#3442 workflow had: When `grep -c` finds zero matches it exits 1 and prints `0` to stdout, so `|| echo "0"` appended a second `0`, making `COUNT=$'0\\n0'`. The next line `echo "findings=$COUNT" >> $GITHUB_OUTPUT` wrote a two-line value that the GITHUB_OUTPUT parser rejected with `Invalid format '0'` — and the step failed even though the lint had already succeeded. #3442 fixed this by changing `|| echo "0"` to `|| true`, so `COUNT` ends up as just `0` on the zero-findings case. Timing: your CI run was 2026-06-06 19:41 UTC, #3442 merged 2026-06-06 21:44 UTC — your run hit the workflow file from your branch (workflows run from PR-branch state on `pull_request` events), which was pre-fix. What I didSame maintainer-fix-up pattern as #3279/#3286/#3289/#3381/#3391/#3397/#3398/#3417 — rebased your single commit onto current main, no content changes. Verified locally on Arch Linux x86: full `AetherSDR` build clean (634/635, only the pre-existing unrelated `macDaxDriverInstalled` warning). CI will rerun against the new HEAD. Happy to revert any piece you'd rather take a different cut at. Principle XI. |
Two related fixes to TitleBar's new PanLock accessors so all four platforms compile again: 1. signals: vs. public: — MOC parses anything inside a `signals:` block as a signal declaration, so the inline `bool isPanFollowChecked() const` and `void setPanFollowChecked(bool)` were rejected at moc time with "Not a signal declaration" (TitleBar.h:56). Both methods are accessors, not signals — they belong in `public:`. 2. Out-of-line over inline. Even after the section move, the inline bodies referenced `m_panFollowBtn->isChecked()` and `QSignalBlocker`, which need the full QPushButton type. The header only forward-declares QPushButton, so inline bodies couldn't compile in callers either (the moc error masked this until now). Moving the definitions to TitleBar.cpp keeps the header light and matches the style of neighbours like `isSystemMoveAreaAt`. Build verified locally on Arch Linux x86 — 632/632 clean (only the pre-existing unrelated macDaxDriverInstalled warning). Same maintainer fix-up pattern as aethersdr#3279/aethersdr#3286/aethersdr#3289/aethersdr#3381/aethersdr#3398/aethersdr#3417/aethersdr#3439/aethersdr#3441. Principle XI.
Two related fixes to TitleBar's new PanLock accessors so all four platforms compile again: 1. signals: vs. public: — MOC parses anything inside a `signals:` block as a signal declaration, so the inline `bool isPanFollowChecked() const` and `void setPanFollowChecked(bool)` were rejected at moc time with "Not a signal declaration" (TitleBar.h:56). Both methods are accessors, not signals — they belong in `public:`. 2. Out-of-line over inline. Even after the section move, the inline bodies referenced `m_panFollowBtn->isChecked()` and `QSignalBlocker`, which need the full QPushButton type. The header only forward-declares QPushButton, so inline bodies couldn't compile in callers either (the moc error masked this until now). Moving the definitions to TitleBar.cpp keeps the header light and matches the style of neighbours like `isSystemMoveAreaAt`. Build verified locally on Arch Linux x86 — 632/632 clean (only the pre-existing unrelated macDaxDriverInstalled warning). Same maintainer fix-up pattern as aethersdr#3279/aethersdr#3286/aethersdr#3289/aethersdr#3381/aethersdr#3398/aethersdr#3417/aethersdr#3439/aethersdr#3441. Principle XI.
Summary
Adds a FreeDV Reporter window (View menu → FreeDV Reporter) that shows the live station table from
qso.freedv.org— the same data the existing spot source feeds into the DX cluster overlay, now surfaced in a dedicated resizable window.FreeDvReporterDialog(src/gui/) —PersistentDialogsubclass with:QTableViewdriven byFreeDvReporterModel(13 columns: callsign, locator, distance km, bearing °, software version, frequency, mode, status, station message, last TX time, last heard callsign, SNR, last update)AppSettingsnested-JSON (Principle V)FramelessWindowTitleBar+FramelessResizer::install)FreeDvReporterModel(src/gui/) —QAbstractTableModelwith:MaidenheadLocator.hinfo.statusdirectlym_highlightTimerticks at 1 s to expire stale highlights without full-model resetsFreeDvClientextensions (src/core/):StationInfostruct promoted from private to public — consumed directly by the model and dialogstationUpdated(sid, info),stationRemoved(sid),stationsCleared()m_inBulkUpdateflag prevents spot emission duringbulk_updatereplay (spots only on live TX start)onMessageUpdate()handler formessage_updateSocket.IO events (was silently ignored)stationUpdatedemitted from all event handlers so the model stays current without pollingMainWindowwiring: dialog constructed lazily on first open, wired toFreeDvClientsignals and active-slice changes.Constitution principle honored
Principle V — New Configuration Uses Nested JSON Per Feature, Not Flat AppSettings. All dialog settings (band filter index, filter mode, track-slice toggle, window geometry) are stored under a single
AppSettings["FreeDvReporterDialog"]JSON blob.Test plan
HAVE_WEBSOCKETSundefined — all new GUI code is gated#ifdef HAVE_WEBSOCKETSqso.freedv.orgChecklist
docs/COMMIT-SIGNING.md)AppSettingscalls — nested-JSON underFreeDvReporterDialog(Principle V)MeterSmoother— N/A