Files
OpsLog/catshare_freq_test.go
rouggy 10e1504531 fix(cat-share): answer get_freq with the listening VFO, not the transmit one
Reported from a station running an IC-7850 over USB: with split engaged,
turning VFO B moved VFO A slightly. OpsLog writes nothing to that radio on
its own — but it shares it, and what it was sharing was wrong.

Hamlib's 'f' means the VFO IN USE. RigState follows ADIF, where FreqHz is
where we TRANSMIT, and the two are the same number until split is
engaged — so the adapter handed FreqHz over unchanged and every client
asking for the dial was given VFO B. A client that reads the dial and
writes it back, which is what WSJT-X and its like do, then wrote VFO B's
frequency into VFO A. Hence a shift the size of the split offset, and
hence 'it did not do this before': it only happens with split on.

get_split_freq ('i') already answers the transmit frequency and is
untouched. The one-line rule is lifted into shareRXFreq with a test that
includes this case, the simplex case, and a backend that reports split
without ever filling in the receive frequency — which would otherwise
answer a client with 0 Hz.
2026-08-26 11:41:15 +02:00

67 lines
2.2 KiB
Go

package main
import (
"testing"
"hamlog/internal/cat"
)
// What a rigctl client is told when it asks "what frequency is the radio on".
//
// Hamlib's "f" means the VFO IN USE — where the operator is listening. RigState
// follows ADIF, where FreqHz is where we TRANSMIT, and the two are the same
// number until split is engaged. They were handed over unchanged, so with split
// on every client asking for the dial was given VFO B.
//
// Reported from a station running an IC-7850 over USB: turning VFO B moved
// VFO A. OpsLog was writing nothing to that radio — a client read the dial, was
// handed the transmit VFO, and wrote it back as the dial.
func TestShareRXFreqAnswersTheListeningVFO(t *testing.T) {
const rx, tx = 14195000, 14200500
cases := []struct {
name string
st cat.RigState
want int64
}{
{
name: "simplex — the one frequency there is",
st: cat.RigState{FreqHz: rx},
want: rx,
},
{
name: "split — the RX VFO, not the TX one",
st: cat.RigState{Split: true, FreqHz: tx, RxFreqHz: rx},
want: rx,
},
{
// A backend that reports split without ever filling RxFreqHz would
// otherwise be answered with 0, and a client told the radio is on
// 0 Hz does something worse than nothing with it.
name: "split claimed but no RX frequency known",
st: cat.RigState{Split: true, FreqHz: tx},
want: tx,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := shareRXFreq(c.st); got != c.want {
t.Errorf("shareRXFreq(%+v) = %d, want %d", c.st, got, c.want)
}
})
}
}
// And the pair, stated together: the two questions must not return the same
// answer while split is on, or the distinction has been lost somewhere.
func TestShareSplitReportsTheOtherVFO(t *testing.T) {
st := cat.RigState{Split: true, FreqHz: 14200500, RxFreqHz: 14195000}
rxAnswer := shareRXFreq(st)
txAnswer := st.FreqHz // what Split() hands back for "i" / get_split_freq
if rxAnswer == txAnswer {
t.Fatalf("get_freq and get_split_freq both answered %d — a client cannot tell the VFOs apart", rxAnswer)
}
if rxAnswer != st.RxFreqHz || txAnswer != st.FreqHz {
t.Errorf("got rx=%d tx=%d, want rx=%d tx=%d", rxAnswer, txAnswer, st.RxFreqHz, st.FreqHz)
}
}