Request preparation
What would you like?
Summary
Implement a lightweight SDR plugin/extension subsystem for AetherSDR,
modelled on the extension architecture found in Web-888/KiwiSDR (see
Web-888 Extensions Tab).
The first two bundled plugins would be:
- FT8 / FT4 decoder (background, per-band, reports to PSKReporter)
- WSPR decoder (background, per-band, reports to WSPRnet)
Both are well-proven in embedded/SDR contexts and have excellent open-source
C library foundations we can link directly.
Motivation
AetherSDR already bundles several signal-processing engines
(CwDecoder via ggmorse, RNNoiseFilter, RADEEngine, SpectralNR) as
self-contained src/core/ classes. A formal plugin interface would:
- Allow optional DSP tasks to run against the live audio stream
without touching the main RX/audio path.
- Enable community contributors to add decoders (JS8Call, AX.25,
APRS, P25, …) without modifying core files.
- Mirror the extension UX that Web-888/KiwiSDR users already
understand: enable/disable per extension, callsign + grid
configuration, optional network reporting.
Proposed Architecture
Plugin Interface (src/core/SdrPlugin.h)
class SdrPlugin : public QObject {
Q_OBJECT
public:
virtual ~SdrPlugin() = default;
// Called once after construction; give the plugin its config section
virtual void initialize(const QString& configPrefix) = 0;
// Called by AudioEngine for every decoded audio block
// rate: sample rate (Hz), mono float32, same 24 kHz stream as CwDecoder
virtual void feedAudio(const float* samples, int count, int sampleRate) = 0;
// Human-readable name shown in the plugin applet / settings tab
virtual QString name() const = 0;
// Returns the QWidget* panel to embed in PluginApplet (may be nullptr)
virtual QWidget* createPanel(QWidget* parent) = 0;
signals:
void messageDecoded(const QString& text, double freqHz, double snr);
};
Plugins are registered in RadioModel or a new PluginManager singleton.
AudioEngine::feedAudioData() iterates the active plugin list after its
existing NR2 / RN2 / CW chain — no changes to the VITA-49 decode path.
FT8 / FT4 Plugin (src/plugins/Ft8Plugin)
Library: kgoba/ft8_lib (MIT licence,
pure C, no external dependencies, ~200 KB RAM, proven on STM32F7).
Integration notes:
- FT8 uses 15-second windows; FT4 uses 7.5-second windows.
The plugin accumulates audio internally and triggers a decode at each
window boundary (wall-clock aligned, same as WSJT-X).
- Input: 24 kHz float32 mono from
AudioEngine (already available via
the CwDecoder feed path — same feedAudio() hook).
ft8_lib's demo decoder expects 12 kHz; a single r8brain stage
(already a dependency) downsamples inline.
- Decoded messages are emitted via
messageDecoded() and displayed in a
scrolling QPlainTextEdit panel docked in the AppletPanel.
- PSKReporter upload: optional HTTP POST to
https://www.pskreporter.info/
using QNetworkAccessManager (already used by SmartLinkClient / Auth0).
- Settings (stored via
AppSettings, keys e.g. Ft8PluginEnabled,
Ft8PluginCallsign, Ft8PluginGrid) configured in a new
Radio Setup → Extensions tab.
Bundling strategy (same pattern as RNNoise / ggmorse):
# CMakeLists.txt — add under optional deps block
option(ENABLE_FT8 "Enable FT8/FT4 decoder plugin" ON)
if(ENABLE_FT8)
add_subdirectory(third_party/ft8_lib)
target_link_libraries(AetherSDR PRIVATE ft8_lib)
target_compile_definitions(AetherSDR PRIVATE HAVE_FT8)
endif()
WSPR Plugin (src/plugins/WsprPlugin)
WSPR uses 2-minute windows on even UTC minutes.
Candidate library: twit65/WSPR-library
or the decoder extracted from wsprd (WSJT-X, GPLv3 — check licence
compatibility before bundling; alternatively call wsprd as a subprocess).
Upload target: http://wsprnet.org/post (same QNetworkAccessManager).
GUI / UX
- New EXT toggle button in
AppletPanel toggle row (alongside ANLG, RX,
TX, PHNE, P/CW, EQ).
PluginApplet: tabbed widget, one tab per enabled plugin, showing its
createPanel() widget.
- Radio Setup → Extensions tab (new 10th tab in
RadioSetupDialog):
table of all known plugins with Enable checkbox, callsign field, grid field,
PSKReporter/WSPRnet upload toggle.
- Settings keys follow existing
AppSettings PascalCase convention:
Ft8PluginEnabled, Ft8PluginCallsign, Ft8PluginGrid,
Ft8PluginPskReporter, WsprPluginEnabled, etc.
Audio Path — No Regression Risk
The plugin audio feed is read-only and downstream of all existing DSP:
Request preparation
What would you like?
Summary
Implement a lightweight SDR plugin/extension subsystem for AetherSDR,
modelled on the extension architecture found in Web-888/KiwiSDR (see
Web-888 Extensions Tab).
The first two bundled plugins would be:
Both are well-proven in embedded/SDR contexts and have excellent open-source
C library foundations we can link directly.
Motivation
AetherSDR already bundles several signal-processing engines
(CwDecoder via ggmorse, RNNoiseFilter, RADEEngine, SpectralNR) as
self-contained
src/core/classes. A formal plugin interface would:without touching the main RX/audio path.
APRS, P25, …) without modifying core files.
understand: enable/disable per extension, callsign + grid
configuration, optional network reporting.
Proposed Architecture
Plugin Interface (
src/core/SdrPlugin.h)Plugins are registered in
RadioModelor a newPluginManagersingleton.AudioEngine::feedAudioData()iterates the active plugin list after itsexisting NR2 / RN2 / CW chain — no changes to the VITA-49 decode path.
FT8 / FT4 Plugin (
src/plugins/Ft8Plugin)Library:
kgoba/ft8_lib(MIT licence,pure C, no external dependencies, ~200 KB RAM, proven on STM32F7).
Integration notes:
The plugin accumulates audio internally and triggers a decode at each
window boundary (wall-clock aligned, same as WSJT-X).
AudioEngine(already available viathe
CwDecoderfeed path — samefeedAudio()hook).ft8_lib's demo decoder expects 12 kHz; a single
r8brainstage(already a dependency) downsamples inline.
messageDecoded()and displayed in ascrolling
QPlainTextEditpanel docked in the AppletPanel.https://www.pskreporter.info/using
QNetworkAccessManager(already used by SmartLinkClient / Auth0).AppSettings, keys e.g.Ft8PluginEnabled,Ft8PluginCallsign,Ft8PluginGrid) configured in a newRadio Setup → Extensions tab.
Bundling strategy (same pattern as RNNoise / ggmorse):
WSPR Plugin (
src/plugins/WsprPlugin)WSPR uses 2-minute windows on even UTC minutes.
Candidate library:
twit65/WSPR-libraryor the decoder extracted from
wsprd(WSJT-X, GPLv3 — check licencecompatibility before bundling; alternatively call
wsprdas a subprocess).Upload target:
http://wsprnet.org/post(sameQNetworkAccessManager).GUI / UX
AppletPaneltoggle row (alongside ANLG, RX,TX, PHNE, P/CW, EQ).
PluginApplet: tabbed widget, one tab per enabled plugin, showing itscreatePanel()widget.RadioSetupDialog):table of all known plugins with Enable checkbox, callsign field, grid field,
PSKReporter/WSPRnet upload toggle.
AppSettingsPascalCase convention:Ft8PluginEnabled,Ft8PluginCallsign,Ft8PluginGrid,Ft8PluginPskReporter,WsprPluginEnabled, etc.Audio Path — No Regression Risk
The plugin audio feed is read-only and downstream of all existing DSP: