feat(rotator): add Hy-Gain DCU-1 backend (RotorCard DXA, Rotor-EZ, Green Heron)
New internal/rotator/dcu1 client speaking the DCU-1 command set (AP1nnn / AM1 to go-to, AI1 to read bearing), over a serial COM port (4800 baud default) or a raw TCP serial-over-IP bridge. Azimuth-only; Stop re-commands the current bearing since the base set has no stop opcode. Wired through app.go (normRotorType, default port 4001, GoTo/Heading/Stop/test switches, park returns the same "PstRotator only" note as ARCO) and exposed in the rotor settings as "Hy-Gain DCU-1 (RotorCard DXA, Rotor-EZ, Green Heron)" with the ARCO's serial/TCP transport chooser and a bilingual hint. Protocol is implemented from the standard DCU-1 spec and still needs field confirmation against a real RotorCard DXA.
This commit is contained in:
@@ -52,6 +52,7 @@ import (
|
||||
"hamlog/internal/qso"
|
||||
"hamlog/internal/relaydev"
|
||||
"hamlog/internal/rigctld"
|
||||
"hamlog/internal/rotator/dcu1"
|
||||
"hamlog/internal/rotator/gs232"
|
||||
"hamlog/internal/rotator/pst"
|
||||
"hamlog/internal/rotgenius"
|
||||
@@ -13477,7 +13478,7 @@ type logicalRotor struct {
|
||||
|
||||
// normRotorType clamps a rotor type to a known backend.
|
||||
func normRotorType(t string) string {
|
||||
if t == "rotgenius" || t == "arco" {
|
||||
if t == "rotgenius" || t == "arco" || t == "dcu1" {
|
||||
return t
|
||||
}
|
||||
return "pst"
|
||||
@@ -13490,6 +13491,8 @@ func rotatorDefaultPort(typ string) int {
|
||||
return 9006 // 4O3A native default
|
||||
case "arco":
|
||||
return 4001 // placeholder — the real number is set in ARCO's LAN menu
|
||||
case "dcu1":
|
||||
return 4001 // only used with a serial-over-IP bridge; DCU-1 has no standard
|
||||
default:
|
||||
return 12000 // PstRotator UDP
|
||||
}
|
||||
@@ -13682,6 +13685,16 @@ func arcoClient(l rotorLink) *gs232.Client {
|
||||
return gs232.New(l.Host, l.Port)
|
||||
}
|
||||
|
||||
// dcu1Client builds the Hy-Gain DCU-1 client for a rotor's transport: the
|
||||
// controller's COM port (the usual case — RotorCard DXA, Green Heron, Rotor-EZ)
|
||||
// or a serial-over-IP bridge on TCP.
|
||||
func dcu1Client(l rotorLink) *dcu1.Client {
|
||||
if l.Transport == "serial" {
|
||||
return dcu1.NewSerial(l.ComPort, l.Baud)
|
||||
}
|
||||
return dcu1.New(l.Host, l.Port)
|
||||
}
|
||||
|
||||
// RotatorHeading is the live antenna heading for the status bar and compass.
|
||||
type RotatorHeading struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
@@ -13748,6 +13761,16 @@ func (a *App) GetRotatorHeading() RotatorHeading {
|
||||
base.Azimuth = az
|
||||
base.Raw = raw
|
||||
return base
|
||||
case "dcu1":
|
||||
az, raw, herr := dcu1Client(link).Heading()
|
||||
if herr != nil {
|
||||
base.Raw = herr.Error()
|
||||
return base
|
||||
}
|
||||
base.OK = true
|
||||
base.Azimuth = az
|
||||
base.Raw = raw
|
||||
return base
|
||||
default:
|
||||
az, raw, herr := pst.New(link.Host, link.Port).Heading()
|
||||
if herr != nil {
|
||||
@@ -13774,6 +13797,8 @@ func (a *App) RotatorGoTo(az int, el int) error {
|
||||
return rotgenius.New(link.Host, link.Port).GoTo(link.Num, az)
|
||||
case "arco":
|
||||
return arcoClient(link).GoTo(az)
|
||||
case "dcu1":
|
||||
return dcu1Client(link).GoTo(az)
|
||||
default:
|
||||
return pst.New(link.Host, link.Port).GoTo(az, link.HasElevation, el)
|
||||
}
|
||||
@@ -13791,6 +13816,8 @@ func (a *App) RotatorStop() error {
|
||||
return rotgenius.New(link.Host, link.Port).Stop()
|
||||
case "arco":
|
||||
return arcoClient(link).Stop()
|
||||
case "dcu1":
|
||||
return dcu1Client(link).Stop()
|
||||
default:
|
||||
return pst.New(link.Host, link.Port).Stop()
|
||||
}
|
||||
@@ -13809,6 +13836,8 @@ func (a *App) RotatorPark() error {
|
||||
return fmt.Errorf("park is a PstRotator feature; not available on the Rotator Genius")
|
||||
case "arco":
|
||||
return fmt.Errorf("park is a PstRotator feature; not available over the ARCO GS-232 link")
|
||||
case "dcu1":
|
||||
return fmt.Errorf("park is a PstRotator feature; not available over the DCU-1 link")
|
||||
default:
|
||||
return pst.New(link.Host, link.Port).Park()
|
||||
}
|
||||
@@ -13847,6 +13876,14 @@ func testRotorLink(l rotorLink) error {
|
||||
// GS-232 — without moving the antenna.
|
||||
_, _, err := arcoClient(l).Heading()
|
||||
return err
|
||||
case "dcu1":
|
||||
if l.Transport == "serial" && strings.TrimSpace(l.ComPort) == "" {
|
||||
return fmt.Errorf("select the DCU-1 controller's COM port first")
|
||||
}
|
||||
// A bearing query (AI1) proves the link and the DCU-1 command set without
|
||||
// moving the antenna.
|
||||
_, _, err := dcu1Client(l).Heading()
|
||||
return err
|
||||
default:
|
||||
return pst.New(l.Host, l.Port).GoTo(0, false, -1)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user