Summary
Closing a panafall-created panadapter does not reliably tear it down, because the close path never issues the waterfall-side removal command. Per the FlexLib reference, fully closing a panafall requires both display pan remove 0x<panId> (Panadapter.Close) and display panafall remove 0x<wfStreamId> (Waterfall.Close). AetherSDR sends at most the first. Symptom: a second pan that won't dismiss from the UI and has to be torn down indirectly via slice removal.
This is distinct from #268 (a glitch-induced Multi-Flex corruption where Pan 2's FFT goes blank and the pan gets stuck); here the wrong/incomplete command is the root cause under normal conditions.
Two related defects
1. RadioModel::removePanadapter sends a non-existent command — and is dead code.
src/models/RadioModel.cpp:1838:
void RadioModel::removePanadapter(const QString& panId)
{
qCDebug(lcProtocol) << "RadioModel::removePanadapter:" << panId;
sendCmd(QString("display pan close %1").arg(panId)); // ← not a FlexLib command
// Radio will send "display pan <id> removed" → handled in onStatusReceived
}
FlexLib uses display pan remove (not close) — see Panadapter.Close() in the v4.2.18 reference. A grep shows this method has no callers anywhere in the tree, so it's currently dead, but the public "remove a panadapter" API is wrong and is a latent trap for anything that wires up to it.
2. The live close path omits the waterfall removal.
The actual X-button handler (src/gui/MainWindow_Wiring.cpp:1745) bypasses removePanadapter and sends only the pan-side command:
connect(applet, &PanadapterApplet::closeRequested, this, [this](const QString& panId) {
if (m_panStack->count() <= 1) return; // don't close the last pan
m_radioModel.sendCommand(QString("display pan remove %1").arg(panId));
});
It never sends display panafall remove <waterfallId>, so for a pan created via display panafall create (which allocates a panadapter stream and a waterfall stream) the waterfall side is never explicitly removed.
Why the client-side teardown is fine — the gap is on the send side
The status handler already cleans up both streams once the radio echoes display pan <id> removed (src/models/RadioModel.cpp:4444):
if (kvs.contains("removed") || object.endsWith("removed")) {
...
m_panStream->unregisterPanStream(pan->panStreamId());
m_panStream->unregisterWfStream(pan->wfStreamId()); // both streams torn down
emit panadapterRemoved(panId);
pan->deleteLater();
...
}
So the bug is purely that the close request to the radio is incomplete for a panafall.
Confirmed fix shape (already proven live)
The new automation-bridge pan close verb (PR #3842, this issue's sibling) issues the FlexLib-correct pair:
radio->sendCommand("display pan remove " + panId);
if (!wfId.isEmpty())
radio->sendCommand("display panafall remove " + wfId);
Driven against a live FLEX-8400M, this closed a panafall-created second pan cleanly (get pans count 2 → 1) without the slice-removal workaround:
pan add → 2 pans (0x40000000, 0x40000001)
grab pan 1 → ok
pan close 1 → closed [{panId:0x40000001, waterfallId:0x42000001}]
get pans → 1 pan
PanadapterModel already exposes panId() and waterfallId() (src/models/PanadapterModel.h), so the wfId is readily available at the close site.
Suggested fix
- Make
RadioModel::removePanadapter(panId) send display pan remove <panId> and, when the pan has a non-empty waterfallId(), display panafall remove <waterfallId>.
- Route the X-button handler (
MainWindow_Wiring.cpp:1745) through removePanadapter so there's a single source of truth, instead of an inline sendCommand.
- Verify on hardware that the radio echoes
display pan <id> removed for a panafall after the pair (the client cleanup at RadioModel.cpp:4444 then runs unchanged). Mind command ordering / radio-authoritative removal per the project's radio-API guidance.
Repro
- Connect to a radio, add a second panadapter.
- Click the second pan's close (X) button.
- Expected: the pan closes, layout returns to single-pan.
- Actual: the panafall-created pan does not fully tear down (waterfall stream lingers); it ends up only removable via slice removal.
Related
Filed from investigation during PR #3842. The automation side is fixed there; this issue tracks the production GUI close path.
Summary
Closing a panafall-created panadapter does not reliably tear it down, because the close path never issues the waterfall-side removal command. Per the FlexLib reference, fully closing a panafall requires both
display pan remove 0x<panId>(Panadapter.Close) anddisplay panafall remove 0x<wfStreamId>(Waterfall.Close). AetherSDR sends at most the first. Symptom: a second pan that won't dismiss from the UI and has to be torn down indirectly via slice removal.This is distinct from #268 (a glitch-induced Multi-Flex corruption where Pan 2's FFT goes blank and the pan gets stuck); here the wrong/incomplete command is the root cause under normal conditions.
Two related defects
1.
RadioModel::removePanadaptersends a non-existent command — and is dead code.src/models/RadioModel.cpp:1838:FlexLib uses
display pan remove(notclose) — seePanadapter.Close()in the v4.2.18 reference. Agrepshows this method has no callers anywhere in the tree, so it's currently dead, but the public "remove a panadapter" API is wrong and is a latent trap for anything that wires up to it.2. The live close path omits the waterfall removal.
The actual X-button handler (
src/gui/MainWindow_Wiring.cpp:1745) bypassesremovePanadapterand sends only the pan-side command:It never sends
display panafall remove <waterfallId>, so for a pan created viadisplay panafall create(which allocates a panadapter stream and a waterfall stream) the waterfall side is never explicitly removed.Why the client-side teardown is fine — the gap is on the send side
The status handler already cleans up both streams once the radio echoes
display pan <id> removed(src/models/RadioModel.cpp:4444):So the bug is purely that the close request to the radio is incomplete for a panafall.
Confirmed fix shape (already proven live)
The new automation-bridge
pan closeverb (PR #3842, this issue's sibling) issues the FlexLib-correct pair:Driven against a live FLEX-8400M, this closed a panafall-created second pan cleanly (
get panscount 2 → 1) without the slice-removal workaround:PanadapterModelalready exposespanId()andwaterfallId()(src/models/PanadapterModel.h), so the wfId is readily available at the close site.Suggested fix
RadioModel::removePanadapter(panId)senddisplay pan remove <panId>and, when the pan has a non-emptywaterfallId(),display panafall remove <waterfallId>.MainWindow_Wiring.cpp:1745) throughremovePanadapterso there's a single source of truth, instead of an inlinesendCommand.display pan <id> removedfor a panafall after the pair (the client cleanup atRadioModel.cpp:4444then runs unchanged). Mind command ordering / radio-authoritative removal per the project's radio-API guidance.Repro
Related
pan closeverb that implements the correct teardown (and proved it live).Filed from investigation during PR #3842. The automation side is fixed there; this issue tracks the production GUI close path.