Logbook

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.

AMSAT satellite status report dialog showing satellite list and submission form

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.

TransformWhat It SimulatesWhy It Matters
Amplitude scalingRF 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 conditionsHighest-impact technique per the literature. Scales noise proportionally to each patch's existing floor, preserving the character of quiet and noisy signals alike
Frequency shiftingImprecise centre-frequency tuningSlides the signal left or right by a few bins; directly complements the center_freq_offset_hz auxiliary feature
Time shiftingPartially-filled scope display after tuningDrops rows from the top and zero-fills the bottom, simulating the display right after changing frequency
Auxiliary feature jitterVariation in measured metadataPerturbs noise floor, frequency offset, and SNR channels slightly; prevents memorisation of exact values for rare classes
Same-class MixupAdditional within-class variationBlends 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.

Band plan strip on waterfall showing IARU region allocations

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.

RegionCoversSource
Region 1Europe, Africa, Middle East, Northern AsiaRSGB
Region 2AmericasARRL
Region 3South Asia, East Asia, OceaniaIARU 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.

Opencode development screenshot

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:

SkillPurposeMechanism
CI-V ReferenceLook up IC-9700 CI-V commands, hex codes, data encodings, sub-commands, and protocol detailsscripts/civ_explore.py — parses the 542-command reference CSV and auxiliary encoding docs. Subcommands: search, lookup, info, encoding, frame, validate
Pcap DebuggerAnalyse ICOM 9700/7610 Wireshark network traces — decode control, CI-V, and audio streams; inspect socket lifecycle and protocol compliancepcap_explorer.py — dissects pcap/pcapng files, extracts CI-V command/response pairs, audio streams, and timing data
Signal Model SyncSynchronise, 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 RetrainingRetrain the ONNX signal classifier from labelled captures and deploy to distscripts/retrain_model.py — supports --fine-tune from bundled base model and --from-scratch modes
Expertise ManagementRecord and query structured project expertise across sessionsmulch CLI (v0.6.3) — domains: gotchas, architecture, connection-flow, scope-protocol, op-log-level, python-style, test-automation
Test RunnerDiscover, run, and diagnose tests with full pytest/coverage integrationpytest with SerialMock, flake8 lint classes, venv isolation via scripts/run_tests.sh
Persistent ContextGuide every session with project conventions, constraints, and coding standardsAGENTS.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 to main. It produces a macOS .app bundle and a Windows .exe, both packaged for distribution. The build excludes dev-only dependencies (pytest, coverage, flake8, torch) via --exclude-module flags.
  • 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:

CategoryFilesApproach
Protocol & Encodingtests/radio/test_pycomIC9700.pyPure-function tests for BCD, decimal-nibble, and CI-V frame construction — the highest-value tests in the codebase
Radio Controllertests/radio/test_radioController.pyThreaded command queue with SerialMock — verifies queue semantics, timeout handling, and factory creation
Signal Detectiontests/signal/ (4 files)Statistical detector, classifier, patch extraction, training metadata
Satellite Trackingtests/csnsat/test_csnSatManager.pyDoppler rate calculation, offset handling, polling logic with mocked network calls
Config & Providerstests/config/, tests/provider/Config round-trips with temp directories, API response parsing with mocked requests
Rigctl & RC-28tests/rigctl/, tests/rc28/TCP command parsing, HID event parsing — no hardware needed
Build & Linttests/build/test_syntax.pyast.parse syntax validation + flake8 critical/lint checks integrated as pytest classes
Regressiontests/regression/test_known_bugs.pyKnown 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 capture and classification

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.

Execute CI-V commands directly from the UI

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

CommandDescriptionResponse
19 00Read transceiver IDReturns the transceiver ID for IC-9700
03Read operating frequencyBCD-encoded, e.g. 00 00 00 44 01 = 144.000 MHz
04Read operating modeMode byte + filter bandwidth
1C 00Read TX/RX status00 = RX, 01 = TX
15 02Read S-meter levelDecimal-nibble percentage (0–255)
14 0ARead RF power levelDecimal-nibble percentage (0–255)
14 03Read squelch levelDecimal-nibble percentage (0–255)
16 5ARead satellite mode status00 = 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.

Multi pass polar plot

USB Remote Operation

Initial Local Installation

Local installation diagram

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 installation diagram

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.

Remote audio chain diagram

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.