feat(rigctld): split that actually reaches the radio, or an honest refusal

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.
This commit is contained in:
2026-08-11 17:53:46 +02:00
parent fa01968207
commit 43095a1d89
9 changed files with 192 additions and 7 deletions
+1 -1
View File
@@ -102,7 +102,7 @@ func TestKenwoodModeDigit(t *testing.T) {
{"FM", 145000000, '4'},
{"FT8", 7074000, '2'}, // data is ALWAYS USB, even below 10 MHz (K3 "DATA REV" otherwise)
{"FT8", 14074000, '2'}, // …and above
{"", 14074000, 0}, // nothing to set
{"", 14074000, 0}, // nothing to set
}
for _, c := range cases {
if got := kenwoodModeDigit(c.mode, c.hz); got != c.want {