set_split_vfo and set_split_freq both answered RPRT 0 and did nothing. WSJT-X and JTDX in "Split Operating: Rig" send exactly that pair, believed both, and transmitted on the RECEIVE frequency — on a pileup, straight onto the DX, while showing the operator precisely what they had asked for. A lie that leaves no trace in any log is the worst kind of bug this program can have. The two commands are honoured as a PAIR. Arming alone does nothing on the radio, because WSJT-X sends the frequency second and split armed on whatever the transmit VFO happened to hold is worse than no split at all: it transmits somewhere the operator never chose. The request is remembered and set_split_freq does the work. Kenwood gains SetSplit — FB to place the dial, then FR0/FT1 to arm, in that order for the same reason. It writes what State() already knows how to read. Everything else REFUSES, and that is the feature, not a shortfall. Only Flex and Icom could even toggle split before, neither could set the transmit frequency, and Yaesu, TCI and OmniRig have nothing at all. A refusal WSJT-X can report — and act on, by falling back to Fake It — is worth far more than a success it has no way to check. Both paths are pinned: split reaching the rig as one armed call with the right frequency, and a backend that cannot do it producing an error rather than RPRT 0.
53 lines
1.9 KiB
Go
53 lines
1.9 KiB
Go
package rigctld
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// WSJT-X in "Split Operating: Rig" sends set_split_vfo then set_split_freq. Both
|
|
// used to answer RPRT 0 and do NOTHING: the software believed it was
|
|
// transmitting up the band while the radio stayed on the receive frequency —
|
|
// on a pileup, straight onto the DX, with no trace anywhere.
|
|
func TestSetSplitReachesTheRig(t *testing.T) {
|
|
rig := &fakeRig{freq: 14074000, mode: "FT8"}
|
|
s := New(0, rig, func(string, ...any) {})
|
|
|
|
if got, _ := s.handle("S 1 VFOB"); !strings.HasPrefix(got, "RPRT 0") {
|
|
t.Fatalf("set_split_vfo answered %q", got)
|
|
}
|
|
// Arming alone must NOT touch the rig: without a frequency it would transmit
|
|
// on whatever the second VFO happened to hold.
|
|
if len(rig.splitCalls) != 0 {
|
|
t.Errorf("split was armed before a frequency arrived: %v", rig.splitCalls)
|
|
}
|
|
|
|
if got, _ := s.handle("I 14075300.000000"); !strings.HasPrefix(got, "RPRT 0") {
|
|
t.Fatalf("set_split_freq answered %q", got)
|
|
}
|
|
if len(rig.splitCalls) != 1 || rig.splitCalls[0] != "true:14075300" {
|
|
t.Fatalf("rig saw %v, want one call arming split on 14075300", rig.splitCalls)
|
|
}
|
|
|
|
if got, _ := s.handle("S 0 VFOA"); !strings.HasPrefix(got, "RPRT 0") {
|
|
t.Fatalf("split off answered %q", got)
|
|
}
|
|
if len(rig.splitCalls) != 2 || !strings.HasPrefix(rig.splitCalls[1], "false:") {
|
|
t.Errorf("rig saw %v, want split cleared", rig.splitCalls)
|
|
}
|
|
}
|
|
|
|
// A backend that cannot do split must produce an ERROR the client can report.
|
|
// Answering success and doing nothing is what caused the original fault, and it
|
|
// is the one outcome that must never come back.
|
|
func TestSetSplitRefusalIsReported(t *testing.T) {
|
|
rig := &fakeRig{freq: 14074000, noSplit: true}
|
|
s := New(0, rig, func(string, ...any) {})
|
|
|
|
s.handle("S 1 VFOB")
|
|
got, _ := s.handle("I 14075300.000000")
|
|
if strings.HasPrefix(got, "RPRT 0") {
|
|
t.Errorf("a rig that cannot split answered %q — the client will transmit on the wrong frequency", got)
|
|
}
|
|
}
|