feat: amplifier band-follow — answer the amp's frequency polls
On its CAT/AUX connector an ACOM is the MASTER: it polls a transceiver and changes band from the reply, so nothing can be pushed to it. internal/catemu answers those polls on a second serial port, in ACOM command set 5 (Kenwood / Elecraft): FA;, FB;, IF; and ID;, and nothing else — a wrong-length reply is worse than none, it desynchronises the amp's parser for the following poll. Also offered for SPE. Some amps do not poll at all but read the radio↔PC CAT line in parallel; those never hear a responder, so an optional unprompted send (500/1000 ms) covers them. The TX frequency is what is sent: in split the amp must be tuned where we transmit. Frame lengths are pinned by tests — the failure they guard against is silent and only shows up as an amp that mistunes. Untested on hardware.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package catemu
|
||||
|
||||
import "testing"
|
||||
|
||||
// The reply LENGTHS are the contract: an amplifier parses these frames by
|
||||
// fixed offsets, so a frame one character short desynchronises its parser for
|
||||
// every following poll. Pin them.
|
||||
func TestReplyShapes(t *testing.T) {
|
||||
s := New(Config{ComPort: "COM99", Baud: 9600}, nil)
|
||||
s.SetFrequency(14025000)
|
||||
s.SetMode("CW")
|
||||
|
||||
if got := s.ifFrame(14025000); len(got) != 38 {
|
||||
t.Errorf("IF frame is %d chars, want 38: %q", len(got), got)
|
||||
}
|
||||
// TS-2000 IF: "IF" then the 11-digit frequency in Hz.
|
||||
if got := s.ifFrame(14025000); got[:13] != "IF00014025000" {
|
||||
t.Errorf("IF frame frequency field = %q", got[:13])
|
||||
}
|
||||
if got := s.ifFrame(14025000); got[len(got)-1] != ';' {
|
||||
t.Errorf("IF frame not terminated by ';': %q", got)
|
||||
}
|
||||
// Mode digit sits at P9, right after freq(11)+step(4)+rit(6)+3+2+1 — index 29 in the frame.
|
||||
if got := s.ifFrame(14025000)[29]; got != '3' {
|
||||
t.Errorf("CW mode digit = %q, want '3'", got)
|
||||
}
|
||||
s.SetMode("FT8") // data rides on SSB as far as an amp cares
|
||||
if got := s.ifFrame(14074000)[29]; got != '2' {
|
||||
t.Errorf("FT8 mode digit = %q, want '2'", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModeDigit(t *testing.T) {
|
||||
cases := map[string]byte{
|
||||
"CW": '3', "CW-R": '3', "LSB": '1', "USB": '2', "SSB": '2',
|
||||
"FM": '4', "AM": '5', "RTTY": '6', "": '2', "FT8": '2',
|
||||
}
|
||||
for mode, want := range cases {
|
||||
s := New(Config{}, nil)
|
||||
s.SetMode(mode)
|
||||
if got := s.modeDigit(); got != want {
|
||||
t.Errorf("mode %q → %q, want %q", mode, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user