WSPR-Transmitter is a C++20 component for generating WSPR (Weak Signal
Propagation Reporter) RF transmissions with precise symbol timing.
The current production-proven path is a Raspberry Pi backend that uses Broadcom mailbox, DMA, PWM, and clock hardware to emit RF. The codebase has been refactored so transmission control and scheduling live in a generic controller, while platform-specific hardware work lives behind a backend interface.
The component can:
- Configure a WSPR transmission from RF frequency, power level, PPM correction, and optional callsign/grid/power message fields
- Run either continuous-tone output or full WSPR symbol transmissions
- Schedule WSPR transmissions on proper window boundaries
- Stop, reconfigure, and restart safely
- Monitor backend-specific hardware faults and recover through backend-owned recovery logic
The public facade is WsprTransmitter. Higher-level code uses that class and
does not need to manipulate DMA, PWM, mailbox, or watchdog details directly.
The architecture is split into three layers:
WsprTransmitter owns:
- Public API
- Transmission configuration state
- Scheduler and worker-thread orchestration
- High-level transmission state machine
- Symbol timing decisions
- Callback delivery
The controller decides when a transmission starts and when each symbol should be emitted. It does not own backend-private hardware sequencing state.
WsprTransmitBackend defines the hardware-facing contract used by the
controller. The interface is intentionally expressed in generic transmission
operations rather than Raspberry Pi DMA concepts.
Shared backend-neutral data types live in wspr_transmit_types.hpp:
WsprTransmissionPlanSnapshot of transmission intent and configuration passed from controller to backend. It currently includes:- RF center frequency in hertz (Hz)
- Tone spacing in hertz (Hz)
- Logical output power level
- Symbol count
WsprTransmissionConfigureResultSmall result returned by backend configuration. It currently reports the applied RF center frequency in hertz (Hz) after backend quantization or hardware adjustment.
WsprRpiBackend owns all Raspberry Pi-specific implementation details,
including:
- Mailbox allocation and peripheral mapping
- DMA control-block setup and ring management
- PWM / GPCLK programming
- GPIO drive-strength based output control
- DMA watchdog monitoring
- Watchdog recovery and hardware reset behavior
Those details are intentionally kept out of the generic controller/backend boundary.
The backend model is designed so additional hardware implementations can be added without changing the controller's scheduling and transmission logic.
Current backend responsibilities:
- Prepare hardware resources for a configured transmission
- Apply the transmission plan to hardware
- Report the applied configuration back to the controller
- Enable and disable RF output
- Emit controller-scheduled symbols
- Handle backend-private fault monitoring and recovery
Current controller responsibilities:
- Build the transmission plan
- Determine symbol timing
- Drive lifecycle sequencing
- Maintain high-level state
- Coordinate stop and restart behavior
At a high level, the lifecycle is:
configure(...)The controller stores user configuration, derives WSPR symbol data when message mode is selected, and builds the backend-neutral transmission plan.prepareTransmission()The backend allocates or initializes hardware-specific resources.configureTransmission(plan) -> resultThe backend applies hardware configuration and returns the actual RF center frequency in hertz (Hz) that will be used.startAsync()The controller starts either immediate tone transmission or the WSPR scheduler, depending on mode.beginTransmissionOutput(plan)The backend enables RF output.emitSymbol(plan, sym_num, tsym, symbol_index)The controller calls this once per scheduled symbol.tsymis expressed in seconds. In continuous-tone mode,tsymis0.0.endTransmissionOutput()The backend disables RF output.cleanupTransmission()Backend resources are released during shutdown or reconfiguration.
This split keeps timing control in the controller while leaving hardware sequencing private to the backend.
src/
main.cpp Demo / test harness
Makefile Standalone build rules
wspr_transmit.hpp Controller / facade public API
wspr_transmit.cpp Controller implementation
wspr_transmit_backend.hpp Generic backend interface
wspr_transmit_backend_rpi.hpp
wspr_transmit_backend_rpi.cpp
Raspberry Pi backend
wspr_transmit_types.hpp Shared state, plan, and result types
This project expects sibling modules that provide:
- WSPR message encoding
- Broadcom mailbox access and DMA-safe memory allocation
The current standalone Makefile includes those submodule paths when present.
cd src
make debugCommon targets:
debugbuilds the debug test binaryreleasebuilds an optimized binarycleanremoves build artifacts
The Raspberry Pi runtime path requires appropriate privileges for low-level hardware access.
The Debian backend contracts workflow runs the simulator and transmission
controller contract tests in an isolated Debian Trixie checkout. Those tests
are standalone and require no hardware or elevated privileges.
Si5351 transition, startup-quiescence, full debug-build, and physical-backend regressions still require the dependency topology recorded by the parent WsprryPi repository. The parent non-hardware workflow runs those tests against the exact pinned sibling submodules. Neither workflow performs installation, service management, transmitter device access, live transmission, or RF qualification.
- RF frequency values are expressed in hertz (Hz)
- Symbol duration values are expressed in seconds
- Power reporting helpers return estimated milliwatts (mW)
- The Raspberry Pi backend is the only production-verified backend at present
- Controller/backend refactor complete
- Generic backend interface in place
- Raspberry Pi RF path re-verified after refactor
WsprTransmissionPlanandWsprTransmissionConfigureResultare part of the stable shared interface
See source file headers for license terms.