fix: solve issue with Antenna Genius for remote operations
This commit is contained in:
+45
-13
@@ -12,18 +12,38 @@ import (
|
||||
"go.bug.st/serial"
|
||||
)
|
||||
|
||||
// IcomSerial controls an Icom transceiver over its USB/serial CI-V port (local
|
||||
// control). It speaks the shared civ protocol, so when the network backend
|
||||
// (icomnet) is added it will reuse the same encode/decode — only the transport
|
||||
// changes. Implements Backend; all methods run on the Manager's CAT goroutine,
|
||||
// so the port is accessed single-threaded (no locking needed).
|
||||
// civTransport is everything IcomSerial needs from its underlying link. It's
|
||||
// satisfied directly by a go.bug.st serial.Port (USB, local control) and, for
|
||||
// remote control, by the icomnet UDP stream which presents the tunnelled CI-V
|
||||
// byte stream as plain Read/Write (there SetDTR/SetRTS/SetReadTimeout are
|
||||
// no-ops). Abstracting it here means the whole IcomController surface — DSP,
|
||||
// scope, RIT, CW — is reused unchanged over the network; only `open` differs.
|
||||
type civTransport interface {
|
||||
Read(p []byte) (int, error)
|
||||
Write(p []byte) (int, error)
|
||||
Close() error
|
||||
SetReadTimeout(time.Duration) error
|
||||
SetDTR(bool) error
|
||||
SetRTS(bool) error
|
||||
}
|
||||
|
||||
// IcomSerial controls an Icom transceiver over the shared civ protocol. The
|
||||
// transport is pluggable via `open`: NewIcomSerial opens a USB/serial port;
|
||||
// NewIcomNet (later) returns one configured with a network transport. Implements
|
||||
// Backend; all methods run on the Manager's CAT goroutine, so the port is
|
||||
// accessed single-threaded (no locking needed).
|
||||
type IcomSerial struct {
|
||||
portName string
|
||||
baud int
|
||||
rigAddr byte // rig's CI-V address (IC-7610 default 0x98)
|
||||
digital string // mode to command for DATA (FT8/RTTY/…)
|
||||
|
||||
port serial.Port
|
||||
// open dials the underlying link (serial port or network stream). Set by the
|
||||
// constructor; called by Connect. Blocks until connected (the network opener
|
||||
// performs the full UDP handshake + login before returning).
|
||||
open func() (civTransport, error)
|
||||
|
||||
port civTransport
|
||||
model string
|
||||
|
||||
// I/O routing. A single reader goroutine owns port.Read and dispatches every
|
||||
@@ -82,7 +102,7 @@ func NewIcomSerial(portName string, baud, civAddr int, digitalDefault string) *I
|
||||
if digitalDefault == "" {
|
||||
digitalDefault = "FT8"
|
||||
}
|
||||
return &IcomSerial{
|
||||
b := &IcomSerial{
|
||||
portName: portName,
|
||||
baud: baud,
|
||||
rigAddr: byte(civAddr),
|
||||
@@ -90,20 +110,32 @@ func NewIcomSerial(portName string, baud, civAddr int, digitalDefault string) *I
|
||||
model: "Icom",
|
||||
scopeFixed: true, // rigs default to a fixed-span scope
|
||||
}
|
||||
// USB/serial transport opener.
|
||||
b.open = func() (civTransport, error) {
|
||||
if b.portName == "" {
|
||||
return nil, fmt.Errorf("no serial port configured")
|
||||
}
|
||||
p, err := serial.Open(b.portName, &serial.Mode{BaudRate: b.baud})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open %s @ %d baud: %w", b.portName, b.baud, err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *IcomSerial) Name() string { return "icom" }
|
||||
|
||||
func (b *IcomSerial) Connect() error {
|
||||
if b.portName == "" {
|
||||
return fmt.Errorf("no serial port configured")
|
||||
if b.open == nil {
|
||||
return fmt.Errorf("no transport configured")
|
||||
}
|
||||
port, err := serial.Open(b.portName, &serial.Mode{BaudRate: b.baud})
|
||||
port, err := b.open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("open %s @ %d baud: %w", b.portName, b.baud, err)
|
||||
return err
|
||||
}
|
||||
// Short read timeout so recv() polls in a tight loop without blocking the
|
||||
// CAT goroutine when the rig is silent.
|
||||
// CAT goroutine when the rig is silent. (No-op on the network transport.)
|
||||
_ = port.SetReadTimeout(60 * time.Millisecond)
|
||||
// Deassert DTR/RTS. Icom USB rigs (IC-7610, IC-7300…) let "USB SEND" and
|
||||
// "USB Keying (CW)" be mapped to the RTS or DTR line: if the port opens with
|
||||
@@ -312,7 +344,7 @@ func (b *IcomSerial) recv(timeout time.Duration, match func(civ.Decoded) bool) (
|
||||
// frames and routes each: our own echoes are dropped, spectrum-scope frames
|
||||
// (0x27) go to specCh, everything else (control replies, acks, unsolicited
|
||||
// transceive updates) goes to respCh. It exits when the port is closed.
|
||||
func (b *IcomSerial) reader(port serial.Port, done chan struct{}) {
|
||||
func (b *IcomSerial) reader(port civTransport, done chan struct{}) {
|
||||
defer close(done)
|
||||
tmp := make([]byte, 512)
|
||||
var rx []byte
|
||||
|
||||
Reference in New Issue
Block a user