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
+2 -47
View File
@@ -59,28 +59,8 @@ type Server struct {
ln net.Listener
conns map[net.Conn]struct{}
closed bool
// Optimistic frequency echo. A sharing client that follows our dial by
// polling get_freq (WSJT-X / JTDX in "Fake It" split) shifts the frequency on
// TX and restores it on RX. Freq() is the last value POLLED from the rig, and
// it lags a set_freq by up to a poll cycle (~100-200 ms). In that window the
// client — no longer transmitting — reads back the still-shifted frequency,
// mistakes it for a manual QSY and adopts it, so every over creeps the dial by
// the shift amount and it never comes back. Echoing the last commanded
// frequency until the rig confirms it (or a short deadline passes) closes the
// window: the client always reads exactly what it just set.
echoMu sync.Mutex
echoHz int64
echoAt time.Time
echoWant bool
}
// freqEchoTTL caps how long a commanded frequency is echoed when the rig never
// reports it back (e.g. it rounded to a coarser step). Long enough to cover a
// poll cycle with margin, short enough that a genuine knob turn during the
// window surfaces quickly.
const freqEchoTTL = 2 * time.Second
func New(port int, rig Rig, logf func(string, ...any)) *Server {
if port <= 0 || port > 65535 {
port = 4532 // the rigctld default every client pre-fills
@@ -213,7 +193,7 @@ func (s *Server) handle(line string) (resp string, quit bool) {
return "", true
case "f", "\\get_freq":
return fmt.Sprintf("%d\n", s.reportedFreq()), false
return fmt.Sprintf("%d\n", s.rig.Freq()), false
case "F", "\\set_freq":
if len(args) < 1 {
return rprt(-1), false
@@ -230,7 +210,6 @@ func (s *Server) handle(line string) (resp string, quit bool) {
s.log("rigctld: set_freq %d failed: %v", hz, err)
return rprt(-9), false
}
s.noteSetFreq(hz)
return rprt(0), false
case "m", "\\get_mode":
@@ -284,7 +263,7 @@ func (s *Server) handle(line string) (resp string, quit bool) {
case "i", "\\get_split_freq":
_, tx := s.rig.Split()
if tx <= 0 {
tx = s.reportedFreq()
tx = s.rig.Freq()
}
return fmt.Sprintf("%d\n", tx), false
case "I", "\\set_split_freq":
@@ -300,30 +279,6 @@ func (s *Server) handle(line string) (resp string, quit bool) {
func rprt(code int) string { return fmt.Sprintf("RPRT %d\n", code) }
// noteSetFreq records a frequency a client just commanded, so the next reads
// echo it back until the rig confirms the tune.
func (s *Server) noteSetFreq(hz int64) {
s.echoMu.Lock()
s.echoHz, s.echoAt, s.echoWant = hz, time.Now(), true
s.echoMu.Unlock()
}
// reportedFreq is what get_freq answers: the last commanded frequency while the
// rig is still catching up to it, otherwise the live polled value. See echoWant.
func (s *Server) reportedFreq() int64 {
live := s.rig.Freq()
s.echoMu.Lock()
defer s.echoMu.Unlock()
if s.echoWant {
if live == s.echoHz || time.Since(s.echoAt) > freqEchoTTL {
s.echoWant = false // rig confirmed the tune, or we waited long enough
return live
}
return s.echoHz
}
return live
}
// stripVFOArg drops a leading VFO name from a command's arguments.
//
// Hamlib has two dialects. In the plain one a client sends "F 14074000"; in VFO