Skip to content

feat(gui): add RADE status applet to sidebar panel#3430

Merged
ten9876 merged 1 commit into
aethersdr:mainfrom
NF0T:feat/rade-applet
Jun 6, 2026
Merged

feat(gui): add RADE status applet to sidebar panel#3430
ten9876 merged 1 commit into
aethersdr:mainfrom
NF0T:feat/rade-applet

Conversation

@NF0T
Copy link
Copy Markdown
Collaborator

@NF0T NF0T commented Jun 6, 2026

Summary

Adds `RadeApplet`, a new sidebar applet that mirrors the RADE/FreeDV
status block from the VFO widget as a standalone, dockable panel in the
right-side AppletPanel.

The applet shows sync state (LED: green synced, grey unsynced), mode label
("RADE" or "FreeDV"), SNR value with color coding (yellow < 5 dB, green
≥ 5 dB), EOO-decoded far-end callsign, and carrier frequency offset. All
fields are driven directly by existing RADEEngine signals — no separate
timer or polling. Content is hidden and state is reset when RADE or FreeDV
deactivates.

The FreeDV waveform path (FDVU/FDVL via the radio's EXT_WVF engine) shows
sync and SNR from the `FreeDV_SNR` meter; it does not populate callsign or
frequency offset as those are RADE-specific.

Constitution principle honored

Principle IX — Surface Only What Survives: the applet exposes only data
already computed by the RADE engine and emitted via existing signals.
No new polling, no new timers, no new state derivation.

Test plan

  • Local build passes (`cmake --build build`)
  • Activate RADE on a DIGU slice: applet shows "RADE", LED goes green
    on sync, SNR populates, EOO callsign appears when decoded
  • Deactivate RADE: applet resets to "RADE inactive"
  • Activate classic FreeDV (FDVU/FDVL): applet shows "FreeDV" label,
    LED and SNR update from meter; no callsign or offset rows shown
  • Build without `HAVE_RADE` (`-DENABLE_RADE=OFF`): applet absent,
    no compilation errors in AppletPanel or MainWindow FreeDV paths —
    verified clean on Windows (MinGW GCC 13.1.0, Qt 6.11.0)
  • Existing tests pass (CI)

Checklist

  • Commits are signed (`docs/COMMIT-SIGNING.md`)
  • No new flat-key `AppSettings` calls — applet is display-only,
    no persistence needed
  • No new meter UI — SNR is a plain `QLabel`, not a `MeterSlider`
  • Documentation updated if user-visible behavior changed
  • Security-sensitive changes reference a GHSA if applicable — N/A

@NF0T NF0T requested a review from a team as a code owner June 6, 2026 14:08
@NF0T NF0T self-assigned this Jun 6, 2026
@NF0T NF0T requested a review from a team as a code owner June 6, 2026 14:08
Copy link
Copy Markdown
Contributor

@aethersdr-agent aethersdr-agent Bot left a comment

Choose a reason for hiding this comment

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

Thanks for the clean, self-contained applet @NF0T — this is a nice surface-only addition. The signal-driven design matches Principle IX, and the HAVE_RADE guards are correctly placed (FreeDV path is independent of the RADE engine, and the new code respects that).

A few small observations, none blocking:

1. Inline stylesheets duplicate the same QLabel rule many times

src/gui/RadeApplet.cpp repeats this string at least four times:

"QLabel { color: #8090a0; font-size: 10px;"
" background: transparent; border: none; padding: 0; margin: 0; }"

…and setRadeSnr / setRadeSynced both re-apply the full sheet just to change the color. Could you pull a single kLabelBaseStyle constant (or set the base style once in the ctor and only toggle the dynamic property / color via a smaller sheet)? It would also make the contrast with the two ThemeManager::applyStyleSheet callsites less jarring — right now the file uses both styles side-by-side and it's not obvious which labels are themable and which aren't.

2. setRadeActive(true) doesn't reset m_offsetLabel/m_callsignLabel contents

setRadeActive(false) clears them, so a normal activate-after-deactivate is fine. But the first activation (with both labels constructed but never cleared) and a hypothetical activate→activate-with-different-label sequence both leave whatever was last shown. Probably benign in practice — just worth a one-line m_offsetLabel->hide(); m_callsignLabel->clear(); m_callsignLabel->hide(); in the on==true branch to keep state machine purely a function of inputs.

3. static_cast<int>(snrDb) truncates toward zero

-2.7 becomes -2, 2.7 becomes 2. Not wrong, but qRound() would match the more conventional "nearest integer dB" display you see elsewhere in the meter UI.

4. Build without HAVE_RADE test plan item is unchecked

The guards in MainWindow.cpp, AppletPanel.{h,cpp}, and the file-level #ifdef HAVE_RADE in RadeApplet.{h,cpp} look correct to me, but since CMake registers src/gui/RadeApplet.cpp unconditionally (it's added outside any if(HAVE_RADE) block), the source file will compile to an empty TU when the flag is off — which works, but you'll want to actually verify it before merge given that's the box you haven't checked yet.

Nice clean PR overall.


🤖 aethersdr-agent · cost: $16.8321 · model: claude-opus-4-7

@NF0T NF0T removed their assignment Jun 6, 2026
@NF0T NF0T force-pushed the feat/rade-applet branch from 8eba519 to 1fc8066 Compare June 6, 2026 16:46
Adds RadeApplet — a new sidebar applet that mirrors the RADE/FreeDV
decoder status block from VfoWidget (sync LED, mode label, SNR, EOO
callsign, frequency offset) as a standalone, reorderable panel tile.

Wired to the same RADEEngine signals as VfoWidget for the RADE path,
and to onFdvMeterUpdated() for the FreeDV waveform path. Applet is
hidden by default and shown when either path activates. Styled via
ThemeManager token templates consistent with the existing applet panel
aesthetic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@NF0T NF0T force-pushed the feat/rade-applet branch from 1fc8066 to 99ffed6 Compare June 6, 2026 17:44
@aethersdr-agent
Copy link
Copy Markdown
Contributor

Hi @NF0T — thanks for this contribution! 👋

The Qt Accessibility Static Analysis check (run 27069409424) is the only thing red right now. It's the lint added recently in a8bafc73 (tools/check_a11y.py, wired up by .github/workflows/a11y-check.yml). The other CI jobs (build, check-macos, check-windows, analyze (cpp)) are still in-progress and unrelated.

What's tripping the check

The new RadeApplet is a QWidget subclass whose constructor creates several QLabel children (m_inactiveLabel, m_statusLabel, m_snrLabel, m_callsignLabel, m_offsetLabel, plus the "SNR" caption) but never calls setAccessibleName() on any of them. The lint's check #3 (constructor-missing-accessible-names) is designed to catch exactly this:

creates child widgets but calls setAccessibleName() zero times — AT users cannot identify controls.

Since the widget exposes live information (sync state, SNR, callsign, frequency offset), screen-reader users would have no way to identify what each label is reading out.

Two ways to fix

Option 1 — add accessible names (recommended): Give each informational label a name in RadeApplet::RadeApplet(). Something like:

m_inactiveLabel = new QLabel(tr("RADE inactive"));
m_inactiveLabel->setAccessibleName(tr("RADE status"));

m_statusLabel = new QLabel;
m_statusLabel->setAccessibleName(tr("RADE sync indicator"));

m_snrLabel = new QLabel(QStringLiteral("---"));
m_snrLabel->setAccessibleName(tr("RADE SNR"));

m_callsignLabel->setAccessibleName(tr("Far-end callsign"));
m_offsetLabel->setAccessibleName(tr("Frequency offset"));

You may also want to follow up with QAccessibleValueChangeEvent notifications in setRadeSnr / setRadeFreqOffset / setRadeCallsign so screen readers announce the values when they change — see docs/a11y.md for the canonical patterns.

Option 2 — opt out if you'd rather defer the a11y work to a follow-up: add // a11y-check: skip-file anywhere in RadeApplet.cpp. The lint is documented as informational, but since the readouts here are real data (not decoration), Option 1 is the better fit.

Heads up on a separate compile issue I noticed while looking

RadeApplet is declared inside namespace AetherSDR { ... } in the header, but in MainWindow.cpp it's referenced bare (RadeApplet*) and forward-declared bare (class RadeApplet; in AppletPanel.h). Once the build job catches up it will likely error on this — you'll probably need either a using AetherSDR::RadeApplet; in those translation units, or to drop the namespace (the rest of src/gui/ mostly doesn't namespace its widget classes). Worth checking in your local cmake --build build with -DHAVE_RADE=ON.

Appreciate the careful PR — the applet itself is a nice mirror of the VfoWidget status. Once the names are in, this should sail through. 🛰️


🤖 aethersdr-agent · cost: $10.0815 · model: claude-opus-4-7

@ten9876 ten9876 self-assigned this Jun 6, 2026
@ten9876 ten9876 merged commit 2cdda12 into aethersdr:main Jun 6, 2026
4 of 5 checks passed
@NF0T NF0T deleted the feat/rade-applet branch June 6, 2026 19:29
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.

2 participants