diff --git a/internal/cat/omnirig.go b/internal/cat/omnirig.go index 7fb0853..f4c430a 100644 --- a/internal/cat/omnirig.go +++ b/internal/cat/omnirig.go @@ -67,6 +67,10 @@ type OmniRig struct { splitFlaky bool splitFlips int splitFlipWindow time.Time + + // pendingReadback schedules one delayed frequency log after a Set — see + // SetFrequency. Zero when nothing is pending. + pendingReadback time.Time } // NewOmniRig creates a non-connected backend. Call Connect before use. @@ -328,6 +332,14 @@ func (o *OmniRig) ReadState() (RigState, error) { } s.FreqHz, s.RxFreqHz, s.Split = resolveOmniRigVFOs(o.rigType, freqMain, freqA, freqB, vfo, splitRaw, splitRecentOn) + // Delayed readback after a Set (see SetFrequency): logged from HERE because + // this runs on the COM-owning goroutine. One line, once per set. + if !o.pendingReadback.IsZero() && time.Now().After(o.pendingReadback) { + o.pendingReadback = time.Time{} + debugLog.Printf("OmniRig.SetFrequency: readback +1.5s FreqA=%d FreqB=%d Freq=%d → shown %d", + freqA, freqB, freqMain, s.FreqHz) + } + // Diagnostic logged ONLY when Split or VFO changes (not on a timer), so normal // operation stays quiet but toggling split or SUB VFO on the radio is // captured. It logs the RESOLVED tx/rx/split too: with the raw values alone a @@ -517,21 +529,31 @@ func (o *OmniRig) SetFrequency(hz int64) error { } } - // Read back all three immediately. OmniRig is async (the CAT command is - // queued + sent over serial), so these may still show the OLD value for - // one poll cycle — but if they NEVER change in the next poll, the rig - // isn't honouring the write (wrong .ini WRITE command for this model). - fa, fb, fg := int64(-1), int64(-1), int64(-1) - if v, err := oleutil.GetProperty(o.rig, "FreqA"); err == nil { - fa = v.Val + // Read back all three, then AGAIN a moment later. OmniRig is async — the CAT + // command is queued and sent over serial — so an immediate read always shows + // the old value and proves nothing. The delayed one is the useful half: it + // separates "the rig ignored the write" from "the rig moved but its .ini + // never re-reads the frequency", which look identical to an operator whose + // display stays on the old band until they nudge the VFO. + // + // Off the CAT goroutine so the poll loop is not held for a second. COM is + // thread-affine, so the delayed read goes through the manager's own command + // channel rather than touching o.rig from here. + logFreqs := func(when string) { + fa, fb, fg := int64(-1), int64(-1), int64(-1) + if v, err := oleutil.GetProperty(o.rig, "FreqA"); err == nil { + fa = v.Val + } + if v, err := oleutil.GetProperty(o.rig, "FreqB"); err == nil { + fb = v.Val + } + if v, err := oleutil.GetProperty(o.rig, "Freq"); err == nil { + fg = v.Val + } + debugLog.Printf("OmniRig.SetFrequency: readback %s FreqA=%d FreqB=%d Freq=%d (target %d)", when, fa, fb, fg, hz) } - if v, err := oleutil.GetProperty(o.rig, "FreqB"); err == nil { - fb = v.Val - } - if v, err := oleutil.GetProperty(o.rig, "Freq"); err == nil { - fg = v.Val - } - debugLog.Printf("OmniRig.SetFrequency: readback FreqA=%d FreqB=%d Freq=%d (target %d)", fa, fb, fg, hz) + logFreqs("now") + o.pendingReadback = time.Now().Add(1500 * time.Millisecond) return nil }