Fix waterfall TX trail by gating on radio interlock state (#1927)#2368
Conversation
Waterfall was unfreezing the instant the user released PTT, but the radio keeps streaming TX-contaminated tiles/FFT during the UNKEY_REQUESTED window. Those rows then took 10–23 s to scroll off the visible waterfall at typical speeds. Move the SpectrumWidget::setTransmitting() calls from the moxChanged handler to the radioTransmittingChanged handler, which fires on the actual interlock state==TRANSMITTING transition. Audio/DAX/serial PTT calls stay on moxChanged so TX/RX audio switching keeps its low-latency local edge. Side-effect win: radioTransmittingChanged is not ownership-gated, so the waterfall now also freezes when another Multi-Flex client triggers TX — previously missed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Thanks for the detailed root-cause writeup — the diagnosis matches the code paths I traced. The fix is small, targeted, and the asymmetric handling (audio/DAX/RADE/serial PTT stay on moxChanged for low-latency edge alignment, only the visual freeze moves to interlock state) is the right call.
A few things I checked and one question:
Verified
RadioModel::radioTransmittingChangedis emitted on everyinterlock state=...status (RadioModel.cpp:3317), so the listener will see the actualTRANSMITTING → UNKEY_REQUESTED → RECEIVEwalk and only flip the spectrum back to RX once the interlock leaves TRANSMITTING. That's exactly the gate you want.SpectrumWidget::setTransmitting(SpectrumWidget.h:329-341) is edge-guarded (if (tx && !m_transmitting)/if (!tx && m_transmitting)), so the multipleradioTransmittingChangedemissions during one TX cycle are idempotent — no double-restore ofm_preTxAutoBlack, no repeatedm_hasNativeWaterfall=false. Safe.m_appletPanel->sMeterWidget()->setTransmitting(tx)was already on this signal, so the new spectrum gating now matches the S-Meter's behavior — consistent.- The ownership-gating side-effect win is real:
radioTransmittingChangedis emitted before them_txOwnedByUscheck at line 3319, so other-client TX will now freeze this client's waterfall. Worth covering in the test plan as you have it.
Question — TX entry side
The writeup carefully covers the unkey transition (TRANSMITTING → UNKEY_REQUESTED → RECEIVE) but doesn't say what happens on the keying side. With the old code, moxChanged(true) froze the spectrum the instant the user pressed PTT. With this PR, the freeze waits for state == "TRANSMITTING", so during any pre-TX states (PTT_REQUESTED, KEY_REQUESTED, TX-delay window, etc.) the spectrum will keep painting whatever the radio sends. If the radio's TX chain ever leaks contaminated tiles before reaching TRANSMITTING (analogous to the unkey window), this fix would expose a small leading trail.
In practice I'd expect the interlock semantics to keep TX-contamination strictly inside TRANSMITTING, but it's worth a quick check on the TX-edge captures you already have. If you see clean entry on the Flex-8600 captures, no change needed — just call it out in the PR notes so future-you doesn't have to re-derive it.
Otherwise this looks good to merge. Nice fix.
Summary
Fixes #1927.
The waterfall continued to show TX-contaminated data for 10–23 seconds after PTT release. Root cause:
SpectrumWidget::setTransmitting(false)was driven fromTransmitModel::moxChanged, which fires the instant the user releases PTT — before the radio actually leaves the TRANSMITTING state.Sequence that produced the trail:
moxChanged(false)→ waterfall unfreezes immediatelyxmit 0is sent to the radioTRANSMITTING → UNKEY_REQUESTED → RECEIVEFix
Move the spectrum
setTransmitting(tx)loop from themoxChangedhandler to theradioTransmittingChangedhandler inMainWindow.cpp.radioTransmittingChangedis driven by the actual interlockstate == "TRANSMITTING"transition (RadioModel.cpp), so the waterfall now stays frozen until the radio confirms it has left TRANSMITTING.Audio / DAX / RADE / serial-port PTT calls remain on
moxChangedto preserve the low-latency local-edge behavior for TX/RX audio switching (waiting for interlock there would introduce audible lag).The
m_hasNativeWaterfallreset on TX→RX inSpectrumWidget.h:334is already in place, so the FFT-fallback stale-tile concern is already handled.Side-effect win:
radioTransmittingChangedis not ownership-gated, so the waterfall now also freezes when another Multi-Flex client triggers TX — which the ownership-gatedmoxChangedpreviously missed.Test plan
— AetherClaude (automated agent for AetherSDR)