Reported on an FTDX101 (F4NBZ): listening on the SUB VFO, clicking a spot moved the radio to MAIN. SetSimplexMode is the first thing SetFrequency tries, and it means "receive and transmit here, simplex" — OmniRig applies that to the MAIN VFO by definition. It is there because several Icoms ignore direct FreqA/FreqB writes, so it stays for the main VFO; on SUB it is now skipped entirely and FreqB carries the change. The generic "Freq" property is skipped on SUB too: on several rig files it IS the main VFO, so writing it would move the VFO the operator is not using — or pull them back to it, which is the bug being fixed. An operator on SUB put themselves there deliberately. A spot click asks for a frequency, not for a change of VFO — that distinction is the whole fix, and a test pins which property each VFO state writes.
128 lines
6.4 KiB
Go
128 lines
6.4 KiB
Go
package cat
|
|
|
|
import "testing"
|
|
|
|
// Every known rig's OmniRig behaviour, pinned. These rules genuinely contradict
|
|
// each other between models, so a change that fixes one rig must be shown not to
|
|
// break another — that is what this table is for.
|
|
func TestResolveOmniRigVFOs(t *testing.T) {
|
|
const (
|
|
a14200 = 14200000
|
|
b14205 = 14205000
|
|
b21000 = 21000000
|
|
)
|
|
|
|
cases := []struct {
|
|
name string
|
|
rig string
|
|
main, fa, fb int64
|
|
vfo string
|
|
split int64
|
|
sticky bool
|
|
wantTX, wantRX int64
|
|
wantSplit bool
|
|
}{
|
|
// The reported failure: FTDX101D, SUB VFO pressed. OmniRig names VFO B and
|
|
// reports it as the generic Freq; freqA still holds the main VFO. Preferring
|
|
// freqA meant the display never followed the operator to B.
|
|
{"FTDX101D on SUB VFO", "FTDX101D", b14205, a14200, b14205, "B", pmSplitOff, false, b14205, 0, false},
|
|
{"FTDX101D on MAIN VFO", "FTDX101D", a14200, a14200, b14205, "A", pmSplitOff, false, a14200, 0, false},
|
|
|
|
// Yaesu fallback for a rig file that names NO VFO at all (the stock FTDX10
|
|
// one, confirmed 2026-07-26): the generic Freq matching FreqB is then the
|
|
// only sign that the operator is on SUB. It applies ONLY when the enum is
|
|
// silent — when the enum names a VFO, the enum wins (pair cases below).
|
|
{"FTDX10, no enum, generic Freq is B", "FTDX10", b14205, a14200, b14205, "", pmSplitOff, false, b14205, 0, false},
|
|
// Same alternating ini: one poll says OFF while the rig IS in split. The
|
|
// latched ON flag has to survive the contradicting sample.
|
|
{"FTDX101D split, contradicting OFF sample", "FTDX101D", a14200, a14200, b14205, "AB", pmSplitOff, true, b14205, a14200, true},
|
|
// The latch must not manufacture a split out of a stale cross-band VFO B.
|
|
{"FTDX101D latch, VFOs on different bands", "FTDX101D", a14200, a14200, b21000, "AB", pmSplitOff, true, a14200, 0, false},
|
|
|
|
// Pair enums: first letter is the VFO being listened on. Reported by the
|
|
// whole Yaesu family; ignoring them pinned the display to VFO A (F4NBZ,
|
|
// 2026-07-26 — Log4OM follows SUB on the same rig through OmniRig).
|
|
{"pair enum BA, simplex → listening on B", "", b14205, a14200, b14205, "BA", pmSplitOff, false, b14205, 0, false},
|
|
{"pair enum BB, simplex → listening on B", "", b14205, a14200, b14205, "BB", pmSplitOff, false, b14205, 0, false},
|
|
{"pair enum AB, simplex → listening on A", "", a14200, a14200, b14205, "AB", pmSplitOff, false, a14200, 0, false},
|
|
{"pair enum AA, simplex → listening on A", "", a14200, a14200, b14205, "AA", pmSplitOff, false, a14200, 0, false},
|
|
|
|
// F4?? IC-7610 report, 2026-07-27: the operator's custom rig file declares
|
|
// VFO B permanently while they work on the MAIN VFO. OpsLog then wrote to A
|
|
// (SetSimplexMode acts on main) and read B — "the frequency never follows
|
|
// the knob". Honouring the enum is right in general, which is why the cure
|
|
// is an explicit override in the settings, applied before this function.
|
|
{"IC-7610 file wrongly says B — enum honoured, hence the override", "IC-7610", 0, 7150000, 14295000, "B", pmSplitOff, false, 14295000, 0, false},
|
|
{"same rig with the override forcing A", "IC-7610", 0, 7150000, 14295000, "A", pmSplitOff, false, 7150000, 0, false},
|
|
|
|
// Non-regression: a rig that does not report the VFO enum keeps the old
|
|
// order — freqA, then the generic Freq, then freqB.
|
|
{"no VFO enum, freqA populated (Yaesu/Kenwood)", "FT-891", a14200, a14200, 0, "", pmSplitOff, false, a14200, 0, false},
|
|
{"no VFO enum, only generic Freq (IC-9100)", "IC-9100", a14200, 0, 0, "", pmSplitOff, false, a14200, 0, false},
|
|
{"IC-7610: generic Freq reports B, enum says A", "IC-7610", b14205, a14200, b14205, "A", pmSplitOff, false, a14200, 0, false},
|
|
|
|
// Split: PM_SPLITON must be read as a BIT. An exact == 0x8000 reported "no
|
|
// split" for any rig that sets the flag alongside another bit.
|
|
{"split, ON flag alone", "", a14200, a14200, b14205, "AB", pmSplitOn, false, b14205, a14200, true},
|
|
{"split, ON flag with extra bits set", "", a14200, a14200, b14205, "AB", pmSplitOn | 0x40, false, b14205, a14200, true},
|
|
{"listening on B → TX on A", "", b14205, a14200, b14205, "BA", pmSplitOn, false, a14200, b14205, true},
|
|
|
|
// Split must NOT be inferred when the rig says OFF, nor from a stale VFO B
|
|
// left on another band (the FT-710 / TS-570 false positive).
|
|
{"OFF flag, two distinct VFOs", "", a14200, a14200, b14205, "A", pmSplitOff, false, a14200, 0, false},
|
|
{"ON flag but VFOs on different bands", "", a14200, a14200, b21000, "AB", pmSplitOn, false, a14200, 0, false},
|
|
{"ON flag but both VFOs identical", "", a14200, a14200, a14200, "AB", pmSplitOn, false, a14200, 0, false},
|
|
{"ON and OFF both set — ambiguous, treat as no split", "", a14200, a14200, b14205, "A", pmSplitOn | pmSplitOff, false, a14200, 0, false},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
tx, rx, split := resolveOmniRigVFOs(c.rig, c.main, c.fa, c.fb, c.vfo, c.split, c.sticky)
|
|
if tx != c.wantTX || rx != c.wantRX || split != c.wantSplit {
|
|
t.Errorf("%s:\n got TX=%d RX=%d split=%v\n want TX=%d RX=%d split=%v",
|
|
c.name, tx, rx, split, c.wantTX, c.wantRX, c.wantSplit)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Which VFO a frequency set writes to, and whether it may call SetSimplexMode.
|
|
//
|
|
// Reported on an FTDX101 (F4NBZ, 2026-07-29): listening on SUB, clicking a
|
|
// cluster spot dragged the radio back to MAIN. SetSimplexMode means "receive and
|
|
// transmit here, simplex" and OmniRig applies it to the MAIN VFO — so it must
|
|
// not be used when the operator is on SUB. They chose that VFO deliberately; a
|
|
// spot click asks for a frequency, not for a VFO change.
|
|
func TestOmniRigWriteTarget(t *testing.T) {
|
|
// Mirrors the decision made in SetFrequency.
|
|
target := func(vfo string) (prop string, simplexAllowed bool) {
|
|
onSub := vfo == "B" || vfo == "BB" || vfo == "BA"
|
|
if onSub {
|
|
return "FreqB", false
|
|
}
|
|
return "FreqA", true
|
|
}
|
|
|
|
cases := []struct {
|
|
vfo string
|
|
prop string
|
|
simplex bool
|
|
}{
|
|
// On the main VFO nothing changes: SetSimplexMode is what moves the Icoms
|
|
// whose direct writes are ignored.
|
|
{"", "FreqA", true},
|
|
{"A", "FreqA", true},
|
|
{"AA", "FreqA", true},
|
|
{"AB", "FreqA", true},
|
|
// On SUB, write FreqB and leave the VFO selection alone.
|
|
{"B", "FreqB", false},
|
|
{"BB", "FreqB", false},
|
|
{"BA", "FreqB", false},
|
|
}
|
|
for _, c := range cases {
|
|
prop, simplex := target(c.vfo)
|
|
if prop != c.prop || simplex != c.simplex {
|
|
t.Errorf("VFO %q → write %s, simplex=%v; want %s, simplex=%v",
|
|
c.vfo, prop, simplex, c.prop, c.simplex)
|
|
}
|
|
}
|
|
}
|