Welcome to the Pycom Logbook — a collection of news, development updates, and technical notes covering upcoming features, remote operation setup, and background on the project.
Pycom Radio Controller Demo and Live RS-44 Contact
AMSAT Satellite Status Reports
It's about time. Pycom's next release will allow you to submit satellite status reports directly to AMSAT — no need to switch to a browser to log that you heard (or didn't hear) a satellite. The new AMSAT Status dialog shows current satellite status at a glance, auto-fills your callsign, grid square, time and the tracked satellite from S.A.T (if in use), and submits reports with a single click.
Thinking about Training Data Augmentation for Signal Classification
Two influential papers in Automatic Modulation Recognition (AMR) provide clear guidance for improving Pycom's signal classifier. O'Shea, Roy & Clancy's Over-the-Air Deep Learning Based Radio Signal Classification (IEEE JSTSP, 2018) demonstrates that models trained only on clean, ideal data degrade dramatically when deployed over the air, where carrier frequency offset, varying SNR, and multipath effects conspire to confuse the classifier. Li, Wang, Dong & Wang's Data Augmentation for Automatic Modulation Recognition (IEEE Wireless Communications Letters, 2025) shows that targeted augmentation — particularly noise injection, channel impairment simulation, and synthetic data generation — significantly improves classification accuracy, especially for underrepresented signal types at low SNR.
Pycom's architecture already mitigates the worst of the over-the-air pitfalls: the center_freq_offset_hz auxiliary feature directly encodes the carrier offset that was identified as the primary error source, and the six-channel auxiliary input (span, reference level, noise floor, bandwidth, frequency offset, SNR) gives the model explicit metadata about signal conditions rather than forcing it to infer everything from pixels. Bandwidth-aware centred patch extraction normalises the view regardless of mode width, and WeightedRandomSampler balances class representation during training. What both papers make clear is that architectural design alone isn't enough — the model also needs to see the full range of variation it will encounter at inference time, and that's where augmentation fills the gap.
The current ScopeCNN-Patch (Convolutional Neural Network) model was trained on just 403 samples across seven active signal classes — FM (160), JT65 (95), CW (73), FT8 (35), FT4 (17), AFSK (14), and SSB (9) — achieving 77% validation accuracy. Eight of the fifteen supported modes have zero training data. This severe class imbalance means the model can recognise strong FM and CW signals reliably but struggles with underrepresented modes like SSB and FT4, where it simply hasn't seen enough variation to generalise. The augmentation approach directly addresses this: rather than requiring hundreds more captures (which means more time at the radio, and potentially investing in something like an IC-7100 so I can generate my own signals to capture), we can apply five on-the-fly transforms during training that synthetically expand each sample into many unique training views. The model sees different augmented versions of the same signal every epoch, teaching it that the same mode can look different under varying conditions.
| Transform | What It Simulates | Why It Matters |
|---|---|---|
| Amplitude scaling | RF gain and AGC variation (70–120% intensity) | Most basic real-world variation — no two contacts have the same signal strength |
| Additive noise (SNR-aware) | Poorer SNR conditions | Highest-impact technique per the literature. Scales noise proportionally to each patch's existing floor, preserving the character of quiet and noisy signals alike |
| Frequency shifting | Imprecise centre-frequency tuning | Slides the signal left or right by a few bins; directly complements the center_freq_offset_hz auxiliary feature |
| Time shifting | Partially-filled scope display after tuning | Drops rows from the top and zero-fills the bottom, simulating the display right after changing frequency |
| Auxiliary feature jitter | Variation in measured metadata | Perturbs noise floor, frequency offset, and SNR channels slightly; prevents memorisation of exact values for rare classes |
| Same-class Mixup | Additional within-class variation | Blends two patches of the same signal type at random ratios for classes with fewer than 20 samples, creating plausible new views without label noise |
Augmentation would be enabled by default for training from scratch and disabled for fine-tuning to preserve the feature distribution the frozen layers expect. The expected improvement is from 77% to 80–85% overall, with the biggest gains on minority classes like SSB and AFSK.
IARU Band Plan Display
The waterfall now includes a colour-coded band plan strip showing IARU allocations for your region. Hover any segment to see its category and description — no more guessing which part of the band is reserved for CW, where the satellite sub-band starts, or whether that frequency is in the FT8 window.
Multi-Region Support
Band plans differ across the three IARU regions. Pycom ships with allocations for all three, sourced from RSGB (Region 1), ARRL (Region 2), and the IARU Region 3 interim band plan. Select your region in Configuration → General → IARU Region and the display updates immediately — no restart needed.
| Region | Covers | Source |
|---|---|---|
| Region 1 | Europe, Africa, Middle East, Northern Asia | RSGB |
| Region 2 | Americas | ARRL |
| Region 3 | South Asia, East Asia, Oceania | IARU Region 3 |
Categories
The band plan strip uses 15 colour-coded categories so you can identify segments at a glance:
CW, All modes, WSPR, FT8, FT4, FM, Repeaters, Beacons, Satellite, EME, Digital modes, SSB, ATV, APRS, and DX. Shared calling frequencies (like FT8 at 144.174 MHz) appear in all regions regardless of your selection.
Customising the Band Plan
The band plan data is stored in bandplan.csv in your Pycom configuration directory (~/Pycom/ on macOS, %USERPROFILE%\Pycom\ on Windows). The file is modifiable — edit it to add, remove, or change allocations to match your local band plan. Pycom will replace it with the default on start-up only if the file doesn't exist; your changes are never overwritten.
Full 70cm Coverage for Region 2
Region 2's 70cm allocation spans 420–450 MHz, wider than Regions 1 and 3 (430–440 MHz). The band plan strip and frequency labels cover the full range, so Region 2 operators see their complete allocation on the waterfall.
Pycom Development
Pycom has evolved from a repository where every line of code was written manually to a fully agentic development stack. Using opencode — a CLI-based AI coding environment — backed by the Ollama Pro plan with access to the GLM-5.1 Cloud model (currently around £15/month), the project now operates with custom skills, specialised agents, structured expertise management, and automated quality gates that keep the codebase consistent and the protocol layer correct. The combination of opencode's skill system and a capable cloud-hosted model means every session starts with deep project context and ends with consistent, reviewed and tested code.
From Solo to Agentic
In the early days, every feature, bug fix, and protocol implementation was hand-coded and tested against the radio. The codebase has grown to over 25,000 lines across 40+ modules, with a CI-V command reference spanning 542 commands. Managing that complexity manually was becoming unsustainable. Today, opencode is configured with persistent context — AGENTS.md, docs/CODING_STANDARDS.md, and docs/PR_REVIEW_CRITERIA.md — that guides every session. These files encode project conventions (decimal-nibble encoding rules, threading constraints, naming standards, read-only library boundaries) so that the AI agent produces code that is consistent with the existing codebase from the start, rather than needing correction afterwards.
A dedicated expertise manager called mulch records patterns, failures, decisions, and conventions across sessions. Before each task, opencode queries mulch to avoid re-introducing known-bad patterns or repeating investigations that were already resolved. Findings are recorded under domains like gotchas, architecture, connection-flow, and scope-protocol, building a living knowledge base that persists between conversations.
Custom Skills & Tools
opencode skills provide domain-specific expertise on demand. Each skill is loaded contextually — when a question or task matches its domain, the full skill instructions and reference data are injected into the session. The table below shows the skills currently configured for Pycom:
| Skill | Purpose | Mechanism |
|---|---|---|
| CI-V Reference | Look up IC-9700 CI-V commands, hex codes, data encodings, sub-commands, and protocol details | scripts/civ_explore.py — parses the 542-command reference CSV and auxiliary encoding docs. Subcommands: search, lookup, info, encoding, frame, validate |
| Pcap Debugger | Analyse ICOM 9700/7610 Wireshark network traces — decode control, CI-V, and audio streams; inspect socket lifecycle and protocol compliance | pcap_explorer.py — dissects pcap/pcapng files, extracts CI-V command/response pairs, audio streams, and timing data |
| Signal Model Sync | Synchronise, retrain, and verify the ONNX signal classifier between the project and ~/Pycom/signals/ | scripts/sync_signals_dir.sh — runs verification, fine-tuning, and model deployment in one step |
| Model Retraining | Retrain the ONNX signal classifier from labelled captures and deploy to dist | scripts/retrain_model.py — supports --fine-tune from bundled base model and --from-scratch modes |
| Expertise Management | Record and query structured project expertise across sessions | mulch CLI (v0.6.3) — domains: gotchas, architecture, connection-flow, scope-protocol, op-log-level, python-style, test-automation |
| Test Runner | Discover, run, and diagnose tests with full pytest/coverage integration | pytest with SerialMock, flake8 lint classes, venv isolation via scripts/run_tests.sh |
| Persistent Context | Guide every session with project conventions, constraints, and coding standards | AGENTS.md, docs/CODING_STANDARDS.md, docs/PR_REVIEW_CRITERIA.md — loaded automatically by opencode at session start |
Specialised Agents
Beyond skills, opencode is configured with dedicated subagents that handle specific workflows autonomously:
- PR Reviewer — Analyses pull requests and diffs against project standards (coding conventions, threading rules, CI-V protocol constraints, read-only boundaries). Catches architectural violations before merge.
- Docs Maintainer — Audits documentation for staleness, duplication, and inconsistency. When features change, it updates the relevant docs to stay in sync with the code.
- Test Runner — Discovers and runs the appropriate test modules, interprets failures, and suggests fixes. Knows the project's pytest configuration, coverage targets, and SerialMock patterns.
- Explore Agent — Fast codebase search for files, patterns, and architectural answers. Used when you need to find "where is X implemented" or "how does Y work" without reading every file.
Two Repositories
Pycom's codebase is split across two repositories:
- pycom-radio-controller — The main application repository. Contains the GUI, radio controller, signal detection, satellite tracking, configuration, and all the agentic tooling described above.
- ICOM_UDP — A separate repository containing the ICOM UDP transport library. This provides scope data parsing, audio streaming, and network-based CI-V communication for radios with built-in Wi-Fi/Ethernet.
The UDP library is copied into the main repo at lib/icom_udp/ as a read-only dependency. Files are set chmod 444 to prevent accidental edits. All changes must be made upstream in the ICOM_UDP repository first, then synced via scripts/sync_icom_udp.sh. This separation ensures the library can be developed and tested independently while keeping the main repo's copy in sync.
Self-Hosted Gitea
All source control, CI, and issue tracking runs on a self-hosted Gitea instance. The setup provides:
- Git hosting — Private repositories for both pycom-radio-controller and ICOM_UDP, with full branch and merge management.
- CI Action Runner — A Gitea Actions workflow (
.gitea/workflows/pyinstaller.yaml) builds distributable packages on every push tomain. It produces a macOS.appbundle and a Windows.exe, both packaged for distribution. The build excludes dev-only dependencies (pytest, coverage, flake8, torch) via--exclude-moduleflags. - Issue tracking — Bugs, feature requests, and milestone tracking (including the UDP transport migration plan) are all managed in Gitea Issues.
Test Suite
The Pycom test suite is built on pytest with hardware-free testing as a core principle. Since the app communicates with a physical ICOM IC-9700 via serial, every test must work without the radio connected:
| Category | Files | Approach |
|---|---|---|
| Protocol & Encoding | tests/radio/test_pycomIC9700.py | Pure-function tests for BCD, decimal-nibble, and CI-V frame construction — the highest-value tests in the codebase |
| Radio Controller | tests/radio/test_radioController.py | Threaded command queue with SerialMock — verifies queue semantics, timeout handling, and factory creation |
| Signal Detection | tests/signal/ (4 files) | Statistical detector, classifier, patch extraction, training metadata |
| Satellite Tracking | tests/csnsat/test_csnSatManager.py | Doppler rate calculation, offset handling, polling logic with mocked network calls |
| Config & Providers | tests/config/, tests/provider/ | Config round-trips with temp directories, API response parsing with mocked requests |
| Rigctl & RC-28 | tests/rigctl/, tests/rc28/ | TCP command parsing, HID event parsing — no hardware needed |
| Build & Lint | tests/build/test_syntax.py | ast.parse syntax validation + flake8 critical/lint checks integrated as pytest classes |
| Regression | tests/regression/test_known_bugs.py | Known bugs documented as tests — fail until fixed, then become permanent guards |
Every test run creates a fresh virtual environment via scripts/run_tests.sh, installs dependencies, runs the full suite with coverage, and tears down — ensuring no contamination between runs. Coverage targets range from 90%+ for protocol/encoding code down to 40% for GUI code, with an overall target of 75%+.
The Agentic Advantage
The transition from solo manual coding to agentic development has fundamentally changed the pace and quality of Pycom's delivery. In the months since adopting the opencode stack, features that previously took weeks of careful manual implementation — each requiring protocol reference lookups, encoding verification, threading audit, and manual testing against the radio — now ship in days or even hours. The CI-V Explore skill eliminated constant context-switching to the ICOM manual; the pcap debugger turned hours of trace analysis into minutes; mulch ensures that hard-won knowledge about decimal-nibble gotchas and threading constraints is never lost between sessions.
Recent feature velocity illustrates the shift: AI signal detection and classification with a trainable ONNX model and 15-mode classifier, 542-command CI-V coverage with validated encoding reference, full UDP transport library integration for network-based radio control, and a redesign of the main display tab with enhanced support for dual transceivers — all developed and merged with AI-assisted code review, automated testing, and structured expertise management. The test suite alone grew from zero to 45+ test files covering protocol encoding, command queues, signal processing, configuration, and regression guards — something that would have taken far longer to build manually.
The key insight: agentic development doesn't replace understanding — it amplifies it. The project's AGENTS.md, coding standards, and mulch expertise records ensure the AI operates within well-defined constraints, while the specialised agents handle review, documentation, and testing that would otherwise be bottlenecks. The result is a system that allows me to test ideas and ship faster without cutting corners.
Automatic Signal Detection & AI Identification
Pycom can automatically detect signals on the waterfall and classify them using a built-in AI model — no manual scanning required. Configure sensitivity with three presets or eight tuneable parameters, capture signals for review, and train your own model from real off-the-air data. See the Signal Detection, AI Identification, and Signal Reference pages for full details.
Signal Detection
The statistical detector runs in real time alongside the waterfall, finding signal groups that stand out from the noise floor. Choose from Normal, Sensitive, and Strong Only presets, or adjust individual parameters like detection percentile, and minimum contrast Z to suit your operating conditions. Detection is automatically suppressed during transmit to prevent overloading the detector.
Signal Capture & Labelling
Detected signals appear in the capture window sorted by frequency. Select any candidate or drag select a section on the waterfall to inspect a specific signal, then assign a label from the 15 supported signal types — or mark it as Unknown. Add notes, adjust the label, and capture as many examples as you need to build a training dataset.
AI Classification
The built-in ONNX neural network classifies detected signals against 15 mode types including CW, FM, FT8, JT65, SSB, and more. A confidence threshold (default 50%) filters out uncertain predictions. The distributed model achieves 77% validation accuracy on signals with training data at present, and you can reload a fine-tuned model without restarting the app. As I capture more signals, they will be automatically added to new distributions of the application. Contact me on Discord and share your classified signals for them to be included in the application.
Train Your Own Model
Collect captures, label them, and retrain the classifier to recognise the signals you encounter on your own bands. Use python retrain_model.py --fine-tune to add your data to the distributed base model, preserving what it already knows while learning new patterns. Or train from scratch for full control. Quality warnings alert you to class coverage gaps, data imbalance, and accuracy regression before the new model is deployed.
CI-V Command Executor
The Status tab includes a CI-V command executor for sending raw commands directly to your IC-9700. This is useful for experimenting with commands that aren't exposed in the GUI, reading radio state, or diagnosing communication issues.
How It Works
The command field is pre-filled with the CI-V preamble and addresses (FE FE A2 E0 in my case). Enter your command and data bytes after the preamble, add a terminating FD, then click Execute CI-V Command. The radio's response (or an error message) appears in the status text area above.
Examples
| Command | Description | Response |
|---|---|---|
19 00 | Read transceiver ID | Returns the transceiver ID for IC-9700 |
03 | Read operating frequency | BCD-encoded, e.g. 00 00 00 44 01 = 144.000 MHz |
04 | Read operating mode | Mode byte + filter bandwidth |
1C 00 | Read TX/RX status | 00 = RX, 01 = TX |
15 02 | Read S-meter level | Decimal-nibble percentage (0–255) |
14 0A | Read RF power level | Decimal-nibble percentage (0–255) |
14 03 | Read squelch level | Decimal-nibble percentage (0–255) |
16 5A | Read satellite mode status | 00 = OFF, 01 = ON |
The full CI-V command reference contains over 540 commands refer to the ICOM IC-9700 instruction manual for complete details on data encoding.
Comming Soon
The video below shows a fully implemented library that I've created for using the radios built in Wi-Fi/ethernet server over the network. I'm currently working on integrating this with Pycom so that it's no longer tied to a USB connection. Benefits include integrated audio and faster responsiveness including scope.
Measure Base Station performance using S.A.T and FT4 Satellite Loopback
Pycom can log every S.A.T update to with precision, along with WSJTX log files it's possible to create polar plots for multiple satellite passes from your base station. At the default one-second polling interval, each entry captures the satellite's position, applied doppler, and the exact frequencies in use at that moment:
2026-01-30 21:09 INFO [Pycom.lib.csnsat.csnSatManager] Timestamp: 21:09:40, Sat: RS-44, Az: 202.978245, El: -0.136604, Range km: 4338.595298, Main: 435620549, Sub: 145990135, Doppler up: -2865, Doppler down: 8549, Doppler up rate: 0, Doppler down rate: 0, Offset: 0, Tracking: Reverse, Freq Scaling: OFF, RIT: OFF, RIT Freq: 0
Dual WSJT-X Instances
Two instances of WSJT-X run simultaneously — one to generate and transmit FT4, the other to receive and decode the repeated signal. This is set up by duplicating the WSJT-X shortcut, renaming it (e.g. wsjtx - RX), and appending --rig-name=None to the target. Each instance maintains its own log directory, accessible from the file menu.
WSJT-X communication logs are stored in ALL.txt and contain precise timing, frequency, mode, SNR, and offset for each decoded message:
260123_102722 435.118 Rx FT4 -1 0.3 1081 CQ DL3NGN JN59
260123_102730 435.118 Tx FT4 0 0.0 2245 CQ M0SNZ IO91
Post-Pass Analysis
Scripts for post-pass processing and creating polar plots of satellite passes are available at github.com/stevendodd/Sat-Analysis.
USB Remote Operation
Initial Local Installation

Of course you can use the software locally where you install and operate the software on your shack computer.
Audio is available via the USB serial connection. Speakers and microphone connected to the radio should be automatically installed on your operating system when you install the USB drivers.
There is support within Pycom for a local RC-28 controller and you can run doppler tracking or other software on the same computer; however the software comes into its own when you want to start to operate remotely.
Remote Setup
Once you have the software installed on your shack computer, you can use tools such as Microsoft Windows App or MacOS Screen Sharing to view the display and control the computer remotely. Audio can be streamed over the network both to and from the shack computer using tools like Sonobus. There is also support for a remotely connected RC-28 controller. Using a combination of these tools (or alternatives if you prefer) you can have full operational capabilities when you're away from your shack.
Remote Audio Chain
Connecting remote audio involves configuring levels at each component in the chain, including within the Pycom software. In addition to the components documented in the diagram I also pass audio out of the local SonoBus to Blackhole Audio which allows me to record audio for playback later. I have a wired ethernet network between the two computers however it is possible to reduce the audio quality without too much impact for streaming over the Internet.
Development Environment My Specific Configuration
| Component | Detail |
|---|---|
| Shack Computer | HP EliteDesk 800 G3 Small Form Factor Business PC 4 x Intel(R) Core(TM) i5-7600 CPU @ 3.50GHz (1 Socket) 46 GB DDR4 RAM Proxmox Virtual Environment 8.4.1 |
| Operating systems | Window 10 virtual machine with 2 virtual CPUs and 16GB RAM macOS Monterey (Intel) virtual machine with 2 virtual CPUs and 4GB RAM macOS Sonoma (Silicon) Apple M1 with 16GB RAM |
| USB Drivers | Icom USB Drivers installed. For MacOS I used CP210x VCP Mac OSX Driver |
| Remote control software | Windows App macOS Screen Sharing Sonobus RC-28 Server |
| Doppler tracking | CSN Technologies S.A.T (preference) Gpredict SatPC32 |
| Antenna rotation | PstRotator Gpredict Both underpinned by my own custom rotctld implementation |
Making Amateur Radio Accessible
Although there are many reasons why you might want remote control, the motivation for creating this software was a desire to operate satellites specifically linear satellite and finding a lack of support from currently existing software on the market.
In 2018 I had a surfing accident and was pulled out of the sea by a lifeguard from RNLI. I was left paralysed with a spinal cord injury from the neck downwards. I've recovered sufficiently to be able to use a trackpad awkwardly with the middle finger on my left hand however turning dials and pushing buttons on a radio is a little bit beyond me. Using Pycom Radio Controller remotely allows me to operate successfully without limitations.
You can find out more about the origins of the software and Making Amateur Radio Accessible on the RSGB Tonight@8 episode and information about spinal cord injuries from the London Spinal Cord Injury Centre.