add UBX-MON-VER support and surface device identification in CLI/GUI

Add the Ver message parser and a fixed UbxQuery (was unused and had a
broken semaphore/state bug) to poll MON-VER once on connect. The CLI
prints hw/sw version and extensions; the GUI polls it per receiver,
pre-fills the read-only ID field with a connection counter and
rewrites it from the MOD= extension once the device answers, and
propagates the resolved display name to the Sky and Plot tabs so they
no longer show the stale placeholder ID.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM4FNUjhu9x8df5Dp1hHvd
This commit is contained in:
2026-07-16 20:22:25 +02:00
co-authored by Claude Sonnet 5
parent d506f9afe0
commit 2dba3a9a2f
8 changed files with 176 additions and 27 deletions
+31 -6
View File
@@ -3,6 +3,7 @@ import time
import json
import signal
import argparse
import threading
from backend.network_backend import NetworkBackend
from backend.serial_backend import SerialBackend
@@ -16,8 +17,9 @@ from nmea.messages import Gll, Gsa, Gsv, Gga, Rmc
from ubx.ubx import frame_create
from ubx.msg_types import *
from ubx.packetizer import UbxPacketizer
from ubx.messages import Sig, Sat, RawX, UbxMsg
from ubx.messages import Sig, Sat, RawX, Ver, UbxMsg
from ubx.listener import UbxListener
from ubx.query import UbxQuery
# ---------------------------------------------------------------------------
@@ -87,7 +89,8 @@ def _make_listeners(name: str):
def wire_source(name: str, backend, log_dir: str | None,
loggers: list, transceivers: dict, pacer_delay: float = 0.0):
loggers: list, transceivers: dict, ver_queries: dict,
pacer_delay: float = 0.0):
xcvr = Transceiver(name)
backend.register_xcvr(xcvr)
transceivers[name] = xcvr
@@ -104,6 +107,10 @@ def wire_source(name: str, backend, log_dir: str | None,
nmea_rx, ubx_rx = _make_listeners(name)
ver_query = UbxQuery(xcvr, Ver())
ubx_rx.append(ver_query)
ver_queries[name] = ver_query
if pacer_delay > 0:
nmea_pacer = ReplayPacer(pacer_delay)
ubx_pacer = ReplayPacer(pacer_delay)
@@ -157,6 +164,7 @@ if __name__ == '__main__':
backends = []
loggers = []
transceivers = {}
ver_queries = {}
# Network sources → one shared NetworkBackend
net_sources = [s for s in sources if s['type'] == 'network']
@@ -165,13 +173,14 @@ if __name__ == '__main__':
for s in net_sources])
backends.append(net_be)
for s in net_sources:
wire_source(s['name'], net_be, log_dir, loggers, transceivers)
wire_source(s['name'], net_be, log_dir, loggers, transceivers, ver_queries)
# Serial sources → one SerialBackend each
for s in [s for s in sources if s['type'] == 'serial']:
serial_sources = [s for s in sources if s['type'] == 'serial']
for s in serial_sources:
ser_be = SerialBackend(s['device'], s.get('baudrate', 115200))
backends.append(ser_be)
wire_source(s['name'], ser_be, log_dir, loggers, transceivers)
wire_source(s['name'], ser_be, log_dir, loggers, transceivers, ver_queries)
# File sources → one shared FileBackend (no secondary logging)
file_sources = [s for s in sources if s['type'] == 'file']
@@ -182,7 +191,8 @@ if __name__ == '__main__':
name = source_id_from_path(s['path'])
if name:
wire_source(name, file_be, log_dir=None, loggers=loggers,
transceivers=transceivers, pacer_delay=delay)
transceivers=transceivers, ver_queries=ver_queries,
pacer_delay=delay)
signal.signal(signal.SIGINT,
lambda sig, frm: handler(sig, frm, backends, loggers))
@@ -191,6 +201,21 @@ if __name__ == '__main__':
be.connect()
be.start()
# Poll MON-VER once per live (network/serial) source, off the main thread
# since UbxQuery.poll() blocks until the response frame arrives.
def report_version(name: str, query: UbxQuery):
ver = query.poll()
if ver is None:
return
ext = '; '.join(e['extension'] for e in ver['extensions'])
print(f"{name}: device hw={ver['hw_version']} sw={ver['sw_version']}"
+ (f" ({ext})" if ext else ""))
for s in net_sources + serial_sources:
name = s['name']
threading.Thread(target=report_version, args=(name, ver_queries[name]),
daemon=True).start()
# Poll loop only meaningful for network/serial sources
net_names = [s['name'] for s in net_sources]
if net_names: