# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Running the application ```bash # Activate the virtual environment first source .venv/bin/activate # Run the app python sky_plot.py ``` ## Architecture This is a PyQt5 desktop app that reads GNSS satellite data from a u-blox receiver (UBX protocol) over serial or TCP/IP and visualizes it in real time. ### Data flow 1. `GnssReader` (QThread in `gnss_thread.py`) polls the device in a loop: sends `NAV-SAT` and `NAV-DOP` poll requests, reads 512 bytes, feeds them through `UbxPacketizer`. 2. Decoded frames are passed to `extract_satellites()` in `ubx.py`, which returns a dict with optional keys `'sat'` (list of satellite dicts) and `'dop'` (dict with `p_dop`, `h_dop`, etc.). 3. `GnssReader` emits the `satellites_updated(dict)` signal. 4. `MainWindow._on_satellites()` routes the payload to three widgets: `SkyPlotWidget`, `SatelliteTable`, and `PlotTab`. ### Module overview | File | Responsibility | |---|---| | `sky_plot.py` | Entry point; `MainWindow` with three tabs: Connection, Sky View, Plot | | `gnss_thread.py` | `GnssReader(QThread)` — device I/O loop, emits signals | | `ubx.py` | UBX protocol: `UbxPacketizer` (byte-stream → frames), `parse_nav_sat`, `parse_nav_dop`, `extract_satellites` | | `transport.py` | Abstract `GnssTransport` base class | | `transport_serial.py` | `SerialTransport` — wraps pyserial | | `transport_network.py` | `TcpTransport` — wraps socket (for ser2net) | | `connection_panel.py` | `ConnectionPanel(QGroupBox)` — serial/TCP UI, emits `connect_requested(factory, label)` | | `lru_store.py` | `LruStore` — persists up to 5 recent TCP connections via `QSettings` | | `sky_plot_widget.py` | `SkyPlotWidget(QWidget)` — polar sky plot, custom `paintEvent` | | `sat_table.py` | `SatelliteTable(QTableWidget)` — tabular satellite list | | `plot_tab.py` | `PlotTab` — 2×4 grid of `SinglePlotPanel`s (pyqtgraph), time-series plots with persistent settings | | `defines.py` | `GNSS_NAMES` and `GNSS_COLORS` shared constants | ### Key design notes - **Transport factory pattern**: `ConnectionPanel` emits a `factory` callable (a lambda that creates a transport). `GnssReader` calls the factory in its thread, keeping socket/serial construction off the main thread. - **Disabled old code**: `MainWindow.__build_ui` and `MainWindow.__connect` (double-underscore prefix) are the original single-view implementations, effectively disabled by Python name mangling. The active methods are `_build_ui` and `_connect`. - **`sat_table.py` duplicates** `GNSS_NAMES`/`GNSS_COLORS` from `defines.py` — prefer `defines.py` as the source of truth when extending. - **Plot settings persistence**: `PlotSettingsStore` saves each panel's selected field and SV filter to `QSettings("gnss_skyplot", "connections")` (same org/app as `LruStore`). - **History cap**: each `SinglePlotPanel` keeps at most `MAX_HISTORY = 300` data points per satellite/PDOP curve.