fix(catshare): echo PTT state so JTDX doesn't drop transmit
JTDX polls get_ptt DURING transmit to confirm the rig is keyed (WSJT-X does not, which is why it worked and JTDX cut after ~2 s). We answered a blanket "0" (RX), so JTDX concluded PTT had failed and dropped the over. get_ptt now echoes the last PTT state commanded via set_ptt — always consistent with the client's own command. Trace showed T 1 → three t→"0" polls → T 0.
This commit is contained in:
@@ -36,6 +36,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -59,6 +60,13 @@ type Server struct {
|
||||
ln net.Listener
|
||||
conns map[net.Conn]struct{}
|
||||
closed bool
|
||||
|
||||
// ptt mirrors the last PTT state a client commanded via set_ptt. WSJT-X/JTDX
|
||||
// poll get_ptt DURING transmit to confirm the rig is keyed; if get_ptt reads
|
||||
// RX they conclude PTT failed and abort the over after a second or two. We
|
||||
// don't read PTT back from every backend, so echo what the client last set —
|
||||
// always consistent with its own command, and enough to satisfy the check.
|
||||
ptt atomic.Bool
|
||||
}
|
||||
|
||||
func New(port int, rig Rig, logf func(string, ...any)) *Server {
|
||||
@@ -228,10 +236,12 @@ func (s *Server) handle(line string) (resp string, quit bool) {
|
||||
return rprt(0), false
|
||||
|
||||
case "t", "\\get_ptt":
|
||||
// We do not read PTT back from every backend, and answering "transmitting"
|
||||
// wrongly would make a client hold off for ever. Reporting RX is the safe
|
||||
// direction: the worst case is a client that transmits when we said it
|
||||
// could, which is what it was going to do anyway.
|
||||
// Echo the last commanded PTT state. WSJT-X/JTDX poll this WHILE
|
||||
// transmitting to confirm the rig is keyed; answering a blanket "0" (RX)
|
||||
// made them decide PTT had failed and abort the over after ~1-2 s.
|
||||
if s.ptt.Load() {
|
||||
return "1\n", false
|
||||
}
|
||||
return "0\n", false
|
||||
case "T", "\\set_ptt":
|
||||
if len(args) < 1 {
|
||||
@@ -242,6 +252,7 @@ func (s *Server) handle(line string) (resp string, quit bool) {
|
||||
s.log("rigctld: set_ptt %v failed: %v", on, err)
|
||||
return rprt(-9), false
|
||||
}
|
||||
s.ptt.Store(on)
|
||||
return rprt(0), false
|
||||
|
||||
case "v", "\\get_vfo":
|
||||
|
||||
@@ -138,6 +138,28 @@ func TestHandleReportsBackendFailure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// get_ptt must echo the last commanded PTT state. WSJT-X/JTDX poll get_ptt while
|
||||
// transmitting to confirm the rig is keyed; a blanket "0" made them decide PTT
|
||||
// had failed and abort the over after a second or two.
|
||||
func TestGetPTTEchoesSetPTT(t *testing.T) {
|
||||
s := New(0, &fakeRig{}, nil)
|
||||
if got, _ := s.handle("t"); got != "0\n" {
|
||||
t.Fatalf("initial get_ptt = %q, want 0", got)
|
||||
}
|
||||
if got, _ := s.handle("T 1"); got != "RPRT 0\n" {
|
||||
t.Fatalf("set_ptt 1 = %q, want RPRT 0", got)
|
||||
}
|
||||
if got, _ := s.handle("t"); got != "1\n" {
|
||||
t.Fatalf("get_ptt after T 1 = %q, want 1 — client would abort TX", got)
|
||||
}
|
||||
if got, _ := s.handle("T 0"); got != "RPRT 0\n" {
|
||||
t.Fatalf("set_ptt 0 = %q, want RPRT 0", got)
|
||||
}
|
||||
if got, _ := s.handle("t"); got != "0\n" {
|
||||
t.Fatalf("get_ptt after T 0 = %q, want 0", got)
|
||||
}
|
||||
}
|
||||
|
||||
// dump_state is parsed POSITIONALLY by Hamlib clients: WSJT-X reads the first
|
||||
// line as the protocol version and refuses to continue if the block is short or
|
||||
// misshapen. Pinning its shape is what stops a well-meaning edit from silently
|
||||
|
||||
Reference in New Issue
Block a user