From 980c54a616b61437ccd25faa05e572a58749a2f7 Mon Sep 17 00:00:00 2001 From: rouggy Date: Wed, 26 Aug 2026 21:25:06 +0200 Subject: [PATCH] fix(tci): TUNE can be switched off again, and the meters watch it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two faults with one cause: this radio does not echo 'tune:0,true'. So the panel never knew a tune was running. The button stayed on TUNE and every further press sent another START — there was no way to stop it from here at all. The state is recorded when the command is sent now; whatever the radio says afterwards still wins, it simply never says anything. And the transmit meters were asked for only while t.tx, which a tune carrier does not set: the radio reports tuning as its own state, not as a transmission. So power and SWR sat at zero for the whole tune — the exact carrier an operator holds a tune for in order to watch an SWR on. They now follow PTT or TUNE, and the S-meter reads '—' under our own carrier either way. --- frontend/src/components/TCIPanel.tsx | 6 +++--- internal/cat/tci.go | 6 +++++- internal/cat/tci_panel.go | 13 ++++++++++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/TCIPanel.tsx b/frontend/src/components/TCIPanel.tsx index c93f5b7..8dbbd63 100644 --- a/frontend/src/components/TCIPanel.tsx +++ b/frontend/src/components/TCIPanel.tsx @@ -274,15 +274,15 @@ export function TCIPanel({ onReportRST }: { onReportRST?: (rst: string) => void There is no temperature: the protocol has no such command, and a made-up figure on a transmitter is the kind somebody trusts. */} - { if (st.tx || !onReportRST) return; onReportRST(sMeterRST(s.s, s.over, mode)); }} title={t('tcip.sMeterHint')} /> - {st.tx && ( + {(st.tx || st.tuning) && (
diff --git a/internal/cat/tci.go b/internal/cat/tci.go index 1d99fef..7a3ab2f 100644 --- a/internal/cat/tci.go +++ b/internal/cat/tci.go @@ -309,7 +309,11 @@ func (t *TCI) ReadState() (RigState, error) { // anything while it is keyed. Fired and forgotten from here — the answers // arrive on the reader like everything else — and only while transmitting, // so a receiving station pays nothing for a meter nobody is watching. - if t.tx { + // Keyed by PTT **or** by TUNE. A tune carrier is exactly when the meters + // matter most — it is the carrier an operator is watching an SWR on — and + // asking only on t.tx left them at zero for the whole tune, because the + // radio reports tuning as its own state and not as a transmission. + if t.tx || t.panel.st.Tuning { tx := t go func() { _ = tx.send("tx_power;") diff --git a/internal/cat/tci_panel.go b/internal/cat/tci_panel.go index ce89db7..7e3afb6 100644 --- a/internal/cat/tci_panel.go +++ b/internal/cat/tci_panel.go @@ -341,7 +341,18 @@ func (t *TCI) SetLock(on bool) error { return t.send(fmt.Sprintf("lock:0,%t;", o // // It TRANSMITS, at tune_drive rather than at drive — which is the setting to // check before pressing it, and why the panel shows the two side by side. -func (t *TCI) SetTune(on bool) error { return t.send(fmt.Sprintf("tune:0,%t;", on)) } +// +// The state is recorded HERE rather than waited for. This radio does not echo +// "tune:0,true", so the panel had no way of knowing a tune was running: the +// button stayed on TUNE and every further press sent another START, which is +// why it could not be switched off again. Whatever the radio says afterwards +// still wins — it simply never says anything. +func (t *TCI) SetTune(on bool) error { + t.mu.Lock() + t.panel.st.Tuning = on + t.mu.Unlock() + return t.send(fmt.Sprintf("tune:0,%t;", on)) +} func clampTCIPct(v int) int { if v < 0 {