fix(rade): auto-deactivate when slice mode changes externally#2747
Conversation
When RADE is active, activateRADE() sets audio_mute=1 on the slice and expects deactivateRADE() to restore it. The VfoWidget and RxApplet mode combos emit radeActivated(false) when the user switches mode via the UI, but external mode changes — TCI client sending mode=USB, a profile load, a remote SmartSDR client on the same radio — bypass both combos entirely. Previously this left audio_mute=1 stranded on the slice, silently killing RX audio until manually unmuted. Connect SliceModel::modeChanged on the RADE slice in activateRADE() to a new onRadeSliceModeChanged() slot. If the mode leaves the DIGU/DIGL family that RADE requires, deactivateRADE() is called immediately so the mute is always restored regardless of which path drove the mode change. Disconnect in deactivateRADE() before any state teardown to prevent re-entrant calls. Qt::UniqueConnection guards against duplicate activation (e.g. VfoWidget and RxApplet both emitting radeActivated simultaneously) connecting the slot twice. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Thanks @NF0T — clean, well-reasoned fix that pairs nicely with #2745.
Verified the flow against the surrounding code:
s->setMode("DIGL"/"DIGU")at MainWindow.cpp:13498 is called before the newconnectat :13509, and the new slot whitelists DIGU/DIGL, so the radio-echomodeChanged("DIGL")arriving on the now-connected slot is a no-op. Matches your design note.- In
deactivateRADE()thedisconnectruns inside theif (m_radeSliceId >= 0)block beforesetAudioMute(m_radePrevMute). Qt allows disconnecting a signal from within its own slot, so the recursive call path (modeChanged→onRadeSliceModeChanged→deactivateRADE→disconnect) is safe — no furthermodeChangedinvocations queue up, andsetAudioMutedoesn't loop back to mode. SliceModel::modeChanged(const QString&)signature matches the slot (src/models/SliceModel.h:189).Qt::UniqueConnectionplus the existing duplicate-activation guard at activateRADE:13472 covers the VfoWidget+RxApplet race.
One small note (not blocking): the new connect happens before the if (!ok) return; at :13523 — if RADEEngine::start() fails, the modeChanged connection persists, mirroring the pre-existing leak of m_radeSliceId/audio_mute=1 on that path. Arguably benign here (an external mode switch will at least clean up the stranded mute), but worth noting if you ever harden that error path.
LGTM.
|
Claude here — landed at 22:24 UTC, four minutes after #2745. Thanks Ryan! 🎉 Pairing #2745 (UI-level deactivate fix) with #2747 (model-level deactivate fix) closes the entire RADE-mute-stranded bug class — any future mode-change code path automatically deactivates correctly because the trigger is now on The design notes on Two community contributions from you today (#2745 + #2747) and both nailed it. Stellar work. 73, Jeremy KK7GWY & Claude (AI dev partner) |
Follow-on to #2745.
Why
activateRADE()setsaudio_mute=1on the RADE slice, expectingdeactivateRADE()to restore it when the user switches away from RADE. The VfoWidget and RxApplet mode combos handle this for UI-driven mode changes. But three paths bypass both combos entirely:mode USB(e.g. WSJT-X or a logging program switching modes via CAT)In all three cases,
modeChangedfires on theSliceModelbut neither combo emitsradeActivated(false).deactivateRADE()never runs,audio_mute=1is stranded on the slice, and the radio's band-stack saves that muted state. The result is the same silent RX-audio loss as #2734, triggered by a different path.What changes
src/gui/MainWindow.cpp/MainWindow.h— connectSliceModel::modeChangedon the RADE slice when RADE activates; deactivate automatically if the mode leaves the DIGU/DIGL family:Net diff: +21 / −1 across two files.
Design notes
Why not guard against DIGU→DIGL transitions?
activateRADE()sets the mode toDIGLorDIGUbased on band convention. If the mode flips between the two (e.g. a band change that crosses the 10 MHz boundary), RADE deactivates cleanly and the user re-enables it. Keeping the same-family pass-through would add complexity for a case where re-activation is the right behavior anyway.Re-entrancy: The disconnect in
deactivateRADE()runs beforem_radeSliceIdis cleared and before any furthersetAudioMuteor engine teardown. IfonRadeSliceModeChangedsomehow fires during teardown, the signal is already disconnected at the point of re-entry.Qt::UniqueConnectionadditionally prevents double-connection ifactivateRADE()is called twice in quick succession (e.g. VfoWidget and RxApplet both emittingradeActivated(true)simultaneously — the duplicate-activation guard at the top ofactivateRADE()normally prevents this, butUniqueConnectionadds a second layer).modeChangedduring initialactivateRADE():activateRADE()callss->setMode("DIGL"/"DIGU")before connecting the signal, so the radio-echomodeChanged("DIGL")that arrives later fires on a connected slot — but the checkmode != "DIGU" && mode != "DIGL"correctly passes through, and no deactivation occurs.Verification
slice set 0 mode=USBvia TCI/CAT console → RADE deactivates, slice unmutes ✓🤖 Generated with Claude Code