fix: Binding to Gui Client to get proper values for cw delay and pitch
This commit is contained in:
+50
-5
@@ -38,6 +38,7 @@ type Flex struct {
|
|||||||
amp flexAmp // external amplifier (PowerGenius XL) state
|
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)
|
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)
|
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
|
// Live meters streamed over UDP (VITA-49). meterMeta is the definitions
|
||||||
// pushed over TCP; meterVal the latest scaled values keyed by meter id.
|
// pushed over TCP; meterVal the latest scaled values keyed by meter id.
|
||||||
@@ -174,6 +175,7 @@ func (f *Flex) Connect() error {
|
|||||||
f.slices = map[int]*flexSlice{}
|
f.slices = map[int]*flexSlice{}
|
||||||
f.meterVal = map[int]float64{}
|
f.meterVal = map[int]float64{}
|
||||||
f.meterSub = map[int]bool{}
|
f.meterSub = map[int]bool{}
|
||||||
|
f.boundClientID = "" // re-bind to the GUI client on each (re)connect
|
||||||
f.mu.Unlock()
|
f.mu.Unlock()
|
||||||
debugLog.Printf("Flex: connected to %s:%d", host, port)
|
debugLog.Printf("Flex: connected to %s:%d", host, port)
|
||||||
|
|
||||||
@@ -187,6 +189,7 @@ func (f *Flex) Connect() error {
|
|||||||
f.send("sub amplifier all") // external amplifier (PowerGenius XL) operate/standby
|
f.send("sub amplifier all") // external amplifier (PowerGenius XL) operate/standby
|
||||||
f.send("sub radio all") // radio-wide incl. interlock (TX/RX state)
|
f.send("sub radio all") // radio-wide incl. interlock (TX/RX state)
|
||||||
f.send("sub cwx all") // CWX: the LIVE CW speed/pitch/break-in (transmit holds only a static default)
|
f.send("sub cwx all") // CWX: the LIVE CW speed/pitch/break-in (transmit holds only a static default)
|
||||||
|
f.send("sub client all") // learn the GUI client (SmartSDR) so we can bind to it (below)
|
||||||
f.startMeters(conn) // open the UDP VITA-49 stream for live meters
|
f.startMeters(conn) // open the UDP VITA-49 stream for live meters
|
||||||
if f.spotsEnabled {
|
if f.spotsEnabled {
|
||||||
// Subscribe so the radio pushes existing spots (we learn their indices),
|
// Subscribe so the radio pushes existing spots (we learn their indices),
|
||||||
@@ -379,17 +382,59 @@ func (f *Flex) handleStatus(payload string) {
|
|||||||
f.tx.cwMonLevel = atoiDefault(val, f.tx.cwMonLevel)
|
f.tx.cwMonLevel = atoiDefault(val, f.tx.cwMonLevel)
|
||||||
case "sidetone", "cw_sidetone":
|
case "sidetone", "cw_sidetone":
|
||||||
f.tx.cwSidetone = val == "1"
|
f.tx.cwSidetone = val == "1"
|
||||||
// NOTE: speed/pitch/break_in_delay also appear in the transmit
|
// Once bound to the GUI client (see the client branch) the transmit
|
||||||
// object, but they are STALE defaults there — the LIVE values come
|
// object carries the GUI client's LIVE CW values, so read them here
|
||||||
// from the cwx object (see the cwx branch). Parsing them here too
|
// (and from cwx). Before binding these are the radio's static
|
||||||
// would let the stale value overwrite the correct one depending on
|
// defaults — that was the "always 600 / 5" bug.
|
||||||
// status arrival order, so we deliberately ignore them here.
|
case "speed", "cwl_speed", "cw_speed", "wpm", "cw_wpm":
|
||||||
|
f.tx.cwSpeed = atoiDefault(val, f.tx.cwSpeed)
|
||||||
|
case "pitch", "cwl_pitch", "cw_pitch":
|
||||||
|
f.tx.cwPitch = atoiDefault(val, f.tx.cwPitch)
|
||||||
|
case "break_in_delay", "cwl_delay", "cw_break_in_delay", "delay":
|
||||||
|
f.tx.cwBreakInDelay = atoiDefault(val, f.tx.cwBreakInDelay)
|
||||||
case "mic_level", "miclevel":
|
case "mic_level", "miclevel":
|
||||||
f.tx.micLevel = atoiDefault(val, f.tx.micLevel)
|
f.tx.micLevel = atoiDefault(val, f.tx.micLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.mu.Unlock()
|
f.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
// Client object — list of connected clients. GUI clients (SmartSDR /
|
||||||
|
// Maestro) carry a client_id; non-GUI clients don't. We bind to the GUI
|
||||||
|
// client so the radio routes GUI-tied data (CW pitch/speed, break-in
|
||||||
|
// delay, RF power) to us. Logged so the exact field names are confirmable.
|
||||||
|
if len(fields) >= 1 && fields[0] == "client" {
|
||||||
|
debugLog.Printf("Flex: status %s", payload)
|
||||||
|
var clientID, program string
|
||||||
|
disconnected := false
|
||||||
|
for _, kv := range fields[1:] {
|
||||||
|
if kv == "disconnected" {
|
||||||
|
disconnected = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key, val, ok := splitKV(kv)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch key {
|
||||||
|
case "client_id":
|
||||||
|
clientID = val
|
||||||
|
case "program":
|
||||||
|
program = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.mu.Lock()
|
||||||
|
alreadyBound := f.boundClientID != ""
|
||||||
|
f.mu.Unlock()
|
||||||
|
lp := strings.ToLower(program)
|
||||||
|
isGUI := program == "" || strings.Contains(lp, "smartsdr") || strings.Contains(lp, "maestro")
|
||||||
|
if !disconnected && clientID != "" && !alreadyBound && isGUI {
|
||||||
|
f.mu.Lock()
|
||||||
|
f.boundClientID = clientID
|
||||||
|
f.mu.Unlock()
|
||||||
|
f.send("client bind client_id=" + clientID)
|
||||||
|
debugLog.Printf("Flex: bound to GUI client %s (program=%q)", clientID, program)
|
||||||
|
}
|
||||||
|
}
|
||||||
// CWX object — the LIVE CW keyer values (speed/pitch/break-in delay).
|
// CWX object — the LIVE CW keyer values (speed/pitch/break-in delay).
|
||||||
// SmartSDR reads these here; the transmit object only carries a static
|
// SmartSDR reads these here; the transmit object only carries a static
|
||||||
// default. Logged in full so we can confirm the exact field names.
|
// default. Logged in full so we can confirm the exact field names.
|
||||||
|
|||||||
Reference in New Issue
Block a user