fix(tci): resubscribe to the meters at 'ready', and log what comes back

The report from a real SunSDR: transmit power and SWR still empty after the
sensors fix. The subscription went out once, at connect — while ExpertSDR3 is
still streaming its initial state dump, exactly where a unidirectional control
command can be ignored. It is now renewed every time the server says 'ready'.

The sensor messages also join the logged-on-arrival set, capped like the rest:
the next log will say whether TX_SENSORS ever arrives, which is the question —
a radio that never sends it (AetherSDR may not) and a frame that arrived and
was dropped leave the same blank meters and need opposite fixes.
This commit is contained in:
2026-08-28 22:51:25 +02:00
parent db24c38e63
commit 8acfda4e42
3 changed files with 31 additions and 3 deletions
+19 -2
View File
@@ -165,8 +165,7 @@ func (t *TCI) Connect() error {
if t.spotsEnabled {
debugLog.Printf("TCI: panorama spots are ON — spots will be sent to the radio")
}
_ = t.send("rx_sensors_enable:true,200;")
_ = t.send("tx_sensors_enable:true,200;")
t.subscribeSensors("connect")
if t.spotsEnabled {
// Forget what we thought was on the panorama at the same moment the radio
// is told to drop it. Kept, the memory would suppress the next spot for
@@ -461,6 +460,17 @@ func (t *TCI) SetTXAudioSource(src string) {
}
// send writes a command to the WebSocket (one writer at a time).
// subscribeSensors asks the radio to push its meters. Nothing measures anything
// until this goes out — the S-meter, the transmit power and the SWR are all
// subscription-only (TCI §4.4) — and it is sent at connect AND again at every
// "ready", because a subscription sent during the server's initial dump can be
// dropped. 200 ms is the rate the protocol's own examples use.
func (t *TCI) subscribeSensors(when string) {
e1 := t.send("rx_sensors_enable:true,200;")
e2 := t.send("tx_sensors_enable:true,200;")
debugLog.Printf("TCI: sensor subscription sent (%s): rx=%v tx=%v", when, e1, e2)
}
func (t *TCI) send(cmd string) error {
t.mu.Lock()
c := t.conn
@@ -567,6 +577,13 @@ func (t *TCI) handle(msg string) {
}
case "ready", "start":
t.ready = true
// (Re)subscribe to the meters HERE, not only at connect. ExpertSDR3
// dumps its whole state and then says "ready"; a unidirectional control
// command sent while that dump is still in flight can be ignored, and
// the report from a real SunSDR — transmit meters still empty after the
// connect-time subscription — has exactly that shape. From a goroutine:
// send takes t.mu, which this handler holds.
go t.subscribeSensors("ready")
case "stop":
t.ready = false
case "vfo":