Tracking follow-up from PR #3221 (feat(rade): unified TX pipeline with EOO frame transmission and callsign encoding).
The gap
The PttOffHook mechanism added in #3221 intercepts requestPttOff() so RADE can emit its End-of-Over frame before the radio drops the carrier. The hook is checked at the first setMox(false) call in TransmitModel::requestPttOff() at line 879:
if (!tone || !tone->isEnabled() || !isPhoneModeForQuindar()
|| tone->phase() == ClientQuindarTone::Phase::Idle
|| m_quindarOutroInFlight) {
cancelPendingQuindarOff();
if (m_pttOffHook) {
m_pttOffHook();
return;
}
setMox(false);
return;
}
But the second setMox(false) — inside the Quindar-outro timer callback at line 902 — does not check the hook:
connect(m_pendingMoxOffTimer, &QTimer::timeout, this, [this]() {
m_pendingMoxOffTimer = nullptr;
m_quindarOutroInFlight = false;
emit quindarActiveChanged(false);
setMox(false); // ← bypasses PttOffHook
});
Why it's safe today
isPhoneModeForQuindar() returns true only for SSB/AM/FM, and activateRADE() sets the slice to RADE's digital mode — so the Quindar outro path can't fire while RADE is active. The hook gap is latent, not exploitable today.
Why it's worth tracking
If we ever:
- Enable Quindar tones in a digital-voice mode (RADE, FreeDV, etc.), or
- Add another EOO-style protocol that needs PTT held open past
requestPttOff(),
…the Quindar outro path will silently drop the carrier without firing the hook, breaking the EOO transmission. This will surface as far-end decode failures and be very hard to root-cause unless someone remembers this gap.
Suggested fix
Mirror the hook check inside the timer callback:
connect(m_pendingMoxOffTimer, &QTimer::timeout, this, [this]() {
m_pendingMoxOffTimer = nullptr;
m_quindarOutroInFlight = false;
emit quindarActiveChanged(false);
if (m_pttOffHook) {
m_pttOffHook();
return;
}
setMox(false);
});
Or, cleaner: factor the hook check into a private dispatchMoxOff() helper that both call sites use.
Acceptance
- Both
setMox(false) sites in TransmitModel::requestPttOff() consult m_pttOffHook before falling through.
- Optionally: add a unit test that sets a hook + enables Quindar + calls
requestPttOff() and asserts the hook fires (currently no Quindar test infrastructure exists, so this may itself be a follow-up).
Refs
73, Jeremy KK7GWY & Claude (AI dev partner)
Tracking follow-up from PR #3221 (
feat(rade): unified TX pipeline with EOO frame transmission and callsign encoding).The gap
The
PttOffHookmechanism added in #3221 interceptsrequestPttOff()so RADE can emit its End-of-Over frame before the radio drops the carrier. The hook is checked at the firstsetMox(false)call in TransmitModel::requestPttOff() at line 879:But the second
setMox(false)— inside the Quindar-outro timer callback at line 902 — does not check the hook:Why it's safe today
isPhoneModeForQuindar()returns true only for SSB/AM/FM, andactivateRADE()sets the slice to RADE's digital mode — so the Quindar outro path can't fire while RADE is active. The hook gap is latent, not exploitable today.Why it's worth tracking
If we ever:
requestPttOff(),…the Quindar outro path will silently drop the carrier without firing the hook, breaking the EOO transmission. This will surface as far-end decode failures and be very hard to root-cause unless someone remembers this gap.
Suggested fix
Mirror the hook check inside the timer callback:
Or, cleaner: factor the hook check into a private
dispatchMoxOff()helper that both call sites use.Acceptance
setMox(false)sites inTransmitModel::requestPttOff()consultm_pttOffHookbefore falling through.requestPttOff()and asserts the hook fires (currently no Quindar test infrastructure exists, so this may itself be a follow-up).Refs
requestPttOff()bodysetPttOffHook()/clearPttOffHook()API73, Jeremy KK7GWY & Claude (AI dev partner)