fix(catshare): revert FT8 freq echo — it blocked JTDX transmit

The optimistic get_freq echo added in 0.23.5 (to stop the "Fake It" dial creep)
stopped JTDX/WSJT-X from going into transmit: the client reads frequency back
during its TX sequence, and echoing the commanded value instead of the rig's
live value interfered with it. No-TX is far worse than a 0.5 kHz drift, so revert
to reporting the rig's live frequency. get_freq/get_split_freq return s.rig.Freq()
again; the echo state, noteSetFreq, reportedFreq and the drift test are removed.
This commit is contained in:
2026-08-05 09:26:33 +02:00
parent a48fa0b2e7
commit b9ea077a64
3 changed files with 7 additions and 107 deletions
+1 -58
View File
@@ -20,7 +20,6 @@ type fakeRig struct {
setFreqs []int64
setModes []string
failSet bool
lagSet bool // record the command but do not move freq — simulate poll lag
}
func (f *fakeRig) Freq() int64 { f.mu.Lock(); defer f.mu.Unlock(); return f.freq }
@@ -32,10 +31,8 @@ func (f *fakeRig) SetFreq(hz int64) error {
if f.failSet {
return fmt.Errorf("rig refused")
}
f.freq = hz
f.setFreqs = append(f.setFreqs, hz)
if !f.lagSet {
f.freq = hz
}
return nil
}
func (f *fakeRig) SetMode(m string) error {
@@ -141,60 +138,6 @@ func TestHandleReportsBackendFailure(t *testing.T) {
}
}
// The FT8 "Fake It" drift. JTDX/WSJT-X shift the dial down on TX and restore it
// on RX, and they follow the dial by polling get_freq. Our Freq() is the last
// POLLED value and lags a set_freq by a poll cycle, so right after the restore
// the client used to read the still-shifted frequency, take it for a manual QSY
// and adopt it — the dial crept down every over and never came back. get_freq
// now echoes the last commanded frequency until the rig confirms it.
func TestFakeItSplitDoesNotDriftTheDial(t *testing.T) {
// lagSet: SetFreq only records the command; we drive the poll catch-up by hand
// to land inside the exact window the drift lived in.
rig := &fakeRig{freq: 14074000, lagSet: true}
s := New(0, rig, nil)
if got, _ := s.handle("f"); got != "14074000\n" {
t.Fatalf("baseline get_freq = %q, want 14074000", got)
}
setFreq := func(hz int64) { rig.mu.Lock(); rig.freq = hz; rig.mu.Unlock() }
for cycle := 0; cycle < 5; cycle++ {
// TX: the client shifts the dial down for the over.
if got, _ := s.handle("F 14073500"); got != "RPRT 0\n" {
t.Fatalf("cycle %d TX set_freq = %q", cycle, got)
}
// Mid-TX, before the rig reports the move, the client reads back exactly
// what it commanded — not the stale 14074000.
if got, _ := s.handle("f"); got != "14073500\n" {
t.Fatalf("cycle %d TX get_freq = %q, want commanded 14073500", cycle, got)
}
setFreq(14073500) // poll catches up to the shifted dial
// RX: the client restores the dial.
if got, _ := s.handle("F 14074000"); got != "RPRT 0\n" {
t.Fatalf("cycle %d RX set_freq = %q", cycle, got)
}
// The rig has not yet reported the restore (still 14073500). Before the fix
// this returned 14073500 and JTDX adopted it — the cumulative drift. Now it
// echoes the restore, so the dial holds.
if got, _ := s.handle("f"); got != "14074000\n" {
t.Fatalf("cycle %d RX get_freq = %q, want restored 14074000 — dial drifted", cycle, got)
}
setFreq(14074000) // poll catches up to the restored dial
}
// Once a poll confirms the commanded dial, the echo is released (in the field
// the client polls continuously, so this happens within a cycle).
if got, _ := s.handle("f"); got != "14074000\n" {
t.Fatalf("confirming get_freq = %q, want 14074000", got)
}
// A genuine knob turn now surfaces at once.
setFreq(14075000)
if got, _ := s.handle("f"); got != "14075000\n" {
t.Errorf("manual QSY get_freq = %q, want 14075000 — echo hid a real move", 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