fix(sat): both antennas on both satellite slices

A slice has an rxant and a txant, and each pair belongs to that slice's
own band. Only two of the four were being set — the downlink's receive
antenna and the uplink's transmit one — so the downlink slice was left
with an empty txant. It never keys, so nothing was wrong on the air, but
the slice was half-configured: move transmit focus to it and the radio
uses whatever antenna it happened to be left on. Reported with XVTA on
2 m and XVTB on 70 cm, where the 70 cm slice showed one antenna and the
2 m slice showed two.

SatAntennas now takes all four, and the late-slice replay in
adoptSatSlice hands the uplink its own pair instead of putting the
transmit antenna on both of its ports.
This commit is contained in:
2026-09-10 17:48:28 +02:00
parent 9dcab0568b
commit 6f9b996db8
5 changed files with 58 additions and 35 deletions
+32 -21
View File
@@ -155,11 +155,13 @@ func (f *Flex) adoptSatSlice(role string, idx int) {
// antenna and the tone are all sent ONCE — the step only re-sends
// frequencies.
f.mu.Lock()
mode, ant, tone := f.satUpMode, f.satTXAnt, f.satTone
mode, rxAnt, txAnt, tone := f.satUpMode, f.satUpRX, f.satUpTX, f.satTone
f.mu.Unlock()
if strings.TrimSpace(ant) != "" {
f.send(fmt.Sprintf("slice s %d txant=%s", idx, ant))
f.send(fmt.Sprintf("slice s %d rxant=%s", idx, ant))
if strings.TrimSpace(rxAnt) != "" {
f.send(fmt.Sprintf("slice s %d rxant=%s", idx, rxAnt))
}
if strings.TrimSpace(txAnt) != "" {
f.send(fmt.Sprintf("slice s %d txant=%s", idx, txAnt))
}
if strings.TrimSpace(mode) != "" {
f.satMode(idx, mode, 0)
@@ -259,7 +261,7 @@ func (f *Flex) SatReceiveHz() (int64, error) {
return s.freqHz, nil
}
// SatAntennas selects the antenna each satellite slice uses.
// SatAntennas selects both antennas of each satellite slice.
//
// The two slices are on two different bands — a V/U bird receives on 70 cm and
// transmits on 2 m, a U/V one does the reverse — so they cannot share one
@@ -274,30 +276,39 @@ func (f *Flex) SatReceiveHz() (int64, error) {
// Empty strings are left alone: an operator who has configured 2 m and not
// 70 cm should keep whatever the radio already had on the other side rather
// than have it cleared.
func (f *Flex) SatAntennas(rxAnt, txAnt string) error {
// A slice has an rxant AND a txant, and both belong to the slice's own band.
// Only two of the four were being set — the downlink's receive antenna and
// the uplink's transmit one — which left the downlink slice with an empty
// txant. It never keys, so nothing was wrong on the air, but the slice was
// half-configured: move transmit focus to it and the radio uses whatever
// antenna it was last left on.
func (f *Flex) SatAntennas(downRX, downTX, upRX, upTX string) error {
f.mu.Lock()
rx, tx := f.satRX, f.satTX
connected := f.conn != nil
// Remembered so a slice that is reported late still gets its antenna.
f.satRXAnt, f.satTXAnt = rxAnt, txAnt
// Remembered so a slice that is reported late still gets its antennas.
f.satDownRX, f.satDownTX = downRX, downTX
f.satUpRX, f.satUpTX = upRX, upTX
f.mu.Unlock()
if !connected {
return fmt.Errorf("flex: not connected")
}
// The downlink slice is the one being listened to, so it takes the receive
// antenna; the uplink slice is the one keyed, so it takes the transmit one.
if rx >= 0 && strings.TrimSpace(rxAnt) != "" {
f.send(fmt.Sprintf("slice s %d rxant=%s", rx, rxAnt))
applog.Printf("flex: satellite downlink slice %d on antenna %s", rx, rxAnt)
}
if tx >= 0 && strings.TrimSpace(txAnt) != "" {
f.send(fmt.Sprintf("slice s %d txant=%s", tx, txAnt))
// A transmit slice also has to HEAR its own band on some radios, and a
// transverter port is the only thing connected to it. Setting the
// receive antenna to match costs nothing when it is already right.
f.send(fmt.Sprintf("slice s %d rxant=%s", tx, txAnt))
applog.Printf("flex: satellite uplink slice %d on antenna %s", tx, txAnt)
set := func(idx int, which, rxAnt, txAnt string) {
if idx < 0 {
return
}
if strings.TrimSpace(rxAnt) != "" {
f.send(fmt.Sprintf("slice s %d rxant=%s", idx, rxAnt))
}
if strings.TrimSpace(txAnt) != "" {
f.send(fmt.Sprintf("slice s %d txant=%s", idx, txAnt))
}
if strings.TrimSpace(rxAnt) != "" || strings.TrimSpace(txAnt) != "" {
applog.Printf("flex: satellite %s slice %d rx=%s tx=%s", which, idx, rxAnt, txAnt)
}
}
set(rx, "downlink", downRX, downTX)
set(tx, "uplink", upRX, upTX)
return nil
}