fix(rigctld): stop refusing two commands JTDX sends in Fake It
From a user's log, repeating throughout the session: rigctld: split off failed: cat: this radio's backend cannot set split rigctld: unimplemented command "X PKTUSB -1" Both are answered with an error, and JTDX treats a rig-control error as fatal: it abandoned a transmission 0.86 seconds into a 13.8-second frame. "S 0" — split OFF. Fake It uses no split, and JTDX still sends this to be sure. A backend that cannot SET split was refusing a request that was ALREADY satisfied. It now succeeds when the rig is simplex, without touching the rig at all. Arming still fails loudly: there the request is real and unmet, and a client that believes it transmits up the band while the radio sits on the DX's frequency is exactly what that refusal exists to prevent. "X <mode>" — set_split_mode. Sent during ordinary setup, Fake It included, where there is no second VFO to give a mode to. Accepted now: applied when a split is in force, and a plain success otherwise, because the transmit VFO already has that mode — it is the same VFO. get_split_mode answers to match. Both regressions fail without the fix with the exact codes from that log, RPRT -9 and RPRT -11.
This commit is contained in:
-18902
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -4,11 +4,13 @@
|
|||||||
"date": "",
|
"date": "",
|
||||||
"en": [
|
"en": [
|
||||||
"Amplifiers: tick the ones sharing a combiner and ON, OFF and OPERATE act on all of them at once. Each keeps its own meters.",
|
"Amplifiers: tick the ones sharing a combiner and ON, OFF and OPERATE act on all of them at once. Each keeps its own meters.",
|
||||||
"Shared CAT: with the wire trace on, every command a client sends and the answer given are logged."
|
"Shared CAT: with the wire trace on, every command a client sends and the answer given are logged.",
|
||||||
|
"Shared CAT: JTDX and WSJT-X no longer get an error for turning split off on a rig that has none, or for setting the transmit mode — which made JTDX abandon a transmission."
|
||||||
],
|
],
|
||||||
"fr": [
|
"fr": [
|
||||||
"Amplificateurs : coche ceux qui partagent un combiner et ON, OFF et OPERATE agissent sur tous à la fois. Chacun garde ses mesures.",
|
"Amplificateurs : coche ceux qui partagent un combiner et ON, OFF et OPERATE agissent sur tous à la fois. Chacun garde ses mesures.",
|
||||||
"CAT partagé : avec la trace activée, chaque commande envoyée par un client et la réponse donnée sont journalisées."
|
"CAT partagé : avec la trace activée, chaque commande envoyée par un client et la réponse donnée sont journalisées.",
|
||||||
|
"CAT partagé : JTDX et WSJT-X ne reçoivent plus d erreur en désactivant un split inexistant ni en réglant le mode d émission — ce qui faisait abandonner une émission à JTDX."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -421,6 +421,17 @@ func (s *Server) handle(line string) (resp string, quit bool) {
|
|||||||
return rprt(0), false
|
return rprt(0), false
|
||||||
}
|
}
|
||||||
s.splitWanted.Store(false)
|
s.splitWanted.Store(false)
|
||||||
|
// Already simplex? Then there is nothing to do and the request is
|
||||||
|
// satisfied. Reporting a failure here is what broke JTDX in "Fake It":
|
||||||
|
// Fake It uses no split, JTDX still sends "S 0" to be sure, and a backend
|
||||||
|
// that cannot SET split answered an error to a request that was already
|
||||||
|
// true. JTDX read that as rig control failing and abandoned the
|
||||||
|
// transmission a second into the frame.
|
||||||
|
//
|
||||||
|
// A refusal is only honest when something actually needed doing.
|
||||||
|
if on, _ := s.rig.Split(); !on {
|
||||||
|
return rprt(0), false
|
||||||
|
}
|
||||||
if err := s.rig.SetSplit(false, 0); err != nil {
|
if err := s.rig.SetSplit(false, 0); err != nil {
|
||||||
s.log("rigctld: split off failed: %v", err)
|
s.log("rigctld: split off failed: %v", err)
|
||||||
return rprt(-9), false
|
return rprt(-9), false
|
||||||
@@ -450,6 +461,34 @@ func (s *Server) handle(line string) (resp string, quit bool) {
|
|||||||
s.log("rigctld: split ON, TX %.0f Hz", hz)
|
s.log("rigctld: split ON, TX %.0f Hz", hz)
|
||||||
return rprt(0), false
|
return rprt(0), false
|
||||||
|
|
||||||
|
case "X", "\\set_split_mode":
|
||||||
|
// "X <mode> <passband>" — the mode of the TRANSMIT VFO.
|
||||||
|
//
|
||||||
|
// Accepted rather than refused. WSJT-X and JTDX send it as part of their
|
||||||
|
// normal setup even in "Fake It", where there is no split and therefore no
|
||||||
|
// second VFO to give a mode to; answering "not implemented" made JTDX give
|
||||||
|
// up on rig control mid-transmission.
|
||||||
|
//
|
||||||
|
// The mode is applied when there IS a split — the transmit VFO is a real
|
||||||
|
// one then. Without split the request has no target and succeeding is the
|
||||||
|
// honest answer: the transmit VFO already has that mode, because it is the
|
||||||
|
// same VFO.
|
||||||
|
if len(args) < 1 {
|
||||||
|
return rprt(-1), false
|
||||||
|
}
|
||||||
|
if on, _ := s.rig.Split(); !on {
|
||||||
|
return rprt(0), false
|
||||||
|
}
|
||||||
|
if err := s.rig.SetMode(args[0]); err != nil {
|
||||||
|
s.log("rigctld: split mode %q failed: %v", args[0], err)
|
||||||
|
return rprt(-9), false
|
||||||
|
}
|
||||||
|
return rprt(0), false
|
||||||
|
case "x", "\\get_split_mode":
|
||||||
|
// Mirrors X: the transmit VFO's mode and passband. Without split that is
|
||||||
|
// simply the current mode.
|
||||||
|
return s.rig.Mode() + "\n2400\n", false
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// A frame ending in ';' is not a rigctl command at all — it is raw rig
|
// A frame ending in ';' is not a rigctl command at all — it is raw rig
|
||||||
// dialect (Kenwood/Elecraft/Yaesu), which means the client is configured
|
// dialect (Kenwood/Elecraft/Yaesu), which means the client is configured
|
||||||
|
|||||||
@@ -50,3 +50,55 @@ func TestSetSplitRefusalIsReported(t *testing.T) {
|
|||||||
t.Errorf("a rig that cannot split answered %q — the client will transmit on the wrong frequency", got)
|
t.Errorf("a rig that cannot split answered %q — the client will transmit on the wrong frequency", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "Fake It" uses NO split, and JTDX still sends "S 0" to make sure. A backend
|
||||||
|
// that cannot SET split used to answer an error to that — a refusal of a
|
||||||
|
// request that was already satisfied — and JTDX read it as rig control failing
|
||||||
|
// and abandoned the transmission a second into a 13.8-second frame.
|
||||||
|
//
|
||||||
|
// Reported against a Yaesu whose backend cannot arm split from software.
|
||||||
|
func TestSplitOffOnASimplexRigSucceeds(t *testing.T) {
|
||||||
|
rig := &fakeRig{freq: 14074000, mode: "FT8", noSplit: true}
|
||||||
|
s := New(0, rig, func(string, ...any) {})
|
||||||
|
|
||||||
|
if got, _ := s.handle("S 0 VFOA"); !strings.HasPrefix(got, "RPRT 0") {
|
||||||
|
t.Errorf("split off on a simplex rig answered %q — nothing needed doing", got)
|
||||||
|
}
|
||||||
|
// And it must not have bothered the rig at all.
|
||||||
|
if len(rig.splitCalls) != 0 {
|
||||||
|
t.Errorf("rig was asked to clear a split it did not have: %v", rig.splitCalls)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arming, however, must still fail loudly: there the request is real and
|
||||||
|
// unmet, and a client that believes it is transmitting up the band while the
|
||||||
|
// radio sits on the DX's frequency is the bug this refusal exists to prevent.
|
||||||
|
if got, _ := s.handle("S 1 VFOB"); !strings.HasPrefix(got, "RPRT 0") {
|
||||||
|
t.Fatalf("arming is deferred to set_split_freq: %q", got)
|
||||||
|
}
|
||||||
|
if got, _ := s.handle("I 14075300.000000"); strings.HasPrefix(got, "RPRT 0") {
|
||||||
|
t.Error("arming split on a backend that cannot must report a failure")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// X (set_split_mode) is sent by WSJT-X and JTDX during ordinary setup, Fake It
|
||||||
|
// included — where there is no second VFO to give a mode to. Answering "not
|
||||||
|
// implemented" made JTDX give up on rig control mid-transmission.
|
||||||
|
func TestSplitModeIsAccepted(t *testing.T) {
|
||||||
|
rig := &fakeRig{freq: 14074000, mode: "FT8"}
|
||||||
|
s := New(0, rig, func(string, ...any) {})
|
||||||
|
|
||||||
|
// No split: nothing to target, and succeeding is honest — the transmit VFO
|
||||||
|
// already has that mode because it is the same VFO.
|
||||||
|
if got, _ := s.handle("X PKTUSB -1"); !strings.HasPrefix(got, "RPRT 0") {
|
||||||
|
t.Errorf("set_split_mode answered %q", got)
|
||||||
|
}
|
||||||
|
if rig.mode != "FT8" {
|
||||||
|
t.Errorf("the mode was changed with no split in force: %q", rig.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading it back must answer a mode and a passband, never an error.
|
||||||
|
got, _ := s.handle("x")
|
||||||
|
if !strings.Contains(got, "FT8") {
|
||||||
|
t.Errorf("get_split_mode = %q, want the current mode", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user