AetherSDR v0.9.8 — Aetherial Audio Channel Strip RX + community-driven reliability sweep
Aetherial Audio Channel Strip RX + community-driven reliability sweep
A focused 24-hour sweep around the Aetherial Audio Channel Strip's RX
side and a long list of community-contributed fixes. The RX panel
gains DESS, full final-output / waveform stages, ADSP cluster bypass,
and parity with the TX layout. Hamlib NET-rigctl interoperability is
unblocked end-to-end — Not1MM, MacLoggerDX, fldigi, and any other
standard rigctld client can now drive AetherSDR's frequency, mode,
filter passband, RF power, PTT, and CW. The release also clears two
silent Windows footguns (UI Scale on Windows was completely broken;
spot clients crashed on disconnect cascade), and lands a CW-knob UX
upgrade plus a dialog chrome refit on the AetherDSP Settings window.
Big thanks to @chibondking (CJ Johnson — editable CW value fields,
cwDelay optimistic-update, UI Scale Windows + reset-to-100% fix, macOS
relaunch via open -n, PGXL OPERATE/STANDBY sync), @jensenpat
(spot client thread affinity Windows crash, hardware-PTT mic_selection
fix, filter width indicator, mode-correct widen/narrow shortcuts,
Not1MM rigctld interop), @rfoust (Robbie Foust — status bar
separator dot fix, audio shutdown null-guards), and @s53zo (MIDI
VFO jog-wheel stabilization). Also crediting @ct1drb for the
original orphaned rigctld pipe-separator fix that #2438 cherry-picked.
New features
Aetherial Audio Channel Strip — RX side (#2425)
- The Channel Strip now has a fully-built RX path alongside the TX
path. RX side mirrors the TX layout: AGC-G (formerly AGC-T), EQ,
De-Esser, Compressor, Tube/Saturator, PUDU, Final Output, and
Waveform tiles, all wired through the inline RX DSP pipeline in
AudioEngine. - The CHAIN status row's ADSP tile is now a Stage-style toggle that
bypasses the entire client-side NR cluster (NR2 / NR4 / MNR / DFNR
/ RN2 / BNR) with a single click; an in-memory snapshot restores
the prior NR state on un-bypass. - ChannelStripPresets schema gains an optional
rxblock so saved
profiles capture both TX and RX state. Old preset files without
the block leave RX state untouched (forward-compatible). - New StripRxOutputPanel with peak/RMS meter, MUTE, BOOST. Strip-side
Waveform panel becomes side-aware so the same widget renders the
TX or RX scope tap based on aSide::Tx | Side::Rxflag.
Editable CW Delay / Speed / Sidetone / Pitch fields (#2429, chibondking)
- The four CW value labels in the PhoneCwApplet are now
QLineEdit
widgets. Click any value and type a number directly — SmartSDR
parity.QIntValidatorclamps to the valid range,editingFinished
drives the slider so the existing slider→model command path fires
unchanged.hasFocus()guards on the slider valueChanged path AND
onsyncCwFromModelkeep mid-edit input from being clobbered by
radio echoes.
Hamlib NET-rigctl / Not1MM interoperability (#2438, jensenpat, closes #2048 #2108)
- Pipe separator (
|) — rigctld's wire convention. AetherSDR only
recognised;, so every pipe-prefixed command returnedRPRT -4.
Cherry-picks the orphaned PR #2051 fix from @ct1drb with full
attribution. - Multi-line bare
bsend_morse — Hamlib spec allows the morse text
on the next line. Per-connectionm_pendingMorseLineflag arms the
next handleLine call to consume morse text verbatim. Required by
Not1MM contest CW. - RFPOWER
get_level/set_level— was returningRPRT -11(silent
failure). Wires through toTransmitModel::setRfPower / rfPower()
with proper 0.0–1.0 ratio ↔ 0–100 percent conversion.dump_state
set_level mask was hardcoded to KEYSPD only — now correctly reflects
the full RFPOWER + KEYSPD set. - Per-mode filter passband placement —
cmdSetModewas applying the
USB convention to every non-LSB mode, soM CW 250produced a
155 Hz filter centered at 172 Hz instead of a 250 Hz filter centered
on 0. Mirrors the canonical mode→filter-edge mapping from
VfoWidget::applyFilterPreset.
AetherDSP Settings dialog refit + VFO DSP launchers
- AetherDSP Settings dialog gets the standard frameless 18 px gradient
title bar (matching NetworkDiagnosticsDialog and AetherialAudioStrip)
with grip glyph, min/max/close trio, drag-to-move, double-click to
maximize, 8-axis resize. - New
setDialogMode(true)on AetherDspWidget bumps every inline
font-size to 13 px to match the VFO DSP toggle row, applies a
parallel 60×24 / 70×26 toggle-button geometry. Applet path leaves
this off. - Per-slice VFO DSP grid gains an
ADSPbutton (opens AetherDSP
Settings — same entry point as the Settings menu) and an
AetherVoicebutton (toggles the Aetherial Audio Channel Strip).
Bug fixes
Spot client thread affinity Windows crash (#2420, jensenpat, fixes #1929 #2418)
QTcpSocket/QUdpSocket/QWebSocketcreate an internal
QSocketNotifierwhose Win32 message-loop affinity binds to the
construction thread, not the QObject's current thread. Constructing
on main-thread thenmoveToThread()toSpotClientsleft the
notifier bound to main-thread; disconnect-cascade socket events
tripped a cross-threadsendEventassert. Each client now defers
socket creation to aninitialize()slot dispatched on the worker
thread viaQt::QueuedConnectionso internal notifiers bind there.
Audio callbacks during shutdown (#2413, rfoust)
- Quit-time crash on macOS where
WanConnection::~WanConnection
triggered a TLS disconnect cascade that emitted
radioTransmittingChanged(false)afterm_audiowas already null
but beforeQObject::~QObjecthad auto-disconnected the connected
lambdas. Surgical null guards on the three TX-state callbacks
(moxChanged,txAudioGateChanged,radioTransmittingChanged)
match the existingif (m_audio && ...)pattern used elsewhere in
the file.
Hardware PTT into radio with mic_selection=PC (#2431, jensenpat, fixes #2373 #2200)
- The interlock gate was forcing
setTransmitting(false)whenever
m_txRequestedwas false — even when the radio reported our handle
as TX owner. Hardware-keyed PTT (mic-line, ACC footswitch, RCA
TXREQ) had no bypass. PC mic audio with hardware PTT had likely
never worked through this branch. Parses the radio's interlock
source=field (SW / MIC / ACC / RCA / TUNE per FlexLib v4.2.18
ParsePTTSource) and addsMIC|ACC|RCAto the bypass list. SW
source still requiresm_txRequestedso SSB optimistic-off
responsiveness is preserved.
UI Scale not applied on Windows; reset to 100% silently ignored (#2432, chibondking)
- Two unrelated bugs in
main.cpp's pre-Qt settings read. (1) The
pre-Qt path was hardcoded to~/.config/AetherSDR/(Linux
convention); on Windows AppSettings actually writes to
%APPDATA%/AetherSDR/, so the file was never found and
QT_SCALE_FACTORnever set. Adds aQ_OS_WINbranch using
qEnvironmentVariable("APPDATA"). (2) Theif (pct != 100)guard
meant a child process inheritingQT_SCALE_FACTORfrom a scaled
parent kept using it after the user reset to 100%. Always-set fix
writes1.00so the inherited value is overridden.
macOS relaunch via open -n on UI Scale restart (#2434, chibondking)
applyUiScale()was callingQProcess::startDetachedon
applicationFilePath(), which on a.appbundle launches
Foo.app/Contents/MacOS/Foodirectly — bypassing Launch Services.
The relaunched instance shows as a separate dock entry, loses
proper activation policy, and on notarized/sandboxed builds can
fail entirely. Walks up to the bundle root and launches via
open -n <bundle>with--argspass-through.
MIDI jog-wheel VFO tuning stabilization (#2422, s53zo, fixes #2421)
- Three independent failure modes on touch-sensitive jog wheels (e.g.
Hercules DJControl Starlight). MIDI Learn could bind the touch
sensor instead of the movement CC. Saved bindings without the
relative flag interpreted unit-pulse 1/127 as absolute endpoints,
turning each detent into ~±63 steps. Tuning targets read
SliceModel::frequency()which lags behind in-flight tune commands,
making rapid spins jump backward. Fixed in three layered passes
with a 250 ms idle-timer-cleared in-flight tune target.
VFO filter width indicator showing 0.1 kHz too low (#2435, jensenpat, fixes #2197)
updateFilterLabel()computed rawfilterHigh - filterLow, which
for SSB/digital modes is ~100 Hz less than the named preset width
due to the intentional low-cut. Bug had been reported three times
(#794, #1225, this issue) — the structural fix promotes
RxApplet::formatFilterWidthto public static and has VfoWidget
call it directly. Single source of truth prevents the next swap.
Mode-correct filter widen/narrow shortcuts (#2436, jensenpat, fixes #2208)
- The
filter_widen/filter_narrowshortcuts (also bound to MIDI
global.filterWiden/global.filterNarrow) only adjusted the
upper edge by ±100 Hz. On LSB / CWL / DIGL / RTTY the upper edge is
fixed near 0, so "widen" actually collapsed the passband. New
RxApplet::stepFilterWidth(direction)walks the active mode's
preset list and routes throughapplyFilterPresetso all modes get
mode-correct edge geometry.
PGXL OPERATE/STANDBY button stuck on OPERATE (#2437, chibondking)
- AmpApplet button was only updated via the direct PGXL TCP path; if
that lagged, the button stayed on the old label. The click handler
readsm_operateBtn->text()to decide toggle direction, so a
stuck OPERATE button emittedoperate=0on click — sending another
standby command instead of returning to OPERATE. Wires
RadioModel::ampStateChanged(authoritative) to also call
AmpApplet::setState.
Status bar separator dots for unsupported optional sections (#2412, rfoust)
- TGXL and PGXL separator dots stayed visible even when the indicators
were hidden, leaving stray· ·clusters in the bottom status bar
on radios without those accessories. Optional separators now toggle
in lockstep with their matching indicators.
cwDelay optimistic-update prevents Delay slider reset (#2428, chibondking)
setCwDelay()was the lone outlier in the CW setter family —
didn't cachemsintom_cwDelaybefore emitting the command.
SubsequentphoneStateChangedemissions read stalem_cwDelay
and snapped the Delay slider back, silently re-enabling QSK on
amplifiers that can't tolerate it. Now matches the
setCwBreakIn/setCwIambicModeoptimistic-update pattern.
DSP-level slider missing on launch
- The leveled DSP toggles (NR / NB / ANF / NRL / NRS / NRF / ANFL)
push onto a target stack so the shared 4th-row slider re-targets
the most recently activated DSP. State changes arriving from radio
status (profile load, reconnect) went through aQSignalBlocker'd
path that suppressed thetoggled()signal — so on launch the
slider was hidden until the user manually toggled the DSP off and
back on. NewconnectLeveledDsphelper mirrors the user-click
stack push/pop on every state change.
Channel Strip initial height on sub-4K displays (#2440)
- The Aetherial Audio Channel Strip opened at its natural 1620 px
height on every display. On 1080p the bottom edge landed offscreen
with the resize grip unreachable. Queries
QGuiApplication::primaryScreen()->availableGeometry().height()
at construction; caps initial height to 960 px on anything below
~4K (2000 px threshold catches 1080p / 1440p / 1600p). 4K and 5K
still get the natural 1620.
Infrastructure
CI runtime + reliability sweep
- Skip CodeQL on doc-only PRs (#2397).
- Cache Windows third-party deps — Opus, FFTW, hidapi, DeepFilterNet
(#2398) — drops cold Windows builds by ~3 min. - Mirror Opus download to GitHub Releases with retry/fallback (#2399)
— replaces the unreliable Xiph mirror that occasionally 503s. - Scope CodeQL to first-party sources (#2400) — drops third_party/
noise from the analysis. - Speed up macOS Intel build with pinned Qt + ccache (#2402).
- Bump Qt to 6.8.3 LTS across all release workflows (#2404).