NetworkBackend's connect/send/lost/error prints always used the
connect-time registration name, so GUI console output kept showing
the placeholder counter ID even after MON-VER resolved the real
device name. Add a display_name per socket (defaults to name, kept
separate from the routing key used by find_by_name/register_xcvr) and
have the GUI update it once MON-VER responds.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM4FNUjhu9x8df5Dp1hHvd
_push_lru ran right after connect, before MON-VER resolves the device
name, so history entries kept the placeholder ID forever. Dedupe LRU
entries by connection endpoint (host/port, serial port, or file path)
instead of by receiver_id, and re-push once the device name is known
so the corrected entry replaces the placeholder instead of duplicating
it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XM4FNUjhu9x8df5Dp1hHvd
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
It still described network_backend.py at the repo root as the entry
point and said nothing about the GUI, the serial/file backends, or
how backend threads now handle parse errors vs. real disconnects.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AzmCaNjDb3TAqPpTwunKTY
A struct size-mismatch or any other parse error in a listener could
propagate uncaught out of the socket-reading thread and silently kill
it. Catch broadly in the event loop and per-listener in call_listener,
and make the NAV-SAT/NAV-SIG/RXM-RAWX group parsers tolerate a
payload length that isn't an exact multiple of the group size instead
of crashing on a short struct.unpack.
Also wire NetworkBackend's socket-loss detection through to
ReceiverManager (via a queued signal, since the callback fires from
the backend thread) so the GUI reflects a peer-initiated disconnect
instead of leaving the row stuck showing "connected". Verified against
a real ZED-X20P: it drops the TCP session on its own after ~20-28s
regardless of traffic; the GUI now marks the receiver disconnected and
frees its ID for reconnecting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AzmCaNjDb3TAqPpTwunKTY
getpeername() raised ENOTCONN when a connect failed or the peer
closed the socket, crashing the event loop thread. Identify sockets
via the selector key's data (set at register time) instead, and
catch OSError on recv/send to cleanly close and unregister a dead
socket rather than crashing.
Also fix select(timeout=100) which waited 100 seconds instead of
100ms, causing stop() to hang after a dead socket was unregistered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
disconnect_receiver left the rid in _states, causing the duplicate-ID
check to fire on reconnect. Use remove_receiver instead (which cleans
up _states/_configs) and reset _rid to None.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ReplayPacer (MsgListener+MsgTalker) sits between packetizer output and
downstream listeners; sleeps after each complete NMEA/UBX frame before
forwarding. FileBackend no longer owns the delay — it reads as fast as
possible and the pacer provides back-pressure on the same thread.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ConnectionConfig: new file_path and delay fields
- ConnectionRow: File page with path input, Browse button, delay
spinner (0–10 s), and auto-fill of receiver-ID from filename
- ReceiverManager.connect_receiver: FileBackend wired for 'file' type;
UBX poll timer skipped for file sources
- FileBackend.register_xcvr: fallback to first unoccupied entry when
name does not match filename pattern
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- --network NAME HOST PORT (repeatable)
- --serial NAME DEVICE BAUD (repeatable)
- --file PATH (repeatable, replayed via FileBackend)
- --log-dir DIR (frame logging, default ./log)
- --delay SECS (file replay inter-frame delay, default 1.0)
- --project FILE load config from JSON project file
- --save FILE export current config as JSON project file
Multiple source types can be combined. Network sources share one
NetworkBackend; serial sources each get their own SerialBackend;
file sources share one FileBackend. The poll loop runs only for
network sources; file-only mode joins the reader threads; serial-only
mode blocks on signal.pause().
file_backend: rename _source_id -> source_id_from_path (public),
add join() to wait for reader threads.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- msg/frame_logger.py: MsgListener that writes raw byte stream per
source to timestamped log files (<YYYY_MM_DD_HH_MM_SS_source>.log)
- main.py: entry point extracted from network_backend.py; adds
--log-dir argument (default ./log) and wires FrameLogger per transceiver
- network_backend.py: now contains only the NetworkBackend class
- .gitignore: exclude log/ directory
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Trail history increased from 1h to 24h (86400 points). Added a spinbox
(0-24h, default 1h) next to 'Show Trails' to control how much trail is
rendered; spinbox is disabled when trails are off.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Removed Trail column from satellite table. Added a single 'Show Trails'
checkbox in the source selector box that enables/disables all trails at once.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Store up to 1 hour of (elevation, azimuth) history per satellite/source
- Trail column in satellite table (checkbox, default off); clicking the
column header toggles all trails on/off
- Trail rendered in GNSS color with alpha fading from 15 (oldest) to 200
(newest), subsampled to max 120 points for render performance
- Trail column fixed at 42 px to avoid checkbox + empty-text visual split
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix _nice_ticks: use floor for first tick and ceil for last tick so the
tick range always fully brackets the data; data was previously clipped
when the maximum fell between two nice tick values
- Add 5% headroom padding so data does not sit flush against plot edges
- Add 'Visible Sats' and 'Used Sats' y-axis modes (global, like pDOP)
- Right-click context menu on plot canvas: Save as PNG and Copy to clipboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
One card per receiver showing receiver ID, pDOP, visible satellites and
used satellites. Cards are added/removed dynamically with receivers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Sky widget always gets the latest data immediately (off-thread render)
- Table rebuilds throttled to ~2 Hz via single-shot QTimer
- Cell values updated in-place when satellite set is unchanged, preserving
scroll position; full rebuild only when satellites appear/disappear
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>