fix: zoom taking too long after clicking a spot

This commit is contained in:
2026-06-30 09:14:50 +02:00
parent 2712902057
commit edede0bc1e
9 changed files with 135 additions and 14 deletions
+5 -1
View File
@@ -247,7 +247,10 @@ 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
RXAvail bool `json:"rx_avail"` // an RX slice exists
Split bool `json:"split"` // RX/TX on separate slices
RXFreqHz int64 `json:"rx_freq_hz,omitempty"` // RX slice freq when split
TXFreqHz int64 `json:"tx_freq_hz,omitempty"` // TX slice freq when split
AGCMode string `json:"agc_mode,omitempty"`
AGCThreshold int `json:"agc_threshold"`
AudioLevel int `json:"audio_level"`
@@ -320,6 +323,7 @@ type FlexController interface {
SetMute(bool) error
SetRXAntenna(string) error
SetTXAntenna(string) error
SetSplit(bool) error
SetNB(bool) error
SetNBLevel(int) error
SetNR(bool) error
+85 -9
View File
@@ -54,6 +54,7 @@ type Flex struct {
spotsEnabled bool // push cluster spots + manage the panadapter overlay
spotIdx map[int]bool // panadapter spot indices currently known to the radio
pendingSpot map[int]string // seq → callsign, awaiting the spot index in the R response
pendingSplit map[int]bool // seq → awaiting the new TX slice's index (split create)
spotCall map[int]string // spot index → callsign (to fill the call on a panadapter click)
sentCmds map[int]string // seq → command text, so an R<seq> error names the command
@@ -148,7 +149,7 @@ func NewFlex(host string, port int, spotsEnabled bool) *Flex {
return &Flex{
host: strings.TrimSpace(host), port: port,
slices: map[int]*flexSlice{}, spotsEnabled: spotsEnabled,
spotIdx: map[int]bool{}, pendingSpot: map[int]string{}, spotCall: map[int]string{},
spotIdx: map[int]bool{}, pendingSpot: map[int]string{}, spotCall: map[int]string{}, pendingSplit: map[int]bool{},
meterMeta: map[int]meterInfo{}, meterVal: map[int]float64{}, meterSub: map[int]bool{},
sentCmds: map[int]string{}, txSetAt: map[string]time.Time{},
}
@@ -314,7 +315,18 @@ func (f *Flex) reader(conn net.Conn) {
f.spotIdx[idx] = true
}
}
// A successful "slice create" for split returns the new slice's index;
// make that slice the transmitter so RX/TX are on separate slices.
splitSeq := f.pendingSplit[seq]
if splitSeq {
delete(f.pendingSplit, seq)
}
f.mu.Unlock()
if splitSeq && ok && len(parts) >= 3 {
if idx, e := strconv.Atoi(strings.TrimSpace(parts[2])); e == nil {
f.send(fmt.Sprintf("slice s %d tx=1", idx))
}
}
}
}
// Connection ended.
@@ -1050,6 +1062,11 @@ func (f *Flex) FlexState() FlexTXState {
CWSidetone: f.tx.cwSidetone,
CWMonLevel: f.tx.cwMonLevel,
}
if rx, tx := f.pickSlicesLocked(); rx != nil && tx != nil && rx != tx && rx.freqHz != tx.freqHz {
st.Split = true
st.RXFreqHz = rx.freqHz
st.TXFreqHz = tx.freqHz
}
if _, rx := f.rxSliceLocked(); rx != nil {
st.RXAvail = true
st.Mode = strings.ToUpper(rx.mode)
@@ -1170,14 +1187,73 @@ func (f *Flex) SetAudioLevel(l int) error { return f.sendSlice("audio_level",
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)) }
func (f *Flex) SetNRLevel(l int) error { return f.sendSlice("nr_level", clampLevel(l)) }
func (f *Flex) SetANF(on bool) error { return f.sendSlice("anf", boolFlex(on)) }
func (f *Flex) SetANFLevel(l int) error { return f.sendSlice("anf_level", clampLevel(l)) }
func (f *Flex) SetAPF(on bool) error { return f.sendSlice("apf", boolFlex(on)) }
func (f *Flex) SetAPFLevel(l int) error { return f.sendSlice("apf_level", clampLevel(l)) }
// SetSplit toggles 2-slice split like SmartSDR's SPLIT button. ON creates a TX
// slice at RX freq + offset (+1 kHz on CW, +5 kHz otherwise) and makes it the
// transmitter; OFF removes the extra TX slice and returns to simplex (RX slice
// transmits again).
func (f *Flex) SetSplit(on bool) error {
f.mu.Lock()
if f.conn == nil {
f.mu.Unlock()
return fmt.Errorf("flex: not connected")
}
rx, tx := f.pickSlicesLocked()
rxIdx, txIdx := -1, -1
for i, s := range f.slices {
if s == rx {
rxIdx = i
}
if s == tx {
txIdx = i
}
}
alreadySplit := rx != nil && tx != nil && rx != tx
var rxFreq int64
var rxMode, rxAnt string
if rx != nil {
rxFreq, rxMode, rxAnt = rx.freqHz, rx.mode, rx.rxAnt
}
f.mu.Unlock()
if on {
if alreadySplit || rx == nil {
return nil // already split (or no slice)
}
offset := int64(5000)
if strings.HasPrefix(strings.ToUpper(rxMode), "CW") {
offset = 1000
}
cmd := fmt.Sprintf("slice create freq=%.6f mode=%s", float64(rxFreq+offset)/1e6, rxMode)
if rxAnt != "" {
cmd += " ant=" + rxAnt
}
if seq := f.send(cmd); seq > 0 {
f.mu.Lock()
f.pendingSplit[seq] = true // mark the new slice TX once its index returns
f.mu.Unlock()
}
return nil
}
if !alreadySplit {
return nil
}
if txIdx >= 0 {
f.send(fmt.Sprintf("slice remove %d", txIdx))
}
if rxIdx >= 0 {
f.send(fmt.Sprintf("slice s %d tx=1", rxIdx))
}
return nil
}
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)) }
func (f *Flex) SetNRLevel(l int) error { return f.sendSlice("nr_level", clampLevel(l)) }
func (f *Flex) SetANF(on bool) error { return f.sendSlice("anf", boolFlex(on)) }
func (f *Flex) SetANFLevel(l int) error { return f.sendSlice("anf_level", clampLevel(l)) }
func (f *Flex) SetAPF(on bool) error { return f.sendSlice("apf", boolFlex(on)) }
func (f *Flex) SetAPFLevel(l int) error { return f.sendSlice("apf_level", clampLevel(l)) }
// ── CW keyer controls (top-level "cw" commands) ──