diff --git a/app.go b/app.go index 6fb474b..e9a887e 100644 --- a/app.go +++ b/app.go @@ -7426,6 +7426,83 @@ func (a *App) FlexSetMute(on bool) error { return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetMute(on) }) } +func (a *App) FlexSetRXAntenna(ant string) error { + if a.cat == nil { + return fmt.Errorf("cat not initialized") + } + return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetRXAntenna(ant) }) +} + +func (a *App) FlexSetTXAntenna(ant string) error { + if a.cat == nil { + return fmt.Errorf("cat not initialized") + } + return a.cat.FlexDo(func(fc cat.FlexController) error { return fc.SetTXAntenna(ant) }) +} + +// keyFlexBandAnt stores the per-band RX/TX antenna map (global, machine-local). +const keyFlexBandAnt = "flex.band_antennas" + +// FlexBandAnt is the antenna pair to select on a given band. +type FlexBandAnt struct { + RX string `json:"rx"` + TX string `json:"tx"` +} + +// GetFlexBandAntennas returns the band→{rx,tx} antenna map (band key uppercased, +// e.g. "20M"). +func (a *App) GetFlexBandAntennas() (map[string]FlexBandAnt, error) { + out := map[string]FlexBandAnt{} + if a.settings == nil { + return out, nil + } + v, _ := a.settings.GetGlobal(a.ctx, keyFlexBandAnt) + if strings.TrimSpace(v) == "" { + return out, nil + } + _ = json.Unmarshal([]byte(v), &out) + return out, nil +} + +// SaveFlexBandAntennas persists the band→antenna map. +func (a *App) SaveFlexBandAntennas(m map[string]FlexBandAnt) error { + if a.settings == nil { + return fmt.Errorf("db not initialized") + } + b, err := json.Marshal(m) + if err != nil { + return err + } + return a.settings.SetGlobal(a.ctx, keyFlexBandAnt, string(b)) +} + +// FlexApplyBandAntenna selects the configured RX/TX antennas for the given band +// (no-op if the backend isn't a Flex or the band has no mapping). Called on band +// changes and spot clicks so the right antennas follow the frequency. +func (a *App) FlexApplyBandAntenna(band string) error { + if a.cat == nil { + return nil + } + band = strings.ToUpper(strings.TrimSpace(band)) + if band == "" { + return nil + } + m, _ := a.GetFlexBandAntennas() + ant, ok := m[band] + if !ok { + return nil + } + return a.cat.FlexDo(func(fc cat.FlexController) error { + if strings.TrimSpace(ant.RX) != "" { + _ = fc.SetRXAntenna(ant.RX) + } + if strings.TrimSpace(ant.TX) != "" { + _ = fc.SetTXAntenna(ant.TX) + } + return nil + }) +} + func (a *App) FlexSetNB(on bool) error { if a.cat == nil { return fmt.Errorf("cat not initialized") diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6f4c461..9be7b0d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -13,7 +13,7 @@ import { GetStartupStatus, CheckForUpdate, WorkedBefore, SetCompactMode, - GetCATState, SetCATFrequency, SetCATMode, SwitchCATRig, + GetCATState, SetCATFrequency, SetCATMode, SwitchCATRig, FlexApplyBandAntenna, GetSecretStatus, UnlockSecrets, RefreshCtyDat, DownloadAllReferenceLists, RotatorGoTo, RotatorStop, GetRotatorHeading, @@ -1431,6 +1431,19 @@ export default function App() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + // FlexRadio: apply the per-band RX/TX antennas whenever the band changes (rig + // QSY, spot click, manual band) — so the right antennas follow the frequency. + // Skipped while the band is LOCKED (entering an old QSO off-air) and for non-Flex + // backends. The backend no-ops if the band has no configured mapping. + const lastAntBandRef = useRef(''); + useEffect(() => { + if (catState.backend !== 'flex' || locks.band) return; + const b = band.trim(); + if (!b || b === lastAntBandRef.current) return; + lastAntBandRef.current = b; + FlexApplyBandAntenna(b).catch(() => {}); + }, [band, catState.backend, locks.band]); + // Cluster live wiring: hydrate per-server status + saved server list, // then subscribe to push events. async function reloadClusterMeta() { diff --git a/frontend/src/components/FlexPanel.tsx b/frontend/src/components/FlexPanel.tsx index 135da99..5840f09 100644 --- a/frontend/src/components/FlexPanel.tsx +++ b/frontend/src/components/FlexPanel.tsx @@ -5,7 +5,7 @@ import { FlexSetProcessor, FlexSetProcessorLevel, FlexSetMon, FlexSetMonLevel, FlexSetMic, FlexMox, FlexAmpOperate, GetPGXLStatus, PGXLSetFanMode, - FlexSetAGCMode, FlexSetAGCThreshold, FlexSetAudioLevel, FlexSetMute, + FlexSetAGCMode, FlexSetAGCThreshold, FlexSetAudioLevel, FlexSetMute, FlexSetRXAntenna, FlexSetTXAntenna, FlexSetNB, FlexSetNBLevel, FlexSetNR, FlexSetNRLevel, FlexSetANF, FlexSetANFLevel, FlexSetAPF, FlexSetAPFLevel, FlexSetCWSpeed, FlexSetCWPitch, FlexSetCWBreakInDelay, FlexSetCWSidetone, FlexSetSidetoneLevel, FlexSetCWFilter, FlexSetFilter, @@ -20,6 +20,7 @@ type FlexState = { mon: boolean; mon_level: number; mic_level: number; atu_status?: string; atu_memories: boolean; rx_avail: boolean; agc_mode?: string; agc_threshold: number; audio_level: number; mute: boolean; + rx_ant?: string; tx_ant?: string; ant_list?: string[]; tx_ant_list?: string[]; nb: boolean; nb_level: number; nr: boolean; nr_level: number; anf: boolean; anf_level: number; mode?: string; cw_speed: number; cw_pitch: number; cw_break_in_delay: number; cw_sidetone: boolean; cw_mon_level: number; @@ -357,6 +358,25 @@ export function FlexPanel() { {/* RECEIVE */} + {((st.ant_list?.length ?? 0) > 0 || (st.tx_ant_list?.length ?? 0) > 0) && ( +
+ Ant +
+ RX + + TX + +
+
+ )}
AGC > = { @@ -240,6 +248,7 @@ const SECTION_LABELS: Partial> = { antenna: 'UltraBeam', antgenius: 'Antenna Genius', pgxl: 'Power Genius', + flex: 'FlexRadio', audio: 'Audio devices', }; @@ -248,12 +257,13 @@ const SECTION_LABELS: Partial> = { interface TreeProps { selected: SectionId; onSelect: (id: SectionId) => void; + flexAvailable?: boolean; } -function Tree({ selected, onSelect }: TreeProps) { +function Tree({ selected, onSelect, flexAvailable }: TreeProps) { return ( @@ -585,6 +595,90 @@ function ComingSoon({ id, icon: Icon }: { id: SectionId; icon?: any }) { ); } +// FlexBandAntennasPanel — pick the RX/TX antenna per band. Applied automatically +// when the band changes (frequency change / spot click). Antennas come live from +// the connected FlexRadio; bands come from Lists → Bands. +function FlexBandAntennasPanel({ bands }: { bands: string[] }) { + const [rxList, setRxList] = useState([]); + const [txList, setTxList] = useState([]); + const [map, setMap] = useState>({}); + const [msg, setMsg] = useState(''); + useEffect(() => { + GetFlexState().then((s: any) => { + setRxList((s?.ant_list ?? []) as string[]); + setTxList(((s?.tx_ant_list?.length ? s.tx_ant_list : s?.ant_list) ?? []) as string[]); + }).catch(() => {}); + GetFlexBandAntennas().then((m: any) => setMap(m ?? {})).catch(() => {}); + }, []); + const set = (band: string, side: 'rx' | 'tx', v: string) => { + const key = band.toUpperCase(); + setMap((m) => { + const cur = m[key] ?? { rx: '', tx: '' }; + const next = { ...m, [key]: { ...cur, [side]: v } }; + SaveFlexBandAntennas(next as any) + .then(() => { setMsg('Saved'); window.setTimeout(() => setMsg(''), 1200); }) + .catch((e: any) => setMsg(String(e?.message ?? e))); + return next; + }); + }; + return ( +
+
+

FlexRadio — per-band antennas

+

+ Choose the RX and TX antenna for each band. They're applied automatically when the band + changes (frequency change or clicking a spot). +

+
+ {rxList.length === 0 && ( +
+ No antennas reported yet — make sure the FlexRadio is connected (CAT interface), then reopen this panel. +
+ )} + {bands.length === 0 ? ( +

No bands configured — add them in Lists → Bands.

+ ) : ( +
+ + + + + + + + + + {bands.map((b) => { + const e = map[b.toUpperCase()] ?? { rx: '', tx: '' }; + return ( + + + + + + ); + })} + +
BandRX antennaTX antenna
{b} + + + +
+
+ )} + {msg && {msg}} +
+ ); +} + export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChanged, flexAvailable }: Props) { const [selected, setSelected] = useState((initialSection as SectionId) || 'station'); const [loading, setLoading] = useState(true); @@ -3848,6 +3942,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan antenna: UltrabeamPanel, antgenius: AntGeniusPanelSettings, pgxl: PGXLPanelSettings, + flex: () => , audio: AudioPanel, }; @@ -3865,7 +3960,7 @@ export function SettingsModal({ onClose, onSaved, initialSection, onMainPaneChan
{/* Left sidebar tree */} {/* Right content pane */} diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index 342d898..d61892f 100644 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -138,6 +138,8 @@ export function FlexATUStart():Promise; export function FlexAmpOperate(arg1:boolean):Promise; +export function FlexApplyBandAntenna(arg1:string):Promise; + export function FlexMox(arg1:boolean):Promise; export function FlexSetAGCMode(arg1:string):Promise; @@ -190,8 +192,12 @@ export function FlexSetProcessor(arg1:boolean):Promise; export function FlexSetProcessorLevel(arg1:number):Promise; +export function FlexSetRXAntenna(arg1:string):Promise; + export function FlexSetSidetoneLevel(arg1:number):Promise; +export function FlexSetTXAntenna(arg1:string):Promise; + export function FlexSetTunePower(arg1:number):Promise; export function FlexSetVox(arg1:boolean):Promise; @@ -258,6 +264,8 @@ export function GetEmailSettings():Promise; export function GetExternalServices():Promise; +export function GetFlexBandAntennas():Promise>; + export function GetFlexState():Promise; export function GetIcomState():Promise; @@ -522,6 +530,8 @@ export function SaveEmailSettings(arg1:main.EmailSettings):Promise; export function SaveExternalServices(arg1:extsvc.ExternalServices):Promise; +export function SaveFlexBandAntennas(arg1:Record):Promise; + export function SaveListsSettings(arg1:main.ListsSettings):Promise; export function SaveLookupSettings(arg1:main.LookupSettings):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index 3dbe839..d3f474b 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -242,6 +242,10 @@ export function FlexAmpOperate(arg1) { return window['go']['main']['App']['FlexAmpOperate'](arg1); } +export function FlexApplyBandAntenna(arg1) { + return window['go']['main']['App']['FlexApplyBandAntenna'](arg1); +} + export function FlexMox(arg1) { return window['go']['main']['App']['FlexMox'](arg1); } @@ -346,10 +350,18 @@ export function FlexSetProcessorLevel(arg1) { return window['go']['main']['App']['FlexSetProcessorLevel'](arg1); } +export function FlexSetRXAntenna(arg1) { + return window['go']['main']['App']['FlexSetRXAntenna'](arg1); +} + export function FlexSetSidetoneLevel(arg1) { return window['go']['main']['App']['FlexSetSidetoneLevel'](arg1); } +export function FlexSetTXAntenna(arg1) { + return window['go']['main']['App']['FlexSetTXAntenna'](arg1); +} + export function FlexSetTunePower(arg1) { return window['go']['main']['App']['FlexSetTunePower'](arg1); } @@ -482,6 +494,10 @@ export function GetExternalServices() { return window['go']['main']['App']['GetExternalServices'](); } +export function GetFlexBandAntennas() { + return window['go']['main']['App']['GetFlexBandAntennas'](); +} + export function GetFlexState() { return window['go']['main']['App']['GetFlexState'](); } @@ -1010,6 +1026,10 @@ export function SaveExternalServices(arg1) { return window['go']['main']['App']['SaveExternalServices'](arg1); } +export function SaveFlexBandAntennas(arg1) { + return window['go']['main']['App']['SaveFlexBandAntennas'](arg1); +} + export function SaveListsSettings(arg1) { return window['go']['main']['App']['SaveListsSettings'](arg1); } diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index c5ffcb3..2153921 100644 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -516,6 +516,10 @@ export namespace cat { agc_threshold: number; audio_level: number; mute: boolean; + rx_ant?: string; + tx_ant?: string; + ant_list?: string[]; + tx_ant_list?: string[]; nb: boolean; nb_level: number; nr: boolean; @@ -565,6 +569,10 @@ export namespace cat { this.agc_threshold = source["agc_threshold"]; this.audio_level = source["audio_level"]; this.mute = source["mute"]; + this.rx_ant = source["rx_ant"]; + this.tx_ant = source["tx_ant"]; + this.ant_list = source["ant_list"]; + this.tx_ant_list = source["tx_ant_list"]; this.nb = source["nb"]; this.nb_level = source["nb_level"]; this.nr = source["nr"]; diff --git a/internal/cat/cat.go b/internal/cat/cat.go index 6e15835..3d1de0f 100644 --- a/internal/cat/cat.go +++ b/internal/cat/cat.go @@ -39,18 +39,18 @@ type Backend interface { // and RxFreqHz is the active VFO (where they listen). When not split, // RxFreqHz is 0 — the UI shouldn't show a redundant RX field. type RigState struct { - Enabled bool `json:"enabled"` // user toggled CAT on - Connected bool `json:"connected"` // backend says rig is online - Backend string `json:"backend,omitempty"` // active backend name - RigNum int `json:"rig_num,omitempty"` // OmniRig slot 1 or 2 (when applicable) - Rig string `json:"rig,omitempty"` // rig model (best-effort) - FreqHz int64 `json:"freq_hz,omitempty"` // TX freq (= active VFO when not split) - RxFreqHz int64 `json:"freq_rx_hz,omitempty"` // RX freq, only set when Split - Split bool `json:"split,omitempty"` // rig is in split mode - Mode string `json:"mode,omitempty"` // ADIF mode (SSB/CW/DATA/AM/FM/RTTY) - Band string `json:"band,omitempty"` // computed from FreqHz - Vfo string `json:"vfo,omitempty"` // "A" | "B" | "AA" | "AB" | "BA" | "BB" - Error string `json:"error,omitempty"` // last connect/poll error if any + Enabled bool `json:"enabled"` // user toggled CAT on + Connected bool `json:"connected"` // backend says rig is online + Backend string `json:"backend,omitempty"` // active backend name + RigNum int `json:"rig_num,omitempty"` // OmniRig slot 1 or 2 (when applicable) + Rig string `json:"rig,omitempty"` // rig model (best-effort) + FreqHz int64 `json:"freq_hz,omitempty"` // TX freq (= active VFO when not split) + RxFreqHz int64 `json:"freq_rx_hz,omitempty"` // RX freq, only set when Split + Split bool `json:"split,omitempty"` // rig is in split mode + Mode string `json:"mode,omitempty"` // ADIF mode (SSB/CW/DATA/AM/FM/RTTY) + Band string `json:"band,omitempty"` // computed from FreqHz + Vfo string `json:"vfo,omitempty"` // "A" | "B" | "AA" | "AB" | "BA" | "BB" + Error string `json:"error,omitempty"` // last connect/poll error if any UpdatedAt time.Time `json:"updated_at,omitempty"` } @@ -230,7 +230,7 @@ func (m *Manager) SendSpot(s SpotInfo) { // FlexRadio control tab. Levels are 0-100. (Phase 1: controls + state pushed by // the radio over TCP; live meters arrive over a separate UDP stream later.) type FlexTXState struct { - Available bool `json:"available"` // backend is Flex and handshaked + Available bool `json:"available"` // backend is Flex and handshaked Model string `json:"model,omitempty"` RFPower int `json:"rf_power"` TunePower int `json:"tune_power"` @@ -247,17 +247,21 @@ type FlexTXState struct { ATUStatus string `json:"atu_status,omitempty"` ATUMemories bool `json:"atu_memories"` // Active RX slice DSP controls. - RXAvail bool `json:"rx_avail"` // an RX slice exists - AGCMode string `json:"agc_mode,omitempty"` - AGCThreshold int `json:"agc_threshold"` - AudioLevel int `json:"audio_level"` - Mute bool `json:"mute"` - NB bool `json:"nb"` - NBLevel int `json:"nb_level"` - NR bool `json:"nr"` - NRLevel int `json:"nr_level"` - ANF bool `json:"anf"` - ANFLevel int `json:"anf_level"` + RXAvail bool `json:"rx_avail"` // an RX slice exists + AGCMode string `json:"agc_mode,omitempty"` + AGCThreshold int `json:"agc_threshold"` + AudioLevel int `json:"audio_level"` + Mute bool `json:"mute"` + RXAnt string `json:"rx_ant,omitempty"` // selected RX antenna + TXAnt string `json:"tx_ant,omitempty"` // selected TX antenna + AntList []string `json:"ant_list,omitempty"` // antennas selectable for RX + TXAntList []string `json:"tx_ant_list,omitempty"` // antennas selectable for TX + NB bool `json:"nb"` + NBLevel int `json:"nb_level"` + NR bool `json:"nr"` + NRLevel int `json:"nr_level"` + ANF bool `json:"anf"` + ANFLevel int `json:"anf_level"` // CW / mode-specific controls. Mode string `json:"mode,omitempty"` // active slice mode (CW/USB/LSB/DIGU…) CWSpeed int `json:"cw_speed"` @@ -314,6 +318,8 @@ type FlexController interface { SetAGCThreshold(int) error SetAudioLevel(int) error SetMute(bool) error + SetRXAntenna(string) error + SetTXAntenna(string) error SetNB(bool) error SetNBLevel(int) error SetNR(bool) error diff --git a/internal/cat/flex.go b/internal/cat/flex.go index d64d139..18cac71 100644 --- a/internal/cat/flex.go +++ b/internal/cat/flex.go @@ -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 " 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 " 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)) }