diff --git a/internal/cat/icomnet.go b/internal/cat/icomnet.go index 1397cf5..0c8db99 100644 --- a/internal/cat/icomnet.go +++ b/internal/cat/icomnet.go @@ -359,6 +359,14 @@ func (n *icomNet) civPump() { buf := make([]byte, 8192) lastIdle := time.Now() lastReq := time.Now() + // Diagnosis for the recurring 2-3-minute silence a real IC-7760 shows on + // this stream while the control link stays alive: when the stream has been + // quiet for 10 s, say so ONCE, with the last read error — a socket error + // and a rig that stopped talking are different repairs, and the 30 s + // watchdog that follows cannot tell them apart from where it sits. + lastPkt := time.Now() + var lastErr error + quietSaid := false for { select { case <-n.done: @@ -366,7 +374,18 @@ func (n *icomNet) civPump() { default: } _ = n.civ.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) - if k, err := n.civ.Read(buf); err == nil && k >= 16 { + k, err := n.civ.Read(buf) + if err != nil { + if e, ok := err.(net.Error); !ok || !e.Timeout() { + lastErr = err // a REAL socket error, not the read deadline + } + } + if err == nil && k >= 16 { + if quietSaid { + debugLog.Printf("icom net: CI-V stream is talking again after %s of silence", time.Since(lastPkt).Round(time.Second)) + quietSaid = false + } + lastPkt = time.Now() n.markRx() switch typ := icnLE.Uint16(buf[4:]); { case typ == 0x07: // ping @@ -410,6 +429,10 @@ func (n *icomNet) civPump() { } } } + if !quietSaid && time.Since(lastPkt) > 10*time.Second { + quietSaid = true + debugLog.Printf("icom net: CI-V stream quiet for 10 s (control link still alive; last socket error: %v; missing-seq backlog: %d) — idles and retransmit requests are still being sent", lastErr, len(n.rxMissing)) + } if time.Since(lastIdle) > 150*time.Millisecond { _, _ = n.civ.Write(icnCtrl(0x00, 0, n.vID, n.vRemote)) lastIdle = time.Now()