From 622bc67df56d597966dc0f628dc2c1a2bac9f7db Mon Sep 17 00:00:00 2001 From: nigelfenton Date: Wed, 15 Jul 2026 19:01:34 -0400 Subject: [PATCH 1/2] fix(ui): reset stale per-connection state so reconnect is clean Two client-side values were cached across a radio disconnect and reused on the next connection, causing wrong state on reconnect. Both are the same shape: a field that RadioModel/the connection resets but a mirror does not. 1. Stale radio-name (status-bar station label). connectToRadio() seeded m_name/m_model/m_version from the discovery RadioInfo but NOT m_nickname/ m_callsign, and m_nickname is never cleared on disconnect. So on connect the label (set synchronously in onConnectionStateChanged) read a stale m_nickname: blank on the first connect, or the PREVIOUSLY connected radio's name. Seed nickname/callsign from the discovery packet (which already carries them), and also refresh the station label in the late-arriving infoChanged handler (it previously updated only model + version). 2. DAX-TX stream not re-created on reconnect (digital/AX.25 TX went out as a bare carrier). RadioModel resets its dax_tx state on disconnect, but AudioEngine::m_txStreamId kept the OLD id. The AetherModem AX.25 path only calls ensureDaxTxStream() when txStreamId()==0, so on reconnect it saw the stale non-zero id, skipped the re-create, and pumped modem audio to a dead stream. Clear m_txStreamId on connection loss so the next transmit re-creates the stream. Both verified live on real hardware (IC-9700 via a Flex-emulating gateway): name now correct on first connect; AX.25 modulation now works on reconnect without re-opening the modem terminal. Co-Authored-By: Claude Opus 4.8 --- src/gui/MainWindow.cpp | 14 ++++++++++++++ src/models/RadioModel.cpp | 8 ++++++++ 2 files changed, 22 insertions(+) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 2f12ceb41..e89b41545 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1432,6 +1432,11 @@ MainWindow::MainWindow(QWidget* parent) m_radioInfoLabel->setText(m_radioModel.model()); if (m_radioVersionLabel && !m_radioModel.version().isEmpty()) m_radioVersionLabel->setText(m_radioModel.version()); + // Also refresh the station/nickname label: the nickname can be corrected + // by the async "info" reply after connect, and this handler previously + // updated only model + version, leaving a stale station name on screen. + if (m_stationLabel && !m_radioModel.nickname().isEmpty()) + setStatusBarStationText(m_stationLabel, m_radioModel.nickname()); }); // Propagate late-arriving SmartSDR+ subscription + dual-SCU diversity @@ -5277,6 +5282,15 @@ void MainWindow::onConnectionStateChanged(bool connected) #endif audioStopRx(); audioStopTx(); + // Clear the cached DAX-TX stream id on connection loss. RadioModel already + // resets its own dax_tx state on disconnect, but AudioEngine::m_txStreamId + // was left holding the OLD id — so after a reconnect the AX.25 modem's + // "create the stream only if txStreamId()==0" guard saw a stale non-zero + // id, skipped ensureDaxTxStream(), and pumped modem audio to a dead stream + // (bare carrier, no modulation). Resetting it here makes the next transmit + // re-create the DAX-TX stream on the new connection. + if (m_audio) + m_audio->setTxStreamId(0); for (auto it = m_panFpsReconcileConnections.begin(); it != m_panFpsReconcileConnections.end(); ++it) { diff --git a/src/models/RadioModel.cpp b/src/models/RadioModel.cpp index a05747fd7..7a32a517b 100644 --- a/src/models/RadioModel.cpp +++ b/src/models/RadioModel.cpp @@ -1767,6 +1767,14 @@ void RadioModel::connectToRadio(const RadioInfo& info) m_name = info.name; m_model = info.model; m_version = info.version; + // Seed nickname/callsign from the discovery packet so the status-bar station + // label is correct the instant onConnectionStateChanged(true) reads it. These + // were previously only set later from the async "info" reply, so on connect + // the label showed a STALE m_nickname — blank on the first connect, or the + // PREVIOUSLY connected radio's name (it is never cleared on disconnect). The + // async reply still refreshes them if they differ. + m_nickname = info.nickname; + m_callsign = info.callsign; m_declaredBands = parseDeclaredBands(info.bands); // empty for real Flex m_maxSlices = maxSlicesForModel(m_model); if (reloadAntennaAliases()) From 20a6a07e21640b7228ab90a60d5a95c32a9576d5 Mon Sep 17 00:00:00 2001 From: nigelfenton Date: Sun, 19 Jul 2026 17:12:59 -0400 Subject: [PATCH 2/2] fix(ui): clear nickname on disconnect so all reconnect paths are clean MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the CHANGES_REQUESTED review on #4260 (@NF0T, confirmed by @ten9876). The previous commit seeded m_nickname only in RadioModel::connectToRadio(const RadioInfo&), so the stale-station-label bug remained live on two other paths: connectViaWan() takes no RadioInfo and never seeds it, and the LAN auto-reconnect timer calls m_connection->connectToRadio(m_lastInfo) directly, bypassing RadioModel::connectToRadio() entirely. onDisconnected() also cleared m_callsign but not m_nickname, so the disconnect-side half named in the commit message wasn't actually fixed. Clearing m_nickname in onDisconnected() alongside the existing m_callsign.clear() closes all three paths in one line, as both reviewers suggested: no connect path can now render a previous radio's station label while the async info reply is in flight. Also drops the redundant 'm_stationLabel &&' guard — setStatusBarStationText() null-checks internally (MainWindow.cpp:320), and no other call site re-guards it. Verified on aurora13 against a live FLEX-6300 via the automation bridge: before connect -> nickname '' after connect -> nickname 'Nigel' after disconnect -> nickname '' (was: stale 'Nigel' before this fix) Build green (815 targets); 126 non-GUI tests pass, 0 failures. Principle VII. Co-Authored-By: Claude Opus 4.8 --- src/gui/MainWindow.cpp | 2 +- src/models/RadioModel.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index e89b41545..8695b187a 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -1435,7 +1435,7 @@ MainWindow::MainWindow(QWidget* parent) // Also refresh the station/nickname label: the nickname can be corrected // by the async "info" reply after connect, and this handler previously // updated only model + version, leaving a stale station name on screen. - if (m_stationLabel && !m_radioModel.nickname().isEmpty()) + if (!m_radioModel.nickname().isEmpty()) setStatusBarStationText(m_stationLabel, m_radioModel.nickname()); }); diff --git a/src/models/RadioModel.cpp b/src/models/RadioModel.cpp index 7a32a517b..6a773a706 100644 --- a/src/models/RadioModel.cpp +++ b/src/models/RadioModel.cpp @@ -3811,6 +3811,13 @@ void RadioModel::onDisconnected() m_staleSessionSerial = m_chassisSerial; m_chassisSerial.clear(); m_callsign.clear(); + // Clear the nickname here too, not just on the connectToRadio() seeding + // path: connectViaWan() takes no RadioInfo and the LAN auto-reconnect timer + // calls m_connection->connectToRadio(m_lastInfo) directly, both bypassing + // RadioModel::connectToRadio(). Clearing on the disconnect side closes all + // three paths at once, so a reconnect can never show the previous radio's + // station label while the async info reply is in flight. (#4260 review) + m_nickname.clear(); m_region.clear(); m_rxAudio = {}; m_netCwStreamId = 0;