feat: added full support in USB (local) & ethernet (local or remote) of audio for Icom
This commit is contained in:
+47
-10
@@ -803,24 +803,44 @@ func (f *Flex) ReadState() (RigState, error) {
|
||||
// band) never hijacks the main frequency. Returns (-1, nil) when no slice is in
|
||||
// use. Caller holds f.mu.
|
||||
func (f *Flex) mainSliceLocked() (int, *flexSlice) {
|
||||
best, bestS := 1<<30, (*flexSlice)(nil)
|
||||
for idx, s := range f.slices {
|
||||
// Iterate in ASCENDING index order — NEVER map-iteration order, which Go
|
||||
// randomises. When two slices transiently BOTH report active=1 (e.g. an
|
||||
// external controller like DXHunter activates a slice on another band while
|
||||
// ours still holds active, before SmartSDR sends active=0 to the old one),
|
||||
// map order returned a RANDOM active slice each call → the operating frequency
|
||||
// flip-flopped 40m/20m every poll and the Ultrabeam motors chased it forever.
|
||||
// Deterministic order = the lowest-indexed active slice wins, stably.
|
||||
firstInUse := -1
|
||||
for _, idx := range f.sortedSliceIdxLocked() {
|
||||
s := f.slices[idx]
|
||||
if !s.inUse {
|
||||
continue
|
||||
}
|
||||
if firstInUse < 0 {
|
||||
firstInUse = idx
|
||||
}
|
||||
if s.active {
|
||||
return idx, s
|
||||
}
|
||||
if idx < best {
|
||||
best, bestS = idx, s
|
||||
}
|
||||
}
|
||||
if bestS != nil {
|
||||
return best, bestS
|
||||
if firstInUse >= 0 {
|
||||
return firstInUse, f.slices[firstInUse]
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// sortedSliceIdxLocked returns the slice indices in ascending order so every
|
||||
// slice-selection helper is deterministic (map iteration is randomised). Caller
|
||||
// holds f.mu.
|
||||
func (f *Flex) sortedSliceIdxLocked() []int {
|
||||
idxs := make([]int, 0, len(f.slices))
|
||||
for idx := range f.slices {
|
||||
idxs = append(idxs, idx)
|
||||
}
|
||||
sort.Ints(idxs)
|
||||
return idxs
|
||||
}
|
||||
|
||||
// activeSliceIndexLocked returns the slice index to send commands to (the main
|
||||
// slice, else 0). Caller holds f.mu.
|
||||
func (f *Flex) activeSliceIndexLocked() int {
|
||||
@@ -841,8 +861,8 @@ func sliceLetter(idx int) string {
|
||||
// txSliceLocked returns the slice flagged as the transmitter (tx=1), or nil.
|
||||
// Caller holds f.mu.
|
||||
func (f *Flex) txSliceLocked() *flexSlice {
|
||||
for _, s := range f.slices {
|
||||
if s.inUse && s.tx {
|
||||
for _, idx := range f.sortedSliceIdxLocked() {
|
||||
if s := f.slices[idx]; s.inUse && s.tx {
|
||||
return s
|
||||
}
|
||||
}
|
||||
@@ -871,7 +891,8 @@ func (f *Flex) operatingLocked() (main, rx, tx *flexSlice) {
|
||||
if main != nil && main != txS && main.freqHz != txS.freqHz && BandFromHz(main.freqHz) == bt {
|
||||
rx = main
|
||||
} else {
|
||||
for _, s := range f.slices {
|
||||
for _, idx := range f.sortedSliceIdxLocked() {
|
||||
s := f.slices[idx]
|
||||
if s.inUse && s != txS && s.freqHz != txS.freqHz && BandFromHz(s.freqHz) == bt {
|
||||
rx = s
|
||||
break
|
||||
@@ -927,6 +948,15 @@ func (f *Flex) SetFrequency(hz int64) error {
|
||||
f.mu.Lock()
|
||||
idx := f.activeSliceIndexLocked()
|
||||
connected := f.conn != nil
|
||||
// Optimistically update the active slice's cached freq NOW, before the radio
|
||||
// echoes the slice status back. Otherwise ReadState/FlexState keep reporting
|
||||
// the OLD freq for the round-trip: the top display (optimistic liveFreqHz)
|
||||
// jumped to the new band while the slice cache — which the FlexPanel and the
|
||||
// Ultrabeam follow loop read — still showed the old one, so the antenna chased
|
||||
// the stale value. The real echo confirms/corrects this a moment later.
|
||||
if s := f.slices[idx]; s != nil {
|
||||
s.freqHz = hz
|
||||
}
|
||||
f.mu.Unlock()
|
||||
if !connected {
|
||||
return fmt.Errorf("flex: not connected")
|
||||
@@ -952,6 +982,13 @@ func (f *Flex) SetMode(mode string) error {
|
||||
if fm == "" {
|
||||
return fmt.Errorf("flex: unsupported mode %q", mode)
|
||||
}
|
||||
// Optimistically cache the new mode too (same reasoning as SetFrequency) so the
|
||||
// panel reflects it immediately instead of lagging the radio's echo.
|
||||
f.mu.Lock()
|
||||
if s := f.slices[idx]; s != nil {
|
||||
s.mode = fm
|
||||
}
|
||||
f.mu.Unlock()
|
||||
// "slice s <rx> mode=<m>" — set command per the SmartSDR API.
|
||||
f.send(fmt.Sprintf("slice s %d mode=%s", idx, fm))
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user