feat: Added Antenna selection to Flex FlexPanel
feat: New settings flexradio to select antennas per band
This commit is contained in:
+58
-21
@@ -33,23 +33,23 @@ type Flex struct {
|
||||
model string
|
||||
gotHandle bool
|
||||
|
||||
slices map[int]*flexSlice
|
||||
tx flexTX // transmit/ATU state pushed by the radio (FlexRadio tab)
|
||||
amp flexAmp // external amplifier (PowerGenius XL) state
|
||||
txSetAt map[string]time.Time // status field → when WE last set it (ignore the radio's lagging echo briefly)
|
||||
lastStateSig string // last logged derived-state signature (log only on change)
|
||||
boundClientID string // GUI client (SmartSDR) we bound to; "" until bound. Binding lets this non-GUI client receive GUI-tied data (CW pitch/speed, break-in delay, RF power).
|
||||
slices map[int]*flexSlice
|
||||
tx flexTX // transmit/ATU state pushed by the radio (FlexRadio tab)
|
||||
amp flexAmp // external amplifier (PowerGenius XL) state
|
||||
txSetAt map[string]time.Time // status field → when WE last set it (ignore the radio's lagging echo briefly)
|
||||
lastStateSig string // last logged derived-state signature (log only on change)
|
||||
boundClientID string // GUI client (SmartSDR) we bound to; "" until bound. Binding lets this non-GUI client receive GUI-tied data (CW pitch/speed, break-in delay, RF power).
|
||||
|
||||
// Live meters streamed over UDP (VITA-49). meterMeta is the definitions
|
||||
// pushed over TCP; meterVal the latest scaled values keyed by meter id.
|
||||
udpConn *net.UDPConn
|
||||
meterMeta map[int]meterInfo
|
||||
meterVal map[int]float64
|
||||
meterSub map[int]bool // ids we've already sent "sub meter <id>" for
|
||||
meterLogAt time.Time // throttle for value logging
|
||||
vitaSeen int // count of UDP datagrams (first few logged for diag)
|
||||
meterRawLogged bool // log the first raw meter-definition status once
|
||||
txRawLogged bool // log the first raw transmit status once (field-name audit)
|
||||
udpConn *net.UDPConn
|
||||
meterMeta map[int]meterInfo
|
||||
meterVal map[int]float64
|
||||
meterSub map[int]bool // ids we've already sent "sub meter <id>" for
|
||||
meterLogAt time.Time // throttle for value logging
|
||||
vitaSeen int // count of UDP datagrams (first few logged for diag)
|
||||
meterRawLogged bool // log the first raw meter-definition status once
|
||||
txRawLogged bool // log the first raw transmit status once (field-name audit)
|
||||
|
||||
spotsEnabled bool // push cluster spots + manage the panadapter overlay
|
||||
spotIdx map[int]bool // panadapter spot indices currently known to the radio
|
||||
@@ -76,14 +76,18 @@ type flexSlice struct {
|
||||
mute bool // RX audio muted
|
||||
nb bool // noise blanker
|
||||
nbLevel int
|
||||
nr bool // noise reduction
|
||||
nr bool // noise reduction
|
||||
nrLevel int
|
||||
anf bool // auto notch filter
|
||||
anf bool // auto notch filter
|
||||
anfLevel int
|
||||
apf bool // CW audio peaking filter
|
||||
apf bool // CW audio peaking filter
|
||||
apfLevel int
|
||||
filterLo int // slice filter low cut (Hz)
|
||||
filterHi int // slice filter high cut (Hz)
|
||||
filterLo int // slice filter low cut (Hz)
|
||||
filterHi int // slice filter high cut (Hz)
|
||||
rxAnt string // selected RX antenna (e.g. ANT1, ANT2, RX_A)
|
||||
txAnt string // selected TX antenna
|
||||
antList []string // antennas valid for this slice (RX side)
|
||||
txAntList []string // antennas valid for TX
|
||||
}
|
||||
|
||||
// flexTX mirrors the radio's transmit/ATU/interlock objects (the SmartSDR-style
|
||||
@@ -184,8 +188,8 @@ func (f *Flex) Connect() error {
|
||||
// Identify ourselves in SmartSDR's client list, then stream slice + transmit
|
||||
// (TX/split) status. Command names per the SmartSDR TCP/IP API docs.
|
||||
f.send("client program OpsLog")
|
||||
f.send("sub slice all") // slice/receiver: freq, mode, AGC, NB/NR/ANF, audio…
|
||||
f.send("sub tx all") // transmit: rfpower, tunepower, vox, processor, mon, mic
|
||||
f.send("sub slice all") // slice/receiver: freq, mode, AGC, NB/NR/ANF, audio…
|
||||
f.send("sub tx all") // transmit: rfpower, tunepower, vox, processor, mon, mic
|
||||
f.send("sub atu all") // antenna-tuner status + memories
|
||||
f.send("sub amplifier all") // external amplifier (PowerGenius XL) operate/standby
|
||||
f.send("sub radio all") // radio-wide incl. interlock (TX/RX state)
|
||||
@@ -673,6 +677,14 @@ func (f *Flex) handleStatus(payload string) {
|
||||
s.audioLevel = atoiDefault(val, s.audioLevel)
|
||||
case "audio_mute", "mute":
|
||||
s.mute = val == "1"
|
||||
case "rxant":
|
||||
s.rxAnt = val
|
||||
case "txant":
|
||||
s.txAnt = val
|
||||
case "ant_list":
|
||||
s.antList = splitCSV(val)
|
||||
case "tx_ant_list":
|
||||
s.txAntList = splitCSV(val)
|
||||
case "nb":
|
||||
s.nb = val == "1"
|
||||
case "nb_level":
|
||||
@@ -960,6 +972,18 @@ func splitKV(kv string) (key, val string, ok bool) {
|
||||
}
|
||||
|
||||
// atoiDefault parses an int (or a float like "20.0", truncated), else def.
|
||||
// splitCSV splits a comma-separated antenna list (e.g. "ANT1,ANT2,RX_A") into a
|
||||
// trimmed slice, dropping empties.
|
||||
func splitCSV(s string) []string {
|
||||
out := []string{}
|
||||
for _, p := range strings.Split(s, ",") {
|
||||
if p = strings.TrimSpace(p); p != "" {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func atoiDefault(s string, def int) int {
|
||||
s = strings.TrimSpace(s)
|
||||
if n, err := strconv.Atoi(s); err == nil {
|
||||
@@ -1043,6 +1067,13 @@ func (f *Flex) FlexState() FlexTXState {
|
||||
st.APFLevel = rx.apfLevel
|
||||
st.FilterLo = rx.filterLo
|
||||
st.FilterHi = rx.filterHi
|
||||
st.RXAnt = rx.rxAnt
|
||||
st.TXAnt = rx.txAnt
|
||||
st.AntList = rx.antList
|
||||
st.TXAntList = rx.txAntList
|
||||
if len(st.TXAntList) == 0 {
|
||||
st.TXAntList = rx.antList // many configs share one antenna list
|
||||
}
|
||||
}
|
||||
if f.amp.handle != "" {
|
||||
st.AmpAvailable = true
|
||||
@@ -1098,6 +1129,10 @@ func (f *Flex) sendSlice(param string, val any) error {
|
||||
rx.apf = val == "1"
|
||||
case "apf_level":
|
||||
rx.apfLevel = toInt(val)
|
||||
case "rxant":
|
||||
rx.rxAnt = fmt.Sprint(val)
|
||||
case "txant":
|
||||
rx.txAnt = fmt.Sprint(val)
|
||||
}
|
||||
}
|
||||
f.mu.Unlock()
|
||||
@@ -1133,6 +1168,8 @@ func (f *Flex) SetAGCMode(m string) error {
|
||||
func (f *Flex) SetAGCThreshold(l int) error { return f.sendSlice("agc_threshold", clampLevel(l)) }
|
||||
func (f *Flex) SetAudioLevel(l int) error { return f.sendSlice("audio_level", clampLevel(l)) }
|
||||
func (f *Flex) SetMute(on bool) error { return f.sendSlice("audio_mute", boolFlex(on)) }
|
||||
func (f *Flex) SetRXAntenna(a string) error { return f.sendSlice("rxant", a) }
|
||||
func (f *Flex) SetTXAntenna(a string) error { return f.sendSlice("txant", a) }
|
||||
func (f *Flex) SetNB(on bool) error { return f.sendSlice("nb", boolFlex(on)) }
|
||||
func (f *Flex) SetNBLevel(l int) error { return f.sendSlice("nb_level", clampLevel(l)) }
|
||||
func (f *Flex) SetNR(on bool) error { return f.sendSlice("nr", boolFlex(on)) }
|
||||
|
||||
Reference in New Issue
Block a user