Releases: ten9876/AetherSDR
AetherSDR v0.9.7 — CW keying overhaul + reliability sweep
CW keying overhaul + reliability sweep
A focused follow-up to v0.9.6 with two themes: a substantial CW operator
upgrade — keyboard and MIDI-mapped straight key + iambic paddles, full
break-in / QSK respect on both paths, netCW timing fixes, and Apollo-era
Quindar tones on PTT engage/disengage — and a reliability sweep covering
SpotHub auto-reconnect, a TCI crash on quit, the long-standing waterfall
TX trail, hardware PTT regressions, and DAX RX latency.
Big thanks to @jensenpat (CW keyboard / MIDI controls, compression
meter gating, Windows MSVC runtime), @M7HNF-Ian (Spot Lines,
HiDPI gauge clipping, TCI crash on quit, SpotHub auto-reconnect),
@filemakers (connect-to-last-radio opt-out checkbox), @NF0T
(RADE TX policy naming + tests), @chibondking (live voltage gauge),
plus issue reporters @vu2cpl (TCI crash with full call stack),
@luigiverdicchio1-prog (SpotHub failed-reconnect repro), @LU5DX
and @rnash2 (spectrum amplitude scale issue — under investigation).
New features
CW keyboard + MIDI controls (#2361, #2391, jensenpat)
- Three new shared shortcut + MIDI actions:
Trigger straight key,
Trigger CW Left Paddle,Trigger CW Right Paddle. Same action IDs
and display names for keyboard shortcuts and MIDI mappings (cwkey/
cwdit/cwdah);MidiSettingsmigrates legacy dotted IDs
(cw.key/cw.dit/cw.dah) on read. - Straight key is a true momentary control: keyboard press + MIDI gate-on
assert the netCW key path, release + gate-off release it. Paddles feed
the local iambic keyer when running so sidetone and on-air timing stay
aligned with WPM; falls back to a held-key path otherwise. - Both paths now honor the radio's
break_insetting fully — with
break_in=1(QSK), key edges trigger TX andbreak_in_delayholds
the relay between elements; withbreak_in=0, keys are queued and the
operator engages PTT manually (Space PTT, MOX, hardware PTT). The
previous auto-PTT envelope masked break-in OFF and killed QSK hang
time on release. - New clickable red TX status badge in the status bar acts as an
emergency transmit cancel — checks Multi-Flex ownership, clears all
local CW state, forces sidetone key-up, and sendscw key 0/
cw ptt 0/transmit tune 0/xmit 0. - Slider focus lease no longer blocks momentary CW shortcuts or Space
PTT — clicking the CW delay slider previously froze J/K/L paddle
keying and Space PTT for 2+ seconds while the lease's arrow-key
capture timer expired. - Pre-existing typo fix: J tile in the on-screen keyboard widget was
bound toQt::Key_I, so binding a shortcut to J in the editor
selected I.
Quindar tones — Apollo-era K/BK on PTT (#2334, fixes #2262)
- Optional 2525 Hz "K" / 2475 Hz "BK" tones on PTT engage / disengage,
modeled after Apollo CapCom audio. Lock-free DSP module wired between
PC mic gain and the final TX limiter so tones are mixed into the
outgoing audio. Local sidetone via a dedicated 48 kHzQAudioSink
(mutually exclusive with CW mode at the mode level — Quindar and CW
share the same sidetone bus). - Added as the QUIN chip in the Final Output Stage panel of the
Aetherial Audio Channel Strip with a frameless editor dialog
(test-tone buttons + Done in the same row as the title bar). - Disabled by default; opt-in via the chip toggle.
netCW keying fix + trace logging (#2336, fixes a long-tail of issues)
- Fixes netCW timing for both straight-key and iambic CW. Adds detailed
aether.cwtrace category logging across MIDI / keyboard input,
netCW UDP scheduling, VITA stream/index details, and TCP fallback /
backstop paths so future timing regressions are diagnosable from logs.
Spot Lines toggle in SpotHub (#2349, M7HNF-Ian)
- New "Spot Lines" toggle in the SpotHub Display settings draws vertical
lines from the spectrum up to each spot label. Off during contests to
reduce visual clutter; on for casual ops to keep label-to-frequency
mapping legible at a glance.
Network Diagnostics live logs tab (#2333)
- New tab in the Network Diagnostics dialog with live tail of the same
logs the diagnostics report references, scoped to the diagnostic
categories (aether.connection,aether.dxcluster,aether.cw,
etc.). Lets users diff what they're seeing against what was logged
without leaving the dialog.
Connect-to-last-radio opt-out checkbox (#2390, filemakers)
- New "Connect to last radio on start up" checkbox on the connection
dialog; defaults to ON so existing users keep current behavior. When
unchecked, AetherSDR no longer auto-connects on startup, on
broadcast-discovery, or on routed-radio probe — useful for operators
who want to pick a radio manually each session. The dialog
auto-launches at startup when opt-out is enabled so the user has a
clear path to pick a radio.
Bug fixes
SpotHub auto-reconnect after failed connection attempt (#2394, M7HNF-Ian, fixes #2380)
- SpotHub (DX cluster / RBN) didn't automatically reconnect after a
Wi-Fi drop or any failed connection attempt — Qt'sQAbstractSocket
only emitsdisconnected()on Connected → Unconnected transitions,
so when the socket failed duringConnectingState(host blocked,
refused, or timed out), the reconnect timer was never armed. Extracts
ascheduleReconnect()helper called from all three failure paths
(live drop, socket error, connect timeout) with guards against
double-scheduling. A per-call epoch counter prevents stale timeouts
from a previous attempt aborting a later successful one. Backoff
sequence unchanged: 5 s → 10 s → 20 s → 40 s → 60 s.
TCI crash on quit when TciServer outlives RadioModel (#2386, M7HNF-Ian, fixes #2385)
TciServerwas constructed as aQObjectchild ofMainWindow, so
Qt deleted it during~QWidget::deleteChildren()— which runs after
MainWindow's value members (includingm_radioModel) have already
destructed.~TciServer()→stop()→releaseDaxForTci()then
dereferenced freed memory. EXC_BAD_ACCESS @0x38, 100% reproducible
on quit when a radio was connected. Reported by VU2CPL with full call
stack. Fixed by explicitly tearing downm_tciServerin
~MainWindow()after the audio thread is stopped but while
m_radioModelis still alive, plus a belt-and-braces
QPointer<RadioModel>so the existing null guards in
releaseDaxForTci()catch any future regression automatically.
Waterfall unfreezes on radio interlock state, not MOX edge (#2368, fixes #1927)
- Waterfall freeze/unfreeze previously gated on the local MOX edge,
which fired the instant the user released PTT — the radio kept
streaming TX-contaminated tiles for theUNKEY_REQUESTEDwindow, and
those rows then took 10–23 s to scroll off the visible waterfall.
Now driven byRadioModel::radioTransmittingChanged(interlock
state=TRANSMITTING), so the freeze is held until the radio
actually leaves TX. Multi-Flex bonus: any client TXing now triggers
the freeze, not just our client.
Honor cw break_in for keyboard CW keying (#2391)
RadioModel::sendCwKeyand the iambic keyer'sonPaddleEventboth
unconditionally wrapped each key/squeeze in acw ptt 1...cw ptt 0
envelope. Made break-in OFF a no-op (auto-PTT forced TX anyway) and
killed the radio's break_in_delay hang time with break-in ON
(force-dropped PTT after every element). Stripped the auto-PTT in
both paths so the radio's break-in setting decides TX behavior,
matching SmartSDR semantics.
Spot list double-click switches mode (#2372, fixes #2298)
- Double-clicking a spot in the Spot List now switches the radio's
mode along with the frequency. Previously only the frequency moved,
leaving the user in the wrong mode for the spotted signal.
Spot trigger includes pan= for external CAT clients (#2369, fixes #2366)
- The spot click protocol now includes
pan=<panId>so external CAT
clients (N1MM Logger, etc.) consuming AetherSDR's spot triggers can
route the click to the correct panadapter in multi-pan setups.
Compression meter gates on radio TX state (#2363, jensenpat)
- The TX compression meter now displays only while the radio reports
state=TRANSMITTING. Previously it could read live during RX from
stale interlock state, confusing operators about what compression
was actually applied to their signal.
Voltage gauge label shows live radio voltage (#2362, chibondking)
- The voltage gauge label now reflects the live voltage value reported
by the radio instead of the static "VOLTS" placeholder.
Minimal mode revert on macOS when entered while maximized (#2367, fixes #2365)
- Entering minimal mode while the window was already maximized on
macOS would revert to a non-minimal layout immediately on toggle.
Layout / state ordering corrected so the entry sticks regardless of
the prior maximized state.
MidiControlManager: drop dead paramAction signal (#2370)
- Cleanup follow-up after #2336 introduced
paramActionTrace—
paramActionwas still emitted but had zero connectors. Removed
the orphan signal + matching declaration.
Infrastructure
DAX RX native pw_stream source on Linux (#2312, fixes #1008)
- DAX RX latency on Linux drops from ~400 ms → ~200 ms via a native
PipeWirepw_streamsource path, replacing the previous PulseAudio
client. Brings DAX RX latency in line with macOS / Windows.
RADE TX policy naming + tests (#2353, NF0T, fixes #2343)
- Renames the RADE TX policy enum to align with the issue tracking
language (HostedDaxBridgeetc.) and adds unit tests covering each
(reason, platform, mode) → (allowed, note)decision row.
HiDPI gauge clipping (#2346, M7HNF-Ian)
- HGauge tick labels and TxApplet value labels were clipping on HiDPI
displays. Layout / paint regions updated to honor device-pixel ratio.
Windows portable ZIP includes MSVC runtime (#2364, jensenpat)
- Windows portable ZIP now bun...
v0.9.6 — Aetherial Audio Channel Strip + AetherSweep Phase 2
Aetherial Audio Channel Strip + AetherSweep Phase 2
Headline additions: a unified Aetherial Audio Channel Strip that brings
every TX DSP stage (gate, EQ, compressor, de-esser, tube, AetherVoice,
reverb, and a brand-new Final Output Stage with brickwall limiter) into a
single editable window with a savable preset library — and AetherSweep
Phase 2, a polish pass on the in-panadapter SWR analyzer with log scale,
threshold-band shading, interpolated bandwidth at SWR ≤ 1.5 / 2.0, a
resonance caret, and band-change-aware auto-stop. Network Diagnostics
gained trend graphs and per-series gutter hints. RADE finally ships in
the official Windows installer.
Big thanks to @jensenpat (oscillator reference fix, AetherSweep
Phase 2, Connect-by-IP recents, minimal mode polish, Windows installer
packaging), @NF0T (RADE on Windows CI/installer, RADE mic meter
fix), @rfoust (Network Diagnostics trend graphs), and
@AetherClaude (stderr non-draining-pipe deadlock) for landing the
bulk of this release.
New features
Aetherial Audio Channel Strip (#2307, #2326)
- New unified TX DSP window covering every stage in the chain: gate, EQ,
compressor, de-esser, tube, AetherVoice (the exciter formerly known as
PooDoo), Freeverb, plus a brand-new Final Output Stage with brickwall
limiter, output trim, DC block, and a 1 kHz test tone. - Savable preset library at
~/.config/AetherSDR/ChannelStrip.settings
(separate fromAetherSDR.settings). Captures every TX DSP knob, the
user's chain order, and final-limiter parameters. Save / Delete /
Export Preset / Export Library buttons in the strip's preset row. - Master/Aux BYPASS button stays in lock-step between the docked Chain
applet and the strip via a shared engine-level snapshot — flipping it
in either place updates both. - Two new 4th-row panels: Aetherial Waveform — TX (1–20 s scope window
with SCOPE/ENVELOPE/HISTORY modes) and Final Output Stage (peak-hold
meter with GR overlay + draggable amber ceiling triangle, mouse-wheel
±0.1 dB). - Logo / title rebrand: PooDoo™ exciter → AetherVoice™ "Aetherial
Voice Processor" with Body / Clarity emphasis controls. Docked tile
(PUDU) renamed toEVOon the chain widget; container button
renamedVUDU. - Double-click any TX chain tile launches the Channel Strip — replaces
the legacy easter-egg launch nub on the Chain applet and the
per-stage TX floating editors (ClientDeEssEditor,
ClientReverbEditor, etc.). - FreeVerb tile in the docked applet panel now shows the same
decay-tail viz as the Channel Strip's reverb panel, signal-driven by
a newAudioEngine::clientReverbStateChangedsignal (no polling).
AetherSweep Phase 2 (#2320, jensenpat)
- Logarithmic SWR scale for cleaner low-ratio detail; threshold-band
shading for SWR 1.0–1.5 (green), 1.5–2.0 (amber), and >2.0 (red). - Resonance caret + dot at the best measured SWR point; visual
start/end notches mark sweep endpoints. - Interpolated bandwidth brackets at SWR ≤ 1.5 and ≤ 2.0, with a
concise BW readout in the corner label. - Resonant frequency display trimmed to kHz precision; corner readout
switched from "latest sample" to best SWR + resonance frequency. - Cleared sweep plots when the swept TX slice changes bands; if a band
change happens while a sweep is running, the sweep stops and clears
without restoring the old freq/pan range so cleanup doesn't fight
the user's new band.
Network Diagnostics trend graphs (#2309, rfoust; #2316)
- Per-metric trend plots with Timeframe selector (1 min / 5 min /
15 min / 1 h / 1 d / 1 w). - Logarithmic Y-axis on the Rates tab — 0 / 1 / 10 / 100 / 1k kbps
decades all visible at once instead of being squashed at the
baseline. - Per-series last-sample hints in the left gutter — small
color-coded values that always show the latest reading for each
visible stream, with a 6 px-tall solid centerline alpha gradient
to keep the labels legible against the trace. - Frameless chrome with draggable title bar + 8-axis resize matching
the rest of AetherSDR's floating windows.
RADE in the official Windows installer (#2324, NF0T)
ENABLE_RADE=ONfor both the Windows CI job and the official
installer workflow. Vendored RADE-prepared Opus + neural-net
weights are statically linked, so no new DLLs ship —AetherSDR.exe
grows ~8–12 MB to match the AppImage.- New
Build Opus (RADE dependency)CI step provides clean failure
attribution and matches the AppImage workflow's pattern. Closes
reports of "RADE absent from Windows installer".
Connect by IP recents dropdown (#2296, jensenpat)
- Connect-by-IP field is now a combo box pre-populated with the last
five addresses (stored asRecentDirectIpAddresses). Successful
connect promotes the address to the top of the list.
Black slider auto-offset (#2328)
- Spectrum Overlay menu's Black slider now drives a noise-floor target
offset while AUTO is engaged: 50 = at the noise floor (today's
behaviour); lower = darker (push threshold above the floor); higher
= lighter. Manual mode keeps existing semantics. Both stored
values persist independently so toggling AUTO swaps the slider
position without losing either preference.
Minimal mode polish (#2290, jensenpat; #2299)
- Title bar drag now reaches the gutter and other previously-dead
zones via sub-pixel hit testing. Exit paths from minimal mode
consolidated; layout stops fighting on toggle.
Aetherial Noise Reduction docked applet (#2297)
- Client-side NR controls live in their own docked applet under
PooDoo Audio (RX); the redundant DSP sub-panel was removed from
the Spectrum Overlay menu for a cleaner DSP surface.
Bug fixes
Oscillator reference status no longer goes stale (#2329, jensenpat)
- New
RadioModel::oscillatorChanged()signal drives the status-bar
reference label and Radio Setup combo immediately from oscillator
state, instead of relying on event ordering with GPS status.
Eliminates the case where the status bar got stuck on the previous
reference if a GPS update never arrived after the oscillator
transition. - Radio Setup dropdown preserves currently-selected/actual options
during presence transitions, so transient flag blips no longer
drop the active option from the combo. RenamedExternal→
External 10 MHzto match the FlexLib API description. - Status-bar tooltips now show desired setting, actual source, lock
state, and GPS/external details.
RADE mic level meter + gain slider (#2292, NF0T)
- The RADE TX mic level meter now updates while the modem is active,
and the gain slider is enabled instead of stuck at zero. Fixes
"no apparent way to set RADE mic level" reports.
Stderr non-draining pipe deadlock (#2300, AetherClaude)
- When stdout/stderr was redirected to a pipe with no reader, the
audio worker thread could deadlock writing log lines. Replaced
blocking writes with a non-blocking path that drops on the floor
rather than stalling the thread.
Windows installer runtime packaging (#2303, jensenpat)
- Tightened Windows runtime-DLL bundling for a smaller installer and
fewer "missing DLL" reports on first launch.
Infrastructure
v0.9.5.1 release-notes expansion (#2288, #2289)
- The CHANGELOG entry for v0.9.5.1 was expanded post-release to
cover all post-v0.9.5 fixes (#2113 reachability sweep, the four
rfoust polish PRs, NR2 audio-thread + Qt-log hotfixes) with full
contributor shoutouts.
v0.9.5.1 — Stability & polish hotfix sweep
Stability & polish hotfix sweep
A focused follow-up release that lands the TCI TX policy hotfix plus six
post-v0.9.5 fixes already in main: SmartLink WAN reconnect after radio
drops, sequenced WAN disconnect teardown, RX slice-tab reset between
radios with different slice counts, macOS panadapter pop-out live
updates and dock-splitter layout, NR2 wisdom-generation safety on the
audio thread, and a Qt log-handler serialization fix that resolves a
macOS tune-time crash.
Big thanks to @rfoust (four SmartLink / disconnect / macOS polish
fixes) and @jensenpat (NR2 audio-thread safety + Qt log
serialization) for landing the bulk of this release.
Bug fixes
TCI TX silent on Windows / Linux non-PipeWire (#2276)
evaluateDaxTxPolicy()now always allowsDaxTxRequestReason::TciTxAudio
regardless of platform / hosted-DAX availability. TCI receives audio
over WebSocket and feeds it into a dedicateddax_txstream that's
independent of SmartSDR DAX2 (which owns the Windows DAX audio
devices, not the radio'sdax_txstream slot — multiple GUI clients
can each register their own).- Test assertions in
tests/radio_status_ownership_test.cppflipped to
match the corrected policy and a new Linux-non-PipeWire test case
added.
SmartLink reconnect after WAN drop (#2282, rfoust)
MainWindownow owns a WAN reconnect timer that re-requests a
SmartLink radio connection using the last selected WAN radio when
RadioModelreports an unexpected WAN disconnect, instead of
leaving the app stuck on the "Radio disconnected — waiting for
reconnect" popup.SmartLinkClient::reconnect()refreshes Auth0 credentials via the
saved refresh token before reconnecting, avoiding reuse of an
expiredid_token. Auth-refresh failure stops the retry loop and
shows a sign-in-required status instead of retrying forever.RadioModel::forceDisconnect()now handles WAN connections so
missed pings transition the app to disconnected/reconnecting
promptly. Ping watchdog logs and forces disconnect once per outage
rather than every second.PanadapterStack::prepareShutdown()releases QRhi GPU resources
before main-window teardown to avoid the macOS Metal teardown crash
that could fire after a stale-state Disconnect.
SmartLink disconnect teardown (#2278, rfoust)
- WAN disconnect previously closed the WAN socket after disconnecting
RadioModel's signals and nulledm_wanConndirectly, skipping
RadioModel::onDisconnected()cleanup entirely. Panadapters,
slices, meters, and streams stayed alive in the model after the user
clicked Disconnect. - Now runs the normal model teardown path on intentional WAN
disconnect, so WAN sessions emitconnectionStateChanged(false)and
clear model state the same way LAN disconnects do.
Reset RX slice tabs on disconnect between radios with different slice counts (#2254, rfoust)
RxApplet::clearSliceButtons()tears down generated slice tab buttons
and restores the static slice badge on disconnect. Stale A–H buttons
no longer linger after switching from a high-slice radio to a
smaller one.MainWindow'sinfoChangedinitializer is now per-connection rather
than one-shot, so each radio rebuilds its slice row from its own
maxSlices. Slice button click connections are guarded against
duplicate signal handlers across reconnects.
Qt log handler serialization fixes macOS tune-time crash (#2284, jensenpat)
- The global
qInstallMessageHandlercallback wrote through a single
QFile*without synchronization, and concurrentqCInfo/qDebug
output from main + worker threads corrupted Qt's internal file
write-buffer state. The tune-policy diagnostic line happened to be
the log call that exposed the corruption — the failing object was
the logging sink, not the Flex tune command path. - Now serializes the handler with an intentionally leaked mutex
(mirroring the existing shutdown-safe treatment of the redaction
regexes) and replaces per-messageQTextStreamwrapping with a
direct UTF-8QFile::write()/flush()path. No change to radio
command ordering or panadapter policy.
macOS panadapter pop-out refresh + multi-pan dock layout (#2280, rfoust)
- Detached panadapter windows on macOS no longer show a static/stale
spectrum image. Cross-window reparenting now resets the native
QRhi/Metal surface and re-requests pan dimensions from the radio
after every float/dock cycle. - Saved floating-window state is no longer restored after later
user-added pans, so adding a second panadapter does not spawn an
unwanted blank floating window. rebuildDockedSplitter()keeps the main-window splitter compact
when multiple pans float/dock — no more empty placeholder slots.
NR2 wisdom generation no longer freezes the audio thread (#2275, jensenpat)
AudioEngine::needsWisdomGeneration()previously only checked
whetheraethersdr_fftw_wisdomexisted. If the file was stale or
incompatible (e.g.fftw-3.3.10header on a build that uses FFTW
3.3.11),setNr2Enabled()ran full FFTW wisdom generation on the
audio worker thread, blocking RX audio and the WAVE scope for
several minutes.- Adds
SpectralNR::loadWisdom()for import-only validation, makes
needsWisdomGeneration()return true when the file exists but
cannot be imported, and routes generation through the existing
background progress dialog instead of the audio thread. If wisdom
import fails at enable time, NR2 falls back to runtime
FFTW_MEASUREplans rather than hanging audio.
v0.9.5 — Reliability sweep + DAX2 coexistence overhaul
[v0.9.5] — 2026-05-02
Reliability sweep + DAX2 coexistence overhaul
A focused stability release. Headline is DAX2 coexistence policy
(jensenpat) — a centralized decision table that makes AetherSDR's
behaviour next to SmartSDR DAX, hosted DAX (macOS / PipeWire), and
external Flex tooling explicit, testable, and consistently logged.
Plus substantial improvements to disconnect teardown (sequenced
stream remove with response wait — fixes the Flex-6300 unreachable-
after-reconnect bug), worker-thread shutdown (no more killTimer
warnings on app exit), panadapter zoom (no spectrum flash), and
multi-flex spot/ATU handling (DXCluster spot trigger_action removal,
ATU per-frequency toggle).
A two-PR issue triage cleanup closed 12 long-standing issues across
small UI bugs, contrast, indicator routing, click-to-tune behaviour,
and visible regressions from recent feature work.
Features
Aetherial Parametric EQ Smoothing combo (#2236)
- New Smoothing combo on the EQ analyzer with 1/24, 1/12, 1/6, 1/3,
1/1 octave fractional smoothing options (linear-power averaging).
Persists viaEqAnalyzerSmoothing; defaults to 1/12 octave. - Audio-domain band-plan strip on the EQ canvas + dashed yellow filter
cutoff guides for both TX and RX. Cutoff guides are draggable to
retune filter edges with mode-aware USB/LSB/AM/FM offset conversion. - Phone applet Low/High Cut buttons snap to nearest 50 Hz.
- ~30 % faster smoothing inner loop after replacing
std::pow(10, x/10)withstd::exp(x * ln(10)/10)(#2239).
Custom filter edges with persistence (#2272)
- Right-click a filter button → Set Custom Edges... opens a
2-spinbox dialog. Asymmetriclo:hipairs persist in
FilterPresets_<mode>via a new mixedwidth/lo:historage
format (backward compat preserved for existing width-only entries). - Reset to Default is now per-slot.
- FilterPassbandWidget shows signed lo/hi/center values so LSB/CW
filters reveal sideband. - Filter-edge grab zone in the spectrum bumped from 5px to 8px so
edges sitting near the VFO line are easier to grab.
Spot click client-side mode mapping (#2272)
- All client-ingested spots (DXCluster, RBN, POTA, SpotCollector,
FreeDV, WSJT-X, manual) now ship withtrigger_action=none. The
radio no longer auto-acts on stored mode strings (which mishandled
"SSB" and similar non-Flex tokens, falling back to CW). - AetherSDR's auto-mode mapping handles tune+mode client-side.
Default flipped to enabled. - FreeDV-source spots activate the RADE engine via
activateRADE()
(sets DIGU/DIGL plus the OFDM modem) instead of just landing on a
plain digital mode.
ATU per-frequency toggle (#2272)
- ATU button now mirrors SmartSDR's behaviour: first click on a
frequency tunes; second click at the same frequency bypasses; freq
change resets the toggle so the next click tunes again.
Bug fixes
DAX2 coexistence policy (#2271, jensenpat)
- New
DaxTxPolicy.hdecision table:(reason, platform, mode) → (allowed, note). Centralizes platform-aware DAX TX decisions with
explicit reasons (HostedDaxBridge, TciTxAudio, ExternalDaxRouteOnly,
GenericAudioRecreate) and consistent log notes at every branch. - Lazy
dax_txstream creation viaRadioModel::ensureDaxTxStream()
— eager creation at GUI attach is removed so SmartSDR DAX can own
the Windows route. daxTxStatusCanUpdateLocalState()anti-stomp: foreign DAX TX
status can no longer accidentally adopt our tracked stream slot.- LAN VITA UDP rebind path: on Flex's
0x500000A9"Port/IP pair
already in use" error, AetherSDR rebinds to an OS-assigned ephemeral
port and retriesclient udpport. WAN/SmartLink already used
ephemeral ports and is correctly excluded from the rebind path. - Dead-orphan DAX RX detection:
client_handle=0 ip=0.0.0.0entries
are ignored as not-our-stream rather than treated as legacy-compat. - External DAX TX/RX visibility: separate seen-once log lines so the
radio's external streams are visible in diagnostic logs without
polluting our local state. - 24 + test assertions across
testStreamStatusOwnershipCompatibility,
testDaxTxStatusOwnership,testDaxTxPolicy, and
testUdpRegistrationPolicy. - Linux non-PipeWire builds correctly skip both
setDax(1)and DAX
stream creation, so digital TX falls back to the physical mic input
rather than going silent (#2273).
Sequenced radio disconnect teardown (#2247, jensenpat)
- Fixes Flex-6300 / 6400 unreachability after reconnect (#2113, #2218).
- Disconnect now sends
stream remove 0x<id>and waits up to 2 s for
the radio's response before closing TCP. Without this, fire-and-
forget teardown left stale Flex sessions that refused subsequent
connects until reboot. - Drops the rejected
client disconnect <self_handle>send — verified
against FlexLib's own self-disconnect path, which uses the0x04
"dying gasp" byte before TCP close. AetherSDR now does the same. - Defensive auto-reconnect arms on refused-connect paths that don't
emit adisconnectedsignal.
Worker object shutdown thread affinity (#2248, rfoust)
- Eliminates
QObject::killTimer: Timers cannot be stopped from another thread,QObject::moveToThread: Current thread is not the object thread, and~QObjectwarnings on app exit. - Worker-thread QObjects (audio engine, spot clients, ext controllers,
radio connection, panadapter stream) are now destroyed via
deleteLater()on their owning threads before each thread quits.
Panadapter zoom keeps spectrum visible (#2246, rfoust)
- New
reprojectSpectrum()mirrors the existing waterfall reprojection.
Zoom changes that overlap with the previous range now interpolate
existing FFT bins forward instead of clearing them, eliminating the
spectrum flash/blank during pinch / wheel / drag zoom.
TGXL detected via direct TCP only (#2250, chrisb1964)
- TUN applet now appears for TGXLs that don't report through the
amplifier API (Flex-8600 fw 4.2.18). Newm_directPresencefallback
inTunerModel;isPresent()returns true via either path. - Handles bare
amplifier <handle> removedform correctly via a new
ampRemovedReregex (matches FlexLib'ss.Contains("removed")
semantics for both bare-flag and kvs-form removals).
RX slice tab capacity initialization (#2243, chibondking)
- RX slice tab capacity is now seeded from the radio model on connect
rather than left at the default — fixes empty tabs when reconnecting
to a different radio model.
Radio model label refresh (#2244, jensenpat)
- Radio model label refreshes on
infocommand response, so
reconnecting to a different model immediately reflects the new
identity in the UI.
FreeDV Reporter OS string (#2269, tmiw)
- OS field lowercased to match the official FreeDV client convention.
Previously AetherSDR users showed as "other" in monthly platform
reports. Thanks Mooneer.
FreeDV Reporter checkbox visibility (#2268, NF0T)
- Three checkboxes in the FreeDV Reporter tab were rendering with no
visible borders against the dark background. Applied the standard
ConnectionPanelcheckbox style.
AppletPanel scrollbar (#2257, Chaosuk97)
- AppletPanel scrollbar widened from 6 px to 12 px with a dim handle
at rest that brightens to#4a6880on hover or drag. 500 ms
delay before dimming back so quick scroll gestures don't flicker.
Always-visible track to match the rest of the app.
Sweep 1 — 7 small UI / contrast fixes (#2270)
AetherSDR vX.Y.Zshown again in the in-app title bar — restored
after the frameless-window mode regression (#2027).- FlexControl knob press tokens are bare
S/C/L, not
X4S/X4C/X4L. Captured against LB2EG's hardware (#2263). - Å Morse sequence corrected to
01101(·−−·−) per ITU-R M.1677-1
(#2264, LB2EG). - Single-click-to-tune suppressed across the entire K / SFI / WNB /
RF Gain / WIDE indicator strip in the top-right of the spectrum,
not just the propagation text (#1564). - Slice record (⏺) and play (▶) indicator alpha values doubled for
legibility against the dark spectrum background (#1576). - Status indicators (TNF / CWX / DVK / FDX) unified to cyan
(#00b4d8) for active and dark grey (#404858) for off; TNF init
colour bug fixed (was semi-transparent white) (#1581). - Status bar time stack: removed grid-square label; date + UTC time
use the same 2-row layout as every other telemetry stack (#1583).
Sweep 2 — 5 small bug fixes (#2272)
- Double-clicking an off-screen slice indicator (
<A/A>) recenters
cleanly without retuning to a wrong frequency (#2237). Qt's
press → release → double-click → release sequence was leaving
m_spotClickConsumed=falseon the trailing release, so the
single-click-to-tune path fired against the new center. - HAVE_RADE without HAVE_WEBSOCKETS compile error (#2204) —
startFreeDvReporting/stopFreeDvReportingnow wrap their
bodies with#ifndef HAVE_WEBSOCKETSno-op fallback. - (See "Features" above for #1846 spot mode, #1993 ATU toggle, #2259
custom filter edges.)
v0.9.4 — AetherSweep, ShackSwitch, and multi-client startup hardening
[v0.9.4] — 2026-05-01
AetherSweep, ShackSwitch, and multi-client startup hardening
A heavy community-contribution release. Headline feature is AetherSweep
(jensenpat) — an in-panadapter SWR sweep analyzer that walks the current TX
band, plots SWR live on the spectrum surface, and handles TGXL bypass +
PGXL checks. ShackSwitch support (nigelfenton) lands as a
new Peripherals tab + dedicated applet, integrating any builder's open-source
Arduino antenna switch over the Antenna Genius protocol.
A trio of substantial reliability fixes from jensenpat addresses long-standing
multi-client startup issues — when SmartSDR or DAX is already connected,
AetherSDR's panadapter creation, slice ownership, and PC audio stream
acquisition now all hold up under the racey status interleavings the
ShackSwitch and DAX users were hitting. Plus a focused mix of platform
fixes: macOS DMG dark-theme on pop-out windows (Chaosuk97), Windows
without-Qt6::SerialPort build (NF0T), NRL gating correctness on 6000-series
radios.
Features
AetherSweep — in-panadapter SWR analyzer (#2202, #2220, #2230, jensenpat)
- New Start/Clear Sweep buttons under the ANT slice menu, plus a 1-10 W
sweep-power slider with cross-panel sync. Persisted as
SwrSweepPowerWattsand defaults to 1 W. - Walks the current TX band stepping a tune carrier in 20 kHz increments
with edge guards, sampling fresh SWR + forward-power meter data per step,
and overlays the curve on the panadapter directly under the slice flag. - Per-band-edge guard, max-260-points cap, and 60 m channelized-band
refusal. Refuses to start when split is active, when transmitting,
when the band is wider than the radio's max pan width, or when a PGXL
amplifier is in OPERATE mode (forces user to STANDBY first). ⚠️ Third-party amplifiers are not auto-detected. AetherSweep
only checks for Power Genius XL status — it has no way
to talk to ACOM, SPE, Elecraft KPA, OM Power, or other linear amps.
If you have a non-PGXL amplifier, manually place it in BYPASS or
STANDBY before starting an SWR sweep. The sweep will run a tune
carrier through whatever path the radio sees, and a non-bypassed
external linear will amplify that carrier into your antenna.- Full TGXL handling: snapshots OPERATE/BYPASS state, places TGXL into
BYPASS to read raw antenna SWR, restores original state on completion
or abort. Reads radio-side SWR while TGXL is bypassed (TGXL stops
emitting RL meter packets in bypass — #2229). - 5-phase state machine with explicit timeouts: WaitingForTgxlBypass,
TgxlBypassSettle, Sweeping, StoppingTune, RestoringTgxl. - Esc to abort. Inputs locked during sweep so the user can't accidentally
retune mid-pass. Disconnect mid-sweep cleanly stops the carrier and
releases TGXL. - Optimistic
setTunePower()update so the sweep's chosen power lands
immediately rather than racing the radio's status echo. - Sweep result label shows source —
RADIOfor direct measurement,
TGXL BYPASSfor tuner-bypassed measurement.
ShackSwitch antenna switch integration (#2214, #2227, nigelfenton)
- New Peripherals tab in Radio Setup with auto-discovery and manual-IP
connect for ShackSwitch devices via the Antenna Genius (AG) UDP/TCP
protocol on port 9007. - New ShackSwitchApplet — compact panel with up to 8 labelled
antenna-port buttons, click-to-switch, active-port highlight. SO2R
dual-radio mode shows Input A / Input B side-by-side with conflict
detection. Single-radio mode (4-port R4 hardware) hides Input B
automatically. - Dummy-load / deselect (clicking the active port deselects it) and
per-port labels driven by the device's own configuration. - Integration is invisible to users without ShackSwitch hardware —
detected by thename="ShackSwitch"field in the AG broadcast beacon. - Web UI launcher button opens the device's local web interface.
- Reference hardware: ShackSwitch v2.0 (Arduino Uno Q, SO2R, 8-port) and
ShackSwitch R4 (Arduino Uno R4 WiFi, single-radio, 4-port).
Bug fixes
Multi-client startup panadapter creation (#2222, jensenpat)
- AetherSDR's startup pan creation now holds up cleanly when SmartSDR,
DAX, or another GUI/audio client is already connected. The radio
replays status for all current pans/slices/streams on connect; without
this fix, AetherSDR could correctly reject other clients' objects but
then fail to instantiate its ownPanadapterModelif ownership status
arrived out of order. - Adds
display panafall create x=100 y=100(FlexLib v4.2.18 syntax)
with capability-based fallback to legacypanadapter create. - New
ensureOwnedPanadapter()factory; deferred-status replay queue
fordisplay panframes that arrive withoutclient_handle; waterfall
ordering recovery viapanadapter=...parent ID lookup. - Routes failure cases through the existing
panadapterLimitReached/
sliceCreateFailedstatus-bar signals so radio resource exhaustion
is visible instead of looking like a startup hang.
PC audio remote stream ownership (#2226, jensenpat)
- Fixes PC Audio failure when SmartSDR / DAX is already running — the
sharedremote_audio_rxpipe was being silently removed because
AetherSDR was parsing thestream createresponse body as decimal
instead of hex."4000009"(no0xprefix) was becoming
0x003D0F09instead of0x04000009, so the create-response stream
didn't match the status-reported stream and AetherSDR removed the
real one. - Extracts a new
RadioStatusOwnershiphelper (header-only, fully
unit-tested) that handles ownership decisions for both panadapters
and remote audio streams — defer when noclient_handle, claim
when ours, ignore when another client's. - Stream-acquisition state machine now tracks create-pending,
remove-requested, and adopted-from-status separately so toggling
PC Audio doesn't race the create response. - Adds
radio_status_ownership_testwith 25 assertions covering all
the ownership decisions and the headline parse bug. - Fixes #2037, #1418, #1473.
TGXL meter goes silent during bypass (#2229, #2230)
- AetherSweep aborted with "no fresh TGXL SWR meter data" whenever the
TGXL was in OPERATE before sweep start. The TGXL stops emittingRL
(return-loss) meter packets while in BYPASS — bypass relays are
passive wire-through, no measurement engine. - Switches the meter source to RADIO while the TGXL is bypassed; the
radio's own SWR coupler measures the same physical signal through
the bypassed relays. UI label still showsTGXL BYPASSsince
that's what describes the configuration.
Pop-out applet panel white background on macOS (#2190, Chaosuk97)
- macOS DMG builds rendered floating windows with a white background
even though the dark-theme stylesheet was applied — the CI-built
libqcocoa.dylibenforcedQt::WA_StyledBackgroundmore strictly
than Homebrew's Qt 6.11.0 build. - Adds
setAttribute(Qt::WA_StyledBackground, true)on all three
floating-window classes (FloatingContainerWindow,PanFloatingWindow,
MainWindow::floatAppletPanel). Cross-platform safe: the attribute
is harmless on Linux/Windows where it was a no-op, and now the
pop-out applet panel correctly themed on every platform (was
defaulting to system theme on Linux/Windows too — latent bug fixed
as a side effect).
Windows build without Qt6::SerialPort (#2195, NF0T)
- Compile error introduced by #2147: the
<QElapsedTimer>include was
guarded by#ifdef HAVE_SERIALPORTwhilem_debounceTimeris declared
under#if defined(HAVE_SERIALPORT) || defined(Q_OS_WIN). Mismatch
broke the Windows-without-SerialPort build configuration. Widens the
include guard to match.
NRL DSP filter visible on 6000-series radios (#2219)
- Fixes regression from #2184 where NRL was incorrectly grouped with
the 8000-series-only firmware DSP filters (NRS, RNN, NRF). NRL is
available on 6000-series too — only NRS/RNN/NRF require BigBend /
DragonFire hardware. Fixes #2198.
Acknowledgements
Massive contributor batch this cycle:
- jensenpat — AetherSweep (3 PRs: feature + power-control polish + TGXL
meter fix), multi-client panadapter startup, PC audio ownership. This
release wouldn't be the leap it is without his work. - nigelfenton — ShackSwitch integration (3 PRs across the cycle:
protocol fix, full integration, callsign-detection cleanup). - NF0T — Windows build fix.
- Chaosuk97 — macOS DMG dark-theme fix.
v0.9.3 — External APD, FreeDV Reporter, Slice Colors, and v4.2 firmware updater
[v0.9.3] — 2026-04-30
External APD, FreeDV Reporter, Slice Colors, and v4.2 firmware updater
A broad release. Headline feature is External APD support for
SmartSDR firmware 4.2.18, which lets the radio sample its outgoing
RF from a coupled feedback path on one of the RX/XVTR inputs so the
predistortion engine trains against the actual transmitted signal —
required when a FLEX-8x00 drives an external linear amplifier.
FreeDV Reporter station reporting (NF0T) lands the long-requested
RADE-integrated reporting flow. chibondking ships customizable
slice colors plus two serial / pop-out fixes. AetherSDR's firmware
updater is rewritten to extract .ssdr files natively from
FlexRadio's v4.2+ MSI installer (no bundled tools needed), and the
NAVTEX data-layer plumbing lands ahead of the upcoming applet.
A long tail of UI polish and Windows-platform fixes also lands —
TX→RX waterfall continuity, scroll-wheel debounce, 48 kHz audio
sink on Windows, applet pop-out persistence, and floating-window
dark theming.
Features
External APD (#2187)
- New "APD" tab in Radio Setup with per-TX-antenna external sampler
selection (ANT1 / ANT2 / XVTA / XVTB → INTERNAL / RX_A / RX_B /
XVTA / XVTB). Tab is hidden unless the radio reportsapd configurable=1, so it stays invisible on 6000-series radios and
pre-4.2.18 firmware. - Equalizer Reset button issues
apd resetto clear all per-antenna
training data. - Protocol additions:
apd samplersub-object parsing (with
fallback-to-INTERNAL for invalidselected_sampler), bare
equalizer_resetflag handling, and the matchingsetApdSamplerPort
command path onTransmitModel. - Cross-checked against
Flex.Smoothlake.FlexLibv4.2.18 source.
FreeDV Reporter station reporting with RADE integration (#2173, NF0T)
- Adds FreeDV Reporter (https://qso.freedv.org) station-reporting
support driven by the RADE modem's sync / SNR / freq-offset events. - New connection toggle in the DX Cluster dialog plus per-slice
reporting toggle that mirrors RADE engine state to the Reporter
WebSocket session. - Builds opportunistically — without
Qt6::WebSocketsorlibrade,
the toggles silently become no-ops.
Customizable slice colors (#2155, chibondking)
- Per-slice color selection through a new
SliceColorManager
singleton. Color assignments persist across sessions and are
visible in VFO widgets, panadapter overlays, and meter strips.
Native MSI firmware installer support (#2169)
- The firmware updater can now extract
.ssdrfiles directly from
FlexRadio's v4.2.18+ MSI installer (which switched from PE/COFF
self-extracting.exeto WiX 6 MSI). Vendoredlibmspack
(LGPL-2.1) handles LZX-compressed CABs; a small OLE Compound File
reader pulls the embedded CAB from the MSI envelope. - "Browse .ssdr" → "Select Installer..." now accepts
.msi,.exe,
and.ssdr. No external tools required (no7z, no MSI runtime). - Format auto-detection on the first 8 bytes (OLE magic vs. PE/COFF MZ).
NAVTEX data-layer plumbing (#2186)
- New
NavtexModelcovers the SmartSDRnavtexwaveform protocol —
per-message Pending → Queued → Sent / Error state, status parsing
fornavtexandnavtex sent, and anavtex sendcommand path
with proper quote/backslash escaping formsg_text. - Foundation for the upcoming NAVTEX applet UI; data layer ships
first so other clients (TCI, scripting) can already publish NAVTEX
traffic. - 21-assertion unit test covers escaping, idempotency, error paths.
CWX active tracking (#2181)
RadioModeltracks CWX send state so the audio gate stays open
during long character sends.
Floating-window dark theme (#2096, AetherClaude)
- Pop-out panadapter and floating-container windows now inherit the
full dark theme via a newTheme.hshared stylesheet, eliminating
flash-of-light-theme on window construction.
Center active VFO when zooming in from keyboard (#2183, jensenpat)
- The keyboard zoom-in shortcut now centers the active VFO in the
viewport (matches the mouse-wheel zoom behavior).
Bug fixes
TX→RX waterfall continuity (#2171, #2182)
- Force FFT-fallback rendering on TX→RX transition to prevent a
visible gap in the waterfall (#2171). - Blank waterfall rows for 400 ms after TX ends so any residual
hardware tail doesn't paint over the noise floor (#2182).
Hide 8000-series DSP filters on 6000-series radios (#2184)
NRL,NRS,RNN,NRFfilters are FLEX-8x00-only. 6000-series
radios no longer show greyed-out controls for filters they can't
use.
Stream-status helper consolidation (#2145)
- Deduplicates the per-stream-type status helpers. Annotates
m_daxTxClientHandleand documents the TCI receiver-index policy
for future contributors.
TCP close handshake before socket destroy (#2113)
- Wait for
disconnected()before tearing down the QTcpSocket on
user-initiated disconnect. Fixes occasional "connection reset"
reports on the radio side.
rigctld short-form split direction (#2111)
S(set split) ands(get split) were transposed in the rigctld
mapping table, so external loggers couldn't read or set split.
CW sidetone on Windows (#2105)
startSidetoneStream()was never called on Windows because the
audio backend init order put it after the first key event. CW
sidetone now starts immediately on connect.
48 kHz RX audio sink on Windows (#2123)
- Prefer 48 kHz over 24 kHz on Windows where WASAPI's resampler
introduces audible high-frequency artifacts at 24 kHz.
Scrollbar styling on applet panels (#2088)
- The applet-panel scroll area was inheriting Qt's default thin grey
scrollbar; restyles to the dark-theme bar.
USB mic level gauge on connect (#2086)
- The USB mic-level gauge wasn't drawn when connecting with
mic source = PC; gauge now appears immediately.
VOX phoneStateChanged on keyboard shortcut (#2084)
- VOX setters didn't emit
phoneStateChanged(), so the UI didn't
refresh when VOX was toggled via keyboard shortcut.
Scroll-wheel debounce (#2151)
- Debounces high-frequency
pixelDeltascroll events from
precision-scroll mice inVfoWidgetandSpectrumWidget.
Marker-width settings cleanup (#2156, mvanhorn)
Slice<N>_MarkerThinsetting key is now removed after migration
toSlice<N>_MarkerWidthinstead of being left in place.
Serial PTT — Win32 WaitCommEvent path (#2147, chibondking)
- FTDI VCP drivers only refresh DSR/CTS in the completion of a
WaitCommEventcall; AetherSDR's polling-based detection missed
edges on Windows. Adds a Win32 native event-wait path on top of
Qt's serial port for DSR detection.
Applet pop-out persistence race (#2154, chibondking)
- Floating applet windows were sometimes restored at the wrong
position because the geometry-save signal raced the close event.
Systemic fix wires save before destruction.
Windows build guard for FreeDV Reporter (#2186)
- The FreeDV Reporter
freedvReportingToggledconnect-lambda used
RADE-guarded symbols inside an#ifdef HAVE_WEBSOCKETSblock.
Windows has WebSockets but no RADE → undefined-identifier compile
errors. Added the missingHAVE_RADEinner guard.
Docs
- Data-modes help refresh (#1939) — replaces lingering "DIGI
applet" references with the current independent-tile UI vocabulary. - DIGI applet scrub (#2179) — sweeps the remaining stale
references in tooltips and dialog text.
Acknowledgements
Thanks to NF0T for the FreeDV Reporter station-reporting
integration; chibondking for slice colors, the serial PTT
WaitCommEvent fix, and the applet pop-out race; jensenpat for
the keyboard zoom-center shortcut; mvanhorn for the marker-key
cleanup; and AetherClaude for the bulk of the long-tail bug
fixes.
v0.9.2.1 — TGXL TUNE for firmware 4.2 hotfix
[v0.9.2.1] — 2026-04-29
TGXL direct autotune for firmware 4.2 compatibility
Hotfix release. After upgrading to SmartSDR firmware 4.2, multiple
users on both AetherSDR and SmartSDR report the TUNE button no longer
works on the 4O3A Tuner Genius XL. The radio's tgxl autotune command
path was reworked in 4.2 firmware and broke for many configurations.
This release routes the TUNE button through the TGXL's native
port-9010 channel when a direct TGXL connection is configured,
bypassing the affected firmware path. Users without a direct TGXL
connection are unaffected by this change and should still configure
direct connection in Radio Setup → Tuner to recover TUNE.
Bug Fixes
TGXL TUNE works again on firmware 4.2 (#2163)
- When a direct TGXL connection (port 9010) is configured, the TUNE
button now sends the nativeautotunecommand directly to the TGXL
instead of routingtgxl autotune handle=<H>through the radio's
command channel. Falls back to the radio path when no direct
connection is available. - The TGXL drives radio PTT via its hardware interlock cable when it
receives the nativeautotunecommand, so client-side keying is
not required. Existing tuning state and SWR readout in the Tuner
applet are unchanged.
Log redaction no longer mangles 4-component version strings
- The PII redactor's IPv4 regex matched anything that looked like
four dot-separated 1-3-digit numbers, so the application's own
version string0.9.2.1was being redacted to*.*.*. 1in
logs and support bundles. Negative lookbehind/lookahead now skip
quoted ("0.9.2.1") and v-prefixed (v0.9.2.1) version forms
while still redacting bare IPs in log output.
v0.9.2 — WAVE Phase 2, v4.2.18 firmware support, and community polish
[v0.9.2] — 2026-04-28
WAVE Phase 2, v4.2.18 firmware support, and community polish
A focused community-driven release. Major work from jensenpat lands
the WAVE Phase 2 applet (four visualizations, settings drawer), proper
TCXO frequency-offset calibration, and the DAX/TCI multi-stream routing
needed for FlexRadio firmware 4.2.18. chibondking and NF0T
ship a serial PTT fix and an r8b heap-corruption crash fix. The VFO
DSP panel gets a UX pass (collapsed Marker + Filter Edge buttons), and
the v4.2.18 discovery beacon is parsed.
Features
WAVE Phase 2 — applet visualization controls (#2124, jensenpat)
- Double-click the WAVE waveform to open a settings drawer (replaces
the prior clear-on-double-click behavior). Drawer is open by default
on first launch for discoverability. - New compact
Viewdropdown with four visualizations:
Scope (the original waveform trace), Envelope (filled RMS with peak
outline), History (side-scrolling activity bars), Bands (vertical
EQ-style frequency bands). - Persisted Zoom and FPS controls in the drawer. Zoom is amplitude
gain across all views, not a time-window preference. - Drawer participates in the applet size hint when expanded so the
applet stays compact when collapsed.
DAX-aware TCI multi-stream routing (#2140, jensenpat)
- Adapts the DAX/TCI audio path for FlexRadio firmware 4.2.18's
explicit stream-ownership reporting. Filters DAX RX/IQ stream
status byclient_handleso we don't accidentally register another
client's stream, while accepting ownership-less status from older
firmware. - Advertises TCI receivers as contiguous owned-slice indexes
(0..N-1) rather than raw Flex slice IDs. Fixes WSJT-X TCI1/TCI2
multi-slice operation when this client owns slice 1 but another
client owns slice 0. - Honors per-client
audio_start:<receiver>so multi-slice WSJT-X
receives only the intended audio. - Stream-removal handling: unregisters DAX/IQ streams and clears TCI
DAX placeholders onremovedstatus. - Adds focused diagnostics for first DAX VITA packets, TCI receiver
maps, DAX RX delivery, and DAX TX route selection — gated behind
lcDax/lcCatcategories so default users see no extra noise.
TCXO frequency-offset calibration (#2119, jensenpat)
- Replaces the unsupported
radio calibratecommand (firmware
v4.1.5 returns0x50000016"unknown command") with the documented
SmartSDR / FlexLib sequence:radio set cal_freq=,radio set freq_error_ppb=0,radio pll_start. - Watches
pll_donestatus for completion with run-specific guards
against stale events from a prior calibration firing during a new
run. - Cross-checked against FlexLib v2.10.1 source. Fixes #1237, #2095.
VFO marker controls — tri-state Marker, single Filter Edge (#2141)
- The four-button row (Thin / Thick / Edges / Hide) collapses to two:
- Marker cycles
Off → 1 px → 3 pxon click. Off skips both
the center line and the top triangle, leaving only the passband
bracket. - Filter Edge is a checkable on/off — checked = edges shown.
- Marker cycles
- New
Slice<N>_MarkerWidthint settings key with one-shot migration
from the oldSlice<N>_MarkerThinbool (True→ 1,False→ 3).
v4.2.18 discovery beacon parsing (#2138, AetherClaude)
- Parses two new fields from the FlexLib v4.2.18 discovery packet:
is_system_model(flags bench / system-build radios) and
turf_region(turf region indicator distinct from the existing
regionfield).turf_regionis shown in the connect dialog
detail line.
Other
- ATU start added to the keyboard shortcut actions list.
- Clear action added to the CW decode window context menu (#2116).
Bug fixes
Resampler heap corruption (#2114, NF0T)
r8b::CDSPResampler24::process()doesn't bounds-check itsl
parameter; passing a block larger than the constructor's
aMaxInLensilently overflows internal filter buffers. Bug
manifested as a crash when the Qt event loop stalled long enough
forQAudioSource::readAll()to return more than 4096 frames in
one call (typical during DX-cluster spot bursts or heavy UI
redraws). Fix: chunk-and-recurse on eachprocess*()path so each
call stays within the configured limit.
Serial PTT input non-functional (#2125, chibondking)
- Three layered bugs blocked the serial-PTT path:
- No way to open the port without restarting AetherSDR — the
Serial tab had no Open / Close buttons and the auto-open label
implied a restart was required. loadSettings()defaulted CTS / DSR polarity toActiveLow
while the dialog combo defaulted to "Active High". Users who
left polarity at the displayed default got silently inverted
logic — footswitch press read as inactive, PTT never fired.updatePolling()was missed when the port was opened, so the
polling loop didn't pick up the configured input function.
- No way to open the port without restarting AetherSDR — the
- Adds Open / Close buttons to the Serial tab, fixes the polarity
default mismatch, and tracks the explicit Open state separately
from auto-open via a newSerialPortOpensetting.
Slice audio loss after band changes (#2128, jensenpat)
- After
display pan set band=, the radio sometimes stops mixing
slice audio without echoingaudio_mute=1, leaving the model in a
"unmuted but silent" state. Fix: 300 ms after the band-change
command, reassertslice set <N> audio_mute=0for any unmuted
slice on that pan.
CWX Live toggle and Send action (#2122, jensenpat)
Livebutton is now a true toggle so operators can turn live
keying back off (was force-on only).Sendbutton now submits the current input when Live is off
(matching Enter-key behavior).- Setup exits Live cleanly without duplicate-sending text already
keyed character-by-character. - Adds
cwx_panel_testcovering Live toggling, Send-click
submission, Enter submission, and Live exit safety.
Connect-radio dialog grouping polish (#2121, jensenpat)
- Scopes the
QFramecallout stylesheet so child labels and
checkboxes don't inherit the panel border / background, fixing
nested-bordered-box rendering on macOS, Windows, and Linux. - Tightens the SmartLink Remote radio list height and moves the
Connect Remote button outside the Remote group.
Internal
bool m_markerThin→int m_markerWidth(0 / 1 / 3) propagated
throughVfoWidget,SpectrumWidget::SliceOverlay, and the
markerStyleChangedsignal. Render path skips both the center
line and the top triangle whenmarkerWidth == 0.
Acknowledgements
Thanks to jensenpat for the WAVE Phase 2, DAX/TCI, calibration,
band-change, CWX, and dialog polish work; chibondking for the
serial PTT triple-fix; and NF0T for catching the r8b heap-
corruption bug.
v0.9.1 — Local iambic CW keyer + CW transmit reliability
[v0.9.1] — 2026-04-27
Local iambic CW keyer, unified sidetone controls, and CW transmit reliability
A focused follow-up to v0.9.0. The headline feature is a software
iambic keyer that turns any MIDI / serial paddle into a sub-5 ms
sidetone source via the new PortAudio backend (issue #2079) — the
radio still produces the on-air signal, but the local sidetone
gate fires the moment the paddle moves instead of waiting for the
radio's keyed-back signal. Three latent bugs in the netcw protocol
path that prevented CW from ever transmitting on FLEX-8600 v4.1.5
firmware are also fixed. Plus the CW panel collapses three sidetone
widget groups into one set of controls and finally wires up the L/R
pan slider that's been a placeholder since the panel was first added.
Features
Local iambic keyer for sub-5 ms paddle sidetone (#2079)
- Software iambic state machine that runs alongside the radio's RF
iambic engine. Both engines see the same paddle inputs at the same
WPM and produce identical Morse timing — but the local keyer drives
the sidetone gate directly, avoiding the 50–200 ms round-trip
through the radio's keyed-back signal. - Modes A and B implemented (Ultimatic / Bug / Straight follow in a
later phase). Hooked into both MIDI Gate params (cw.dit,
cw.dah) and serial paddle paths (DTR/CTS via SerialPortController). - Driven by the existing radio Iambic toggle — no new UI clutter.
The keyer mirrors the radio's iambic state, mode, and WPM via the
TransmitModelphoneStateChangedsignal. - Dedicated worker thread with
std::chrono::steady_clocktiming
and a lock-free atomic key gate on the audio side. 9 unit tests
covering single dit/dah timing, squeeze alternation, inter-element
gap, mode A release behaviour, WPM scaling, paddle swap, idempotent
start, and idle behaviour.
Sidetone controls unified (#2079)
- The CW panel previously had three separate sidetone widget groups:
the radio's "Sidetone" toggle/volume, a "Local STn" toggle/volume
for the local PortAudio sidetone, and a "Follow" pitch row with a
manual override slider. All three are now collapsed into the
single existing Sidetone button, which drives both engines in
lockstep. The volume slider drivesmon_gain_cwon the radio and
the local sidetone identically. Pitch always follows the radio's
cw_pitch.
CW pan slider wired up (#2079)
- The L/R pan slider in the CW panel was a dead UI element with a
TODO comment. Now drives both the radio'smon_pan_cw(radio-side
sidetone pan within the RX audio stream) and the local sidetone
with constant-power pan law (cosine/sine for equal-loudness sweep,
no center dip). Double-click on the slider recenters to 50.
Slice capacity notification (#48)
- Status-bar warning when adding another panadapter would exceed the
radio's slice limit. Three guard points cover the pre-flight check
in the layout dialog, the runtime check inapplyPanLayout, and
the async fallback when the radio rejects apanafall create.
Includes the radio model name and slice count in the message.
PortAudio sidetone via JACK on Linux (#2075 follow-up)
- The PortAudio sidetone backend (introduced in v0.9.0) now prefers
the JACK host API on Linux when available, withpaFramesPerBuffer = 128+suggestedLatency = 0for sub-5 ms quantum. PipeWire's
ALSA shim silently breaks callback-mode streams on some setups
(Pa_StartStreamreturns success but the audio thread never
schedules the callback); pipewire-jack delivers reliable callbacks
at the device's native sample rate. 48 kHz is now the universal
preference, withPa_IsFormatSupportedguarding fallback.
Bug fixes
CW transmit: invalid paddle command form
RadioModel::sendCwPaddlewas emittingcw key 1 0(a 2-arg paddle
form) which the radio's protocol does not accept — FlexLib only
ever sends single-statecw key 1orcw key 0and expects the
client to do iambic timing locally. The 2-arg form was silently
dropped, so paddle keying produced no RF. Now collapses to a
straight-key form when the local iambic keyer isn't running, or
routes through the newsendCwKeyEdge+sendCwPttprimitives
when it is.
CW transmit: lowercase hex in netcw payload
- FlexLib formats
time=0x...andclient_handle=0x...with C#
ToString("X")(uppercase), and the radio's status messages do
too (e.g.S23A59BDF|...). Firmware v4.1.5's netcw parser is
case-sensitive on these fields and silently dropped lowercase
packets. Now formatted explicitly uppercase.
CW transmit: dead TCP fallback
- The post-UDP TCP fallback was sending the netcw-decorated form
(cw key 1 time=0x... index=... client_handle=0x...) which the
radio rejects with0x50001000("command syntax error") on TCP.
Removed when the netcw stream is up; the no-netcw fallback (for
firmware that doesn't support netcw stream creation) is preserved
separately.
Optimistic updates for CW model setters
setCwIambic,setCwIambicMode,setCwSpeed, andsetCwPitch
onTransmitModelpreviously sent the command to the radio
without updating local state, on the assumption that the radio
would echo the new value back. Firmware v1.4.0.0 doesn't reliably
echo iambic flags, WPM, or pitch in transmit status messages, so
any code reading these properties after a UI toggle saw stale
values until the next periodic transmit status arrived (or never).
Now follow the same optimistic-update pattern assetCwBreakIn.
Block on graceful disconnect (#1996, openstreem)
RadioModel::disconnectFromRadionow usesQt::BlockingQueued Connectionfor both thegracefulDisconnectlambda and the
fallbackdisconnectFromRadiocall, matching the destructor's
pattern. Without this, the queued work could be cancelled before
it ran during app teardown — leaving the radio with a stale
session that required a power cycle.
Memory recall: restore repeater offset and tone (#1871, #1965, jensenpat)
- Recalled FM repeater memories properly restore
repeater_offset_dir,
fm_repeater_offset_freq, derivedtx_offset_freq, and CTCSS tone
state. Previously, switching from a repeater memory to simplex
could leave stale TX offset state on the slice.
m_activeTxSlice initializer mismatch (#2076)
- Default-init was
0butclear()sets to-1. Now both default
to-1so a freshMeterModeland a cleared one have identical
activeTxSlice()state.
Per-slice compression meter resolution (#2073)
- TX-chain
COMPPEAK,AFTEREQ, andSC_MICmeters are now
resolved per active slice instead of last-match-wins, fixing
Multi-Flex setups where the wrong slice's compression value was
surfaced in the UI.
ContainerWidget restoreState displaces children
restoreStatere-inserted children at indices 0..N–1 even when
they were already in the layout, displacing non-saved children
like the CHAIN widget.insertChildWidgetis now a no-op when
the child is already present.
Build and CI
M_PI_2replaced with a localkPiOver2constexpr in
CwSidetoneGenerator.cppso the Windows MSVC build doesn't
fail (<cmath>doesn't defineM_PI_2without_USE_MATH_DEFINES).
Acknowledgements
Thanks to the operators who tested CW workflows on real hardware
and surfaced the netcw protocol issues that had been silent since
the netcw backend landed, and to jensenpat and openstreem
for the community fixes bundled in.
v0.9.0
Aetherial Audio RX, frameless UI, and local CW sidetone
A milestone release. The Aetherial Audio suite — Parametric EQ, AGC-T
gate, AGC-C compressor, Tube saturator, and PUDU exciter — now runs
on both RX and TX with fully independent settings, drag-to-reorder
on each path, and matching frameless editor windows. The whole UI
goes frameless: the main window plus every applet pop-out and editor
gets a Discord-style minimise / maximise / close trio with title-bar
drag. CW operators get a dedicated low-latency local sidetone sink
that survives CWX too. Plus community fixes for the FLEX-6000
compression meter, RTTY mark-default reset, RADE decoded audio, and
the usual stack of UX polish.
New to the Aetherial Audio chain? See the Aetherial Audio wiki page for the full guide, including the new RX-chain section.
Features
Aetherial Audio RX chain (#1998)
- Reuses the existing TX DSP modules (Parametric EQ, gate/expander,
compressor, tube saturator, PUDU exciter) on the RX path with
fully independent state from TX. Each stage is a tile in the
CHAIN widget on the RX side: single-click toggles bypass,
double-click opens the floating editor. RX chain order is
drag-to-reorder. RADIO and SPEAK status tiles bookend the chain;
the DSP tile shows whichever client-side noise reducer is active
(NR2 / NR4 / BNR). - All applet titles rebrand to Aetherial: Parametric EQ,
AGC-T (gate), AGC-C (compressor), Tube, PUDU.
Frameless main window with custom title bar (#1926)
- Main window now uses
Qt::FramelessWindowHintwith a custom
20 px title bar carrying drag-to-move viastartSystemMove,
double-click maximise, and a Discord-style minimise / maximise /
close trio. Resize via standard window-edge grip.
Frameless pop-out windows for applets and panadapters (#1922)
- Every floating applet and panadapter pop-out gains the same
frameless title bar with the trio. Single-click trio actions,
drag the bar to move, double-click to maximise.
Frameless editor windows for the entire Aetherial chain (#1998)
- Aetherial Parametric EQ, AGC-T, AGC-C, Tube, PUDU, and Reverb
editors all use the same shared title-bar widget with drag and
trio. Title text reads "Aetherial <Stage> — <Side>" so the
TX vs RX instance is identifiable at a glance.
Polish frameless title bar (#1931)
- Title-bar trio refined to a Discord-style sequence; dropped the
lightbulb icon in favour of a minimal arrow accent so the trio
sits flush against the right edge.
Local CW sidetone with low-latency sink + CWX support (#1969)
- Dedicated low-latency local sidetone path that bypasses the
protocol-level monitor for keying feedback that doesn't fight
network jitter. Works for paddle, straight key, and CWX
generated transmissions. Pitch and gain follow the existing
pitch/mon_gain_cwcontrols.
Two-Tone Tune shortcut (#1995, jensenpat)
- New unassigned
Two-Tone Tunekeyboard action under the TX
shortcut category. Sendstransmit set tune_mode=two_tone
before starting Tune, toggles off on a second press, and
restorestune_mode=single_toneon stop so the regular TUNE
press isn't surprised by sticky two-tone state.
XVTR diagnostic logging (#1964)
- New
xvtrlogging category captures status messages, RF↔IF
frequency translation, and pan-bandwidth conversions so XVTR
setup issues can be diagnosed from log bundles instead of pcap.
XVTR policy regression tests (#1960)
- Test harness covers transverter active/inactive state, RF↔IF
conversion, and the family of pan-recenter cases that used to
drop the waterfall when crossing IF/RF boundaries.
Bug fixes
Flex compression meter derivation across radio families (#1992, jensenpat)
- FLEX-8000 series exposes
TX/AFTEREQat 20 fps as the post-EQ
reference for compression display; FLEX-6000 captures don't
exposeAFTEREQ, so the best matching reference isTX/SC_MIC
at 10 fps withTX/COMPPEAKstill at 20 fps. Mixed cadence is
guarded by a freshness check so a freshCOMPPEAKisn't compared
against a staleSC_MICsample. WhenAFTEREQis present it
takes precedence overSC_MIC.
Preserve rtty_mark_default when radio resets mark on band change (#1968, chibondking)
- The radio resets
slice rtty_markto 2125 in the status broadcast
that follows a band change, regardless of the configured
rtty_mark_default.SliceModel::applyStatus()was accepting
the 2125 value blindly, overwriting the user's configured mark.
Now tracksm_rttyMarkDefault(seeded fromRadioModel) and a
user-override flag so the correct default is pushed back when
the radio resets, without fighting an intentional 2125 selection.
Fix choppy/harsh RADE decoded audio: dedicated buffer + sample-wise mix (#1953, NF0T)
- RADE decoded audio was sharing the SSB output buffer, producing
glitches and harsh artefacts on decoded voice. Split into a
dedicated buffer with per-sample mixing into the speaker output
so RADE and SSB don't fight for the same write window.
Fix CoreMIDI init crash during MIDI auto-connect (#1949, jensenpat)
- macOS CoreMIDI initialisation could crash on launch when
auto-connect ran before the MIDI client was fully constructed.
Init order tightened so the client is ready before any
auto-connect attempt.
Restore shortcuts after slider nudges (#1952, jensenpat)
- Shortcut bindings could go stale after the user nudged a slider
with the keyboard, because focus stayed on the slider and ate
subsequent shortcut keystrokes. Focus now returns to the main
window after a slider nudge.
Preserve MIDI bindings when saving device settings (#1951, jensenpat)
- Saving MIDI device settings was overwriting bindings with an
empty map because the save path read from the wrong source.
Now the existing binding map is merged into the saved settings.
Cancel frequency entry with Escape (#1954, jensenpat)
- The numeric frequency-entry buffer didn't honour Escape, so a
user who started typing a frequency had to clear the buffer
manually. Escape now cancels the entry cleanly.
Gate XVTR waterfall tile shifts (#1925, jensenpat)
- Waterfall tile-shift logic continued to fire on XVTR pans even
when the IF↔RF frequency relationship made the shift meaningless,
producing visual tearing. Shifts are now gated on transverter
state.
Close all floating windows when main window closes (#1920, chibondking)
- Floating applet pop-outs and panadapter pop-outs were left as
orphaned top-level windows when the main window was closed,
forcing the user to close each one individually. All
floating children now close together with the main window.
Build and CI
cmake: declare Qt 6.2 minimum (#1962)
startSystemMoveis gated behind Qt 6.2; the build now declares
the minimum explicitly so older systems get a clean configure
error rather than a confusing link failure. Matches Ubuntu
22.04 LTS shipped Qt.
ci(windows): switch to Ninja so sccache actually wraps cl.exe (#1963)
- The Windows CI job was using MSBuild, which sccache can't wrap
for compilation caching. Moved to Ninja so the sccache layer
established in #1913 actually has effect, cutting Windows build
times further.
ci: speed up Windows build with sccache + Qt/FFTW caching (#1913)
- Adds sccache for compiler caching plus GitHub Actions cache
layers for Qt and FFTW dependencies. First-run pulls populate
the cache; subsequent CI runs reuse compiled objects and prebuilt
third-party artefacts.
Add /bigobj to MSVC compile options (#1910 → #1911)
- The compilation unit hit the C1128 section count limit on
MSVC./bigobjraises the limit so the unit compiles cleanly
without splitting the file.
Dependencies
- Bump
mozilla-actions/sccache-actionfrom 0.0.6 to 0.0.10 (#1941) - Bump
actions/cachefrom 4 to 5 (#1942)
Acknowledgements
Massive cycle. jensenpat delivered eight PRs this release —
including the FLEX-6000 compression meter derivation, two-tone
Tune shortcut, XVTR diagnostic logging and regression tests, and
four crash/UX fixes (CoreMIDI init, MIDI binding persistence,
slider-nudge focus, frequency-entry Escape). chibondking
shipped two PRs (RTTY mark-default preservation and the
floating-window close cascade). Thanks also to NF0T for the
RADE decoded-audio dedicated-buffer fix.
73, Jeremy KK7GWY
Downloads
| Platform | Asset |
|---|---|
| Linux x86_64 | AetherSDR-v0.9.0-x86_64.AppImage |
| Linux aarch64 (Raspberry Pi 5) | AetherSDR-v0.9.0-aarch64.AppImage |
| macOS (Apple Silicon) | AetherSDR-v0.9.0-macOS-apple-silicon.dmg |
| macOS (Intel) | AetherSDR-v0.9.0-macOS-intel.dmg |
| Windows x64 (installer) | AetherSDR-v0.9.0-Windows-x64-setup.exe |
| Windows x64 (portable) | AetherSDR-v0.9.0-Windows-x64-portable.zip |
| Stream Deck plugin | com.aethersdr.radio.streamDeckPlugin |
| StreamController plugin | streamcontroller-aethersdr.zip |
The git tag v0.9.0 is GPG-signed (key 01FBE34B4639247E); macOS bundles are Apple-signed and notarised.