feat(rotator): multiple rotors of any type, plus a CAT PTT hotkey
Settings -> Rotator is now a list like the amplifiers: add/remove rotors, mix PstRotator / Rotator Genius / ARCO, and a Rotator Genius can drive both its ports (one entry = two rotors). The compass gains a rotor selector, and a per-rotor motorized-antenna flag so the boom/pattern paths show only for the rotor carrying the Ultrabeam/SteppIR. Also in this batch: a keyboard PTT hotkey (hold-to-talk or toggle, reusing the audio PTT method with a CAT fallback), and TCI spot push to the panadapter with click-to-fill of the callsign.
This commit is contained in:
@@ -28,6 +28,12 @@ type TCI struct {
|
||||
digitalDefault string // surfaced when the rig reports a digital mode (FT8/…)
|
||||
spotsEnabled bool // mirror cluster spots onto the TCI panorama
|
||||
|
||||
// OnSpotClick is called when the user clicks one of our spots on the TCI
|
||||
// panorama (callsign + freq), so the host can fill the entry form. Set before
|
||||
// Connect. Mirrors the FlexRadio panadapter-click flow.
|
||||
OnSpotClick func(callsign string, freqHz int64)
|
||||
unhandledSeen map[string]bool // log each unknown TCI message type once
|
||||
|
||||
mu sync.Mutex // guards conn + writes + state
|
||||
conn *websocket.Conn
|
||||
dialCancel context.CancelFunc // cancels an in-flight Connect dial (Interrupt/Stop)
|
||||
@@ -294,6 +300,32 @@ func (t *TCI) handle(msg string) {
|
||||
if get(0) == "0" {
|
||||
t.tx = get(1) == "true"
|
||||
}
|
||||
default:
|
||||
lname := strings.ToLower(name)
|
||||
// A click on one of our panorama spots comes back as a "spot…" message.
|
||||
// The exact shape isn't well documented, so read a callsign (+ freq) from
|
||||
// any spot-related message and fire the callback. The log line lets us pin
|
||||
// the real format against a radio if it differs.
|
||||
if strings.HasPrefix(lname, "spot") {
|
||||
debugLog.Printf("TCI: spot event: %s", msg)
|
||||
call := get(0)
|
||||
hz, _ := strconv.ParseInt(get(2), 10, 64)
|
||||
if call != "" && t.OnSpotClick != nil {
|
||||
cb := t.OnSpotClick
|
||||
go cb(call, hz)
|
||||
}
|
||||
return
|
||||
}
|
||||
// Log every OTHER unknown message TYPE once, so the protocol (incl. any
|
||||
// spot-click notification named differently) is discoverable from the log
|
||||
// without flooding it with the frequent streamed messages.
|
||||
if t.unhandledSeen == nil {
|
||||
t.unhandledSeen = map[string]bool{}
|
||||
}
|
||||
if !t.unhandledSeen[lname] {
|
||||
t.unhandledSeen[lname] = true
|
||||
debugLog.Printf("TCI: (unhandled once) %s", msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user