fix(motor-antenna): don't inhibit TX on a band the antenna doesn't cover

The follow filter correctly leaves the antenna put on an un-ticked band, but the
TX-inhibit loop ran independently: it gagged FlexRadio transmit whenever the
antenna reported "moving" (a stray polled Moving flag, or a move finishing from
the previous band), even on a band OpsLog was deliberately not managing. An
operator working 15 m on another antenna had their FT8 cut mid-transmission by
an "antenna moving" interlock.

motorTXInhibitLoop now takes the covered-band set and forces moving=false when
the current rig band isn't in it — un-ticking a band means hands off entirely:
no tune AND no inhibit. Also correct the RCU-01 references to RCU-06 (the actual
controller behind the short 11-byte status frame).
This commit is contained in:
2026-08-06 17:06:49 +02:00
parent 3d603d34aa
commit 01dcd91253
3 changed files with 19 additions and 6 deletions
+14 -3
View File
@@ -14187,7 +14187,7 @@ type motorAntenna interface {
type ubAdapter struct {
c *ultrabeam.Client
// Configured tunable range (MHz) from Settings → Antenna. The follow logic
// uses this as a hard floor/ceiling: some controllers (e.g. an RCU-01 behind
// uses this as a hard floor/ceiling: some controllers (e.g. an RCU-06 behind
// an RS232-to-Ethernet bridge) answer with the short status frame that omits
// FreqMax, which would disable the out-of-range guard and let OpsLog forward
// an un-tunable frequency (80 m → the controller clamps the elements to its
@@ -14456,7 +14456,7 @@ func (a *App) startUltrabeam() {
if s.TXInhibit {
stop := make(chan struct{})
a.motorInhibStop = stop
go a.motorTXInhibitLoop(a.motorAnt, stop)
go a.motorTXInhibitLoop(a.motorAnt, s.Bands, stop)
}
}
@@ -14591,7 +14591,7 @@ func (a *App) applyMotorInhibit(on bool) {
// active; it errs toward SAFE — TX is inhibited while the status reports motion
// OR within a grace window after a commanded move — and always releases the
// inhibit when it stops (so a settings change / shutdown never leaves TX blocked).
func (a *App) motorTXInhibitLoop(c motorAntenna, stop <-chan struct{}) {
func (a *App) motorTXInhibitLoop(c motorAntenna, bands []string, stop <-chan struct{}) {
const grace = 3 * time.Second // cover the poll latency at the very start of a move
ticker := time.NewTicker(300 * time.Millisecond)
defer ticker.Stop()
@@ -14604,6 +14604,17 @@ func (a *App) motorTXInhibitLoop(c motorAntenna, stop <-chan struct{}) {
st := c.Status()
recentCmd := time.Since(time.Unix(0, a.motorMoveCmdNs.Load())) < grace
moving := st.Connected && (st.Moving || recentCmd)
// On a band the antenna doesn't cover, OpsLog never commands a move —
// so it must never gag TX for "antenna moving" either. Otherwise a
// stray polled Moving flag, or a move finishing from the previous band,
// blocks the operator's transmit on a band they work with a different
// antenna (the whole point of un-ticking the band). Hands off means
// hands off: no tune AND no inhibit.
if moving && a.cat != nil {
if rs := a.cat.State(); rs.Connected && rs.FreqHz > 0 && !motorBandAllowed(bands, rs.FreqHz) {
moving = false
}
}
a.applyMotorInhibit(moving)
}
}