fix: a cluster spot dragged the rig from SUB back to MAIN

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.
This commit is contained in:
2026-07-29 16:07:20 +02:00
parent ee1f9ccf35
commit c297f91ca8
3 changed files with 78 additions and 8 deletions
+10
View File
@@ -1,4 +1,14 @@
[ [
{
"version": "0.22.1",
"date": "2026-07-29",
"en": [
"Clicking a cluster spot while listening on the SUB VFO no longer pulls the radio back to MAIN. The command used to tune sets the main VFO by definition; on SUB the sub VFO is now written directly and the VFO selection is left alone."
],
"fr": [
"Cliquer sur un spot du cluster en écoutant sur le VFO SUB ne ramène plus la radio sur le VFO principal. La commande utilisée pour s'accorder agit par définition sur le VFO principal ; sur SUB, c'est désormais le VFO secondaire qui est écrit, sans toucher à la sélection."
]
},
{ {
"version": "0.22.0", "version": "0.22.0",
"date": "2026-07-28", "date": "2026-07-28",
+21 -3
View File
@@ -493,13 +493,25 @@ func (o *OmniRig) SetFrequency(hz int64) error {
// method (RX=TX=freq, simplex). It works on rigs — notably Icom (IC-9100) — // method (RX=TX=freq, simplex). It works on rigs — notably Icom (IC-9100) —
// where direct FreqA/FreqB writes are accepted but never move the radio. // where direct FreqA/FreqB writes are accepted but never move the radio.
// Clearing split is the right thing when tuning to a spot anyway. // Clearing split is the right thing when tuning to a spot anyway.
// …but ONLY when the operator is on the main VFO. SetSimplexMode means
// "receive and transmit here, simplex", and OmniRig implements that on the
// MAIN VFO — so on an FTDX101 listening on SUB, clicking a cluster spot
// dragged the radio back to MAIN (F4NBZ, 2026-07-29). The operator picked SUB
// deliberately; a spot click is a request to change frequency, not to change
// VFO. On SUB the direct FreqB write below does the whole job.
onSubVFO := vfo == "B" || vfo == "BB" || vfo == "BA"
simplexOK := false simplexOK := false
switch {
case onSubVFO:
debugLog.Printf("OmniRig.SetFrequency: on VFO %q — skipping SetSimplexMode so the rig stays on SUB", vfo)
default:
if _, err := oleutil.CallMethod(o.rig, "SetSimplexMode", int32(hz32)); err == nil { if _, err := oleutil.CallMethod(o.rig, "SetSimplexMode", int32(hz32)); err == nil {
simplexOK = true simplexOK = true
debugLog.Printf("OmniRig.SetFrequency: SetSimplexMode(%d) OK", hz32) debugLog.Printf("OmniRig.SetFrequency: SetSimplexMode(%d) OK", hz32)
} else { } else {
debugLog.Printf("OmniRig.SetFrequency: SetSimplexMode unavailable (%v)", err) debugLog.Printf("OmniRig.SetFrequency: SetSimplexMode unavailable (%v)", err)
} }
}
// On Yaesu / Kenwood (and anything non-Icom), SetSimplexMode frequently returns // On Yaesu / Kenwood (and anything non-Icom), SetSimplexMode frequently returns
// OK but is a SILENT NO-OP — the rig never moves. Confirmed on an FT-891 over // OK but is a SILENT NO-OP — the rig never moves. Confirmed on an FT-891 over
@@ -511,12 +523,18 @@ func (o *OmniRig) SetFrequency(hz int64) error {
isIcom := strings.HasPrefix(strings.ToUpper(strings.TrimSpace(rigType)), "IC") isIcom := strings.HasPrefix(strings.ToUpper(strings.TrimSpace(rigType)), "IC")
if !isIcom || !simplexOK { if !isIcom || !simplexOK {
prop := "FreqA" prop := "FreqA"
switch vfo { if onSubVFO {
case "B", "BB", "BA":
prop = "FreqB" prop = "FreqB"
} }
okAny := false okAny := false
for _, p := range []string{prop, "Freq"} { // The generic "Freq" property is written too — but NOT on SUB, where it is
// the main VFO's frequency on several rig files and would move the VFO the
// operator is not using, or pull them back to it.
props := []string{prop, "Freq"}
if onSubVFO {
props = []string{prop}
}
for _, p := range props {
if _, e := oleutil.PutProperty(o.rig, p, hz32); e != nil { if _, e := oleutil.PutProperty(o.rig, p, hz32); e != nil {
debugLog.Printf("OmniRig.SetFrequency: PutProperty(%s) error: %v", p, e) debugLog.Printf("OmniRig.SetFrequency: PutProperty(%s) error: %v", p, e)
} else { } else {
+42
View File
@@ -83,3 +83,45 @@ func TestResolveOmniRigVFOs(t *testing.T) {
} }
} }
} }
// 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)
}
}
}