From 1c71495446044b83f93cc8cd41a997e1d8850b42 Mon Sep 17 00:00:00 2001 From: rouggy Date: Sat, 29 Aug 2026 18:35:41 +0200 Subject: [PATCH] fix(icom): give up on the quiet CI-V stream after 15 s, not 30 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The capture settled it: the 7760 keeps the transport chatting (pings answered, no socket error, no loss) while CI-V data simply stops, the in-place reopen is ignored, and only a fresh session brings it back. So: one reopen attempt at 10 s — kept, it is free and the log will say if a firmware ever honours it — and at 15 s the link is failed deliberately so the manager rebuilds it, halving the outage. Also: the recovered-acknowledgement now sits on the DATA packet, not on any packet — pings were toggling the detector, 160 reopens in thirty seconds all logged as recoveries that never happened. --- internal/cat/icomnet.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/internal/cat/icomnet.go b/internal/cat/icomnet.go index dabefb7..78c3867 100644 --- a/internal/cat/icomnet.go +++ b/internal/cat/icomnet.go @@ -372,6 +372,7 @@ func (n *icomNet) civPump() { lastScope := time.Time{} // scope frames within those var lastErr error quietSaid := false + gaveUp := false for { select { case <-n.done: @@ -386,10 +387,6 @@ func (n *icomNet) civPump() { } } if err == nil && k >= 16 { - if quietSaid { - debugLog.Printf("icom net: CI-V replies are back after %s", time.Since(lastData).Round(time.Second)) - quietSaid = false - } lastPkt = time.Now() n.markRx() switch typ := icnLE.Uint16(buf[4:]); { @@ -403,6 +400,10 @@ func (n *icomNet) civPump() { n.dead.Store(true) // make Alive() fail now → prompt clean reconnect debugLog.Printf("icom net: rig sent DISCONNECT on CI-V stream — session dropped by the rig") case typ == 0x00 && k > 0x15 && buf[0x10] == 0xc1: // CI-V data + if quietSaid { + debugLog.Printf("icom net: CI-V replies are back after %s", time.Since(lastData).Round(time.Second)) + quietSaid, gaveUp = false, false + } lastData = time.Now() n.trackRxSeq(icnLE.Uint16(buf[6:])) // note gaps for retransmit civBytes := buf[0x15:k] @@ -461,6 +462,15 @@ func (n *icomNet) civPump() { _, _ = n.civ.Write(ocPkt) debugLog.Printf("icom net: re-sent the CI-V open on the existing stream") } + // The reopen was given five seconds. On a real IC-7760 it never works — + // the rig ignores it and only a fresh session brings CI-V back — so + // rather than sit out the 30 s watchdog, fail the link NOW and let the + // manager rebuild it: the outage drops from ~35 s to ~15. + if quietSaid && !gaveUp && time.Since(lastData) > 15*time.Second { + gaveUp = true + n.dead.Store(true) + debugLog.Printf("icom net: the reopen did not bring CI-V back — forcing a fresh session") + } if time.Since(lastIdle) > 150*time.Millisecond { _, _ = n.civ.Write(icnCtrl(0x00, 0, n.vID, n.vRemote)) lastIdle = time.Now()