chore: release v0.19.4

This commit is contained in:
2026-07-10 17:16:32 +02:00
parent 0c3089344b
commit 6c39204301
11 changed files with 275 additions and 22 deletions
+21 -3
View File
@@ -60,6 +60,7 @@ type Flex struct {
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)
spotByCall map[string]int // callsign → live spot index, so re-spotting a call replaces its old spot (WSJT decodes re-fire every cycle)
sentCmds map[int]string // seq → command text, so an R<seq> error names the command
// OnSpotClick is called (off the reader goroutine's hot path) when the user
@@ -157,7 +158,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{}, pendingSplit: map[int]bool{},
spotIdx: map[int]bool{}, pendingSpot: map[int]string{}, spotCall: map[int]string{}, spotByCall: map[string]int{}, 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{},
}
@@ -352,6 +353,7 @@ func (f *Flex) reader(conn net.Conn) {
if idx, e := strconv.Atoi(strings.TrimSpace(parts[2])); e == nil {
f.spotCall[idx] = call
f.spotIdx[idx] = true
f.spotByCall[strings.ToUpper(call)] = idx
}
}
// A successful "slice create" for split returns the new slice's index;
@@ -1071,8 +1073,23 @@ func (f *Flex) SendSpot(s SpotInfo) error {
if color == "" {
color = "#FFFFA500" // opaque orange default
}
cmd := fmt.Sprintf("spot add rx_freq=%.6f callsign=%s color=%s source=OpsLog lifetime_seconds=1800 trigger_action=Tune timestamp=%d",
float64(s.FreqHz)/1e6, call, color, time.Now().Unix())
life := s.LifetimeSec
if life <= 0 {
life = 1800 // default 30 min (cluster spots)
}
// De-dupe by callsign: WSJT decodes re-fire every cycle, so a station already
// spotted gets its previous spot removed first — one live spot per call,
// refreshed, instead of a pile that all expire independently.
f.mu.Lock()
if old, ok := f.spotByCall[strings.ToUpper(s.Callsign)]; ok {
delete(f.spotByCall, strings.ToUpper(s.Callsign))
delete(f.spotCall, old)
delete(f.spotIdx, old)
f.send(fmt.Sprintf("spot remove %d", old))
}
f.mu.Unlock()
cmd := fmt.Sprintf("spot add rx_freq=%.6f callsign=%s color=%s source=OpsLog lifetime_seconds=%d trigger_action=Tune timestamp=%d",
float64(s.FreqHz)/1e6, call, color, life, time.Now().Unix())
if m := flexEncode(s.Mode); m != "" {
cmd += " mode=" + m
}
@@ -1118,6 +1135,7 @@ func (f *Flex) ClearSpots() error {
f.mu.Lock()
f.spotIdx = map[int]bool{}
f.spotCall = map[int]string{}
f.spotByCall = map[string]int{}
connected := f.conn != nil
f.mu.Unlock()
if !connected {