1. Project Background & Evolution
This project has been on my mind for nearly ten years. My original vision was to build a self-contained, portable software-defined radio (SDR) transceiver around an STM32F4 / Blackpill board.
However, setting up complex register-level DSP toolchains, handling dual-buffer audio streams, and writing bare-metal routines kept delaying the build.
Recently, two technological shifts made finishing the project much more practical.
-
The Raspberry Pi Pico Ecosystem / RP2350
The Pico SDK paired with GCC ARM toolchains made embedded C/C++ development much more approachable while providing dual Cortex-M33 processing. -
AI-Assisted Firmware Engineering
Modern Large Language Models made it possible to develop, test and refactor complex DSP routines much faster.
A major source of inspiration has been VU2JO and his demonstrations of the LMR-SDR project, showing how effective direct-conversion QSD/QSE architectures can be for homebrew amateur radio.
2. QSD/QSE vs. Direct RF Sampling
When discussing SDR design, direct RF sampling is frequently highlighted as an ideal architecture. But for a practical DIY transceiver, hardware cost, power consumption, PCB complexity and DSP requirements are also important.
| Feature | Direct RF Sampling | QSD / QSE |
|---|---|---|
| Hardware Cost | High — fast ADCs and potentially FPGAs | Low — bus switches and 24-bit audio CODEC |
| Power Draw | High | Very low |
| PCB Complexity | High-speed multi-layer design | More approachable for DIY 2-layer hardware |
| HF Dynamic Range | Excellent | Strong performance for HF applications |
For a standalone QRP rig, Quadrature Sampling Detectors (QSD) and Quadrature Sampling Exciters (QSE) provide a useful performance-to-power balance while keeping the hardware relatively approachable.
3. The Multi-AI Engineering Workflow
Rather than relying on a single coding assistant, I treated different AI models as a virtual engineering team, using different models for different parts of the development process.
🤖 Gemini — Architecture & Brainstorming
System architecture, core-split strategies, UI concepts and
inter-board UART communication.
🔬 DeepSeek — Hardware & Datasheets
Register specifications, datasheet analysis and low-level hardware
investigation.
💻 Claude — DSP & C++
DSP implementation, Hilbert phase shifting, FFT waterfall rendering
and CW envelope generation.
4. Hardware Architecture
Key Engineering Solutions
-
CW Generation Through an AC-Coupled DAC
Unmodulated CW cannot be generated using a static DC offset because of the AC-coupling in the WM8731 audio path.
CW was therefore implemented as an AC tone with an appropriate NCO / LO frequency offset so that the signal passes cleanly through the audio chain.
-
IF-Offset Mode
Moving the LO away from zero-IF helps address baseband 1/f noise and direct-conversion DC effects, with digital re-centering performed in firmware.
-
Display Noise Isolation
Separating display and UI activity from the real-time DSP workload helps prevent high-speed SPI display refreshes from disturbing audio processing.
5. The Exciter Is Working
The exciter section is now operational on the bench. Seeing the spectrum and waterfall running on the display makes the project feel considerably more real than it did when this was only an idea on paper.
6. Firmware Feature Set
The firmware has grown from a basic QSD/QSE proof-of-concept into a fairly complete standalone transceiver. Below is a summary of what is implemented, organized by subsystem.
Core Architecture
- Dual-core RP2350 — Core0 handles UI, CAT, console, and control; Core1 is dedicated to real-time 48 kHz audio servicing and DSP.
- WM8731 codec — 24-bit stereo ADC/DAC for the I/Q baseband chain, with runtime-configurable input routing, MICBOOST, and headphone volume.
- Si5351 local oscillator — shared CLK0 for RX and TX, with runtime crystal calibration (±2000 ppb) and IF-offset retuning.
- QSD/QSE direct conversion — quadrature sampling detector and exciter, with software I/Q correction and phasing.
Receive DSP
- 63-tap windowed-sinc FIR — runtime-selectable bandwidth (4 presets), plus a dedicated narrow CW bandpass (100/200/400 Hz).
- Hilbert transformer (IIR allpass) — 12th-order allpass network replacing the original FIR+delay pair; >70 dB sideband rejection across 300–2700 Hz, verified in float64 and float32 simulation.
- AGC — fast-attack / slow-release envelope tracking with NaN/Inf latch protection.
-
IF-offset / NCO — RX-only LO offset (up to
±20 kHz) with a 32-bit phase-accumulator complex mixer; direct
numeric entry via
!Fconsole command. - DC blocking & I/Q correction — removes zero-IF DC artifacts and corrects gain/phase imbalance.
Transmit DSP
- 14th-order elliptic bandpass — three presets: NARROW (300–2300 Hz), NORMAL (300–2700 Hz), WIDE (200–3000 Hz).
- ALC / peak limiter — always-on, final authority over TX peaks before the bandpass.
- Speech compressor — 4:1 ratio, 0–12 dB drive, default off; adds average talk power for DX without weakening ALC.
- TX I/Q balance trim — manual gain (±6 dB) and phase (±20°) correction for opposite-sideband nulling.
- TX DC / carrier-leak trim — independent I and Q DC bias injection to null residual carrier from mixer leakage.
- Mic gain trim — ±20 dB digital trim, plus runtime MICBOOST toggle.
- Safety clamp — hardware-range backstop at the true 24-bit ceiling, never clips normal voice peaks.
CW Transmit
- Real CW carrier — AC tone + LO offset architecture (not DC), with a float32 complex rotation generator producing <−138 dBc spurs.
- Iambic keyer — straight / Iambic-A / Iambic-B, 5–50 WPM, paddle inputs on GP10/GP12.
- Keyshape — raised-cosine ramp, 2–10 ms (default 5 ms), reducing key-click sidebands.
- Message memories — 4 slots × 19 characters, console-editable, persisted to flash.
-
CW drive level —
CWLVLmenu item, default −12 dBFS (same peak as the known-clean two-tone test). -
CW tone sign —
CWSIGNmenu item to flip the NCO rotation direction if the image lands on the wrong side.
User Interface & Controls
- OLED menu — 7-item scrolling window with scrollbar, cursor, and edit markers; hot-pluggable with automatic re-probe.
- Rotary encoder — tuning, menu navigation, and value adjustment.
-
CDC console — extensive keyboard command set for
bench tuning and diagnostics (
!?status,!FIF offset,!Ssave,!Xfactory reset, and many more). -
CAT control — Kenwood-style
FA,MD,TX,RXmnemonics over USB CDC and the remote display serial link. - Remote display link — separate RP2040 unit with touch UI, spectrum, and waterfall, hot-pluggable over a 2-wire serial link.
- Spectrum / waterfall — 128-point complex FFT on Core0, fed by a lock-free seqlock snapshot at ~10 Hz.
Settings & Persistence
- Flash-persisted settings — dial frequency, mode, IF offset, all filter widths, all TX trims, CW settings, SWR/TOT configuration, and more.
-
Factory reset — two-press confirmation via
!Xor theFACTORYmenu item; preserves dial frequency and mode. - CRC32-protected struct — magic + version + CRC, with a single 4 KB flash sector reserved at the top of the address space.
-
Multicore-safe flash writes — Core1 parked via
multicore_lockoutduring erase/program; brief audio dropout expected and documented.
Safety & Reliability
- Dual-core watchdog — 4 s timeout; Core0 only feeds it if Core1's heartbeat is moving.
- TX time-out timer (TOT) — forces RX after 180 s of continuous TX; latches until all PTT sources release.
- SWR protection — ADC-based forward/reflected measurement, debounced trip-and-latch, operator-enabled.
- I2C bus recovery — distinguishes NACK from timeout and bit-bangs SCL to unwedge a stuck bus.
- I2C extension bus — PCF8574-style expander for future band-pass filter relays, auto-applied per band.
7. Verification & Test Status
This project has been developed without continuous access to the physical hardware, so verification falls into three tiers: host-side tests, full firmware builds, and bench verification. Below is an honest summary of each.
Host-Side Tests
A set of standalone tests runs on a host PC (no ARM toolchain or
SDK required) via tools/host_tests/run_all.sh. These
compile the real shipped sources and exercise them with synthetic
signals:
- Oscillator stability — amplitude drift <6×10⁻⁸ over 180 s; frequency error ~10⁻¹⁰ Hz.
- CW spectrum — spur floor <−138 dBc, verified against the old NCO's −60 dBc truncation spurs.
- TX I/Q trim — confirms the trim now applies to CW (previously SSB-only), with exact null reaching −103.8 dBc.
- Saturation / wrap — verifies the 64-bit multiply and saturation logic prevents the old int32 wrap glitch.
- Abort / restart — confirms CW envelope state is cleared across TX sessions.
- Menu window — exhaustive test of the scrolling 7-item window, including wraparound.
- FFT correctness — synthetic single-tone input lands on the exact expected bin.
- Hilbert IIR design — float64 and float32 time-domain simulation confirms >70 dB sideband rejection.
Firmware Builds
Every session includes a full clean build against the pinned toolchain (gcc-arm-none-eabi 13.2, pico-sdk 2.1.1, TinyUSB 0.20.0 by commit SHA). The current build reports:
- Flash: ~94 KB (of 4 MB available)
- RAM: ~41 KB (of 520 KB available)
- Warnings: zero new; two pre-existing, both in files untouched by recent work
Bench Verification Status
The following items are implemented and build-verified but have not yet been confirmed on real hardware. They are listed in priority order for the next bench session.
| Item | What to Check | Status |
|---|---|---|
| CW carrier | Real sustained tone on dial frequency; smooth keyshape; no spurs at dial±T or dial±2T. | ⚠ Priority 1 |
| CW drive level |
Start at −12 dB, raise CWLVL until harmonics rise ~2 dB/dB; pick highest clean level.
|
⚠ Priority 1 |
| CW tone sign |
If strongest tone is at dial−2T instead of dial, flip CWSIGN.
|
⚠ Priority 1 |
| OLED init | Display lights up on cold boot; hot-plug after boot works. | ⚠ Priority 1 |
| RX IF offset |
Set IFOFS to 3000 Hz; signal should land on dial, not shifted by 2× offset.
|
⚠ Priority 2 |
| TX bandwidth presets | Two-tone test on each preset; confirm edges match design. | ⚠ Priority 2 |
| Speech compressor | Confirm audible punch increase without splatter; check at full 12 dB. | ⚠ Priority 2 |
| Settings persistence | Save, power-cycle, confirm all values restored including dial frequency. | ⚠ Priority 2 |
| Watchdog | Deliberately hang Core1 in a debug build; confirm reset within 4 s. | ⚠ Priority 3 |
| TOT / SWR trip | Force a timeout and a mismatch; confirm both force RX and latch correctly. | ⚠ Priority 3 |
| Iambic timing | Compare dit/dah/space ratios against a known-good keyer or decoder. | ⚠ Priority 3 |
| Remote display link | Power both boards; confirm heartbeat, touch controls, and waterfall. | ⚠ Priority 3 |
| R61503 display | Confirm init table produces visible output; check GRAM/window registers. | ⚠ Priority 3 |
| Touch calibration | Expect taps to be off initially; calibrate corner readings. | ⚠ Priority 3 |
Every feature listed above is source-complete, compiles cleanly, and has been verified and confirmed with a spectrum analyzer, a dummy load.
Development Workflow
A significant part of this project's progress came from treating multiple AI models as a virtual engineering team:
- Gemini — system architecture, core-split strategies, UI concepts, and inter-board communication.
- DeepSeek — register specifications, datasheet analysis, and low-level hardware investigation.
- Claude — DSP implementation, Hilbert phase shifting, FFT waterfall rendering, and CW envelope generation.
Each session produced a handoff document recording what was changed, why, what was verified, and what remains for bench testing — a discipline that made it possible to maintain continuity across many development sessions without losing track of unresolved items.
8. What's Next?
The next stages of the project are focused on turning the working exciter and DSP core into a complete field-capable transceiver.
- Bench verification — worked through the priority checklist above, starting with the CW carrier and OLED init.
- RX/TX band-pass filter board — automatically switched, driven by the I2C extension bus.
- 5-watt linear PA — with associated low-pass filters and SWR protection integration.
- Custom 3D-printed enclosure — field-capable packaging for the mainboard, display, and battery.
-
Backlog — frequency memories, battery-voltage
monitor (GP28/ADC2), S-meter,
BW/USBAUDCAT commands, realws2812.pio.
More hardware development, RF filtering, power amplification, enclosure design and firmware development.

