Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions .github/workflows/appimage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ jobs:
libgstreamer-plugins-bad1.0-0 \
libfuse2 file desktop-file-utils imagemagick \
python3-pip autoconf automake libtool libasound2-dev \
libfftw3-dev libhidapi-dev portaudio19-dev
libfftw3-dev libudev-dev libhidapi-dev portaudio19-dev

# glslc — the SPIR-V shader compiler whisper's Vulkan backend needs at
# BUILD time (CMake resolves it via find_package(Vulkan COMPONENTS glslc),
Expand Down Expand Up @@ -249,15 +249,21 @@ jobs:
# pure Qt-D-Bus backend talks to KDE Wallet / GNOME Keyring at runtime.
run: bash scripts/setup/setup-qtkeychain.sh

- name: Build pinned RTL dependencies (including float FFTW)
run: bash scripts/setup/setup-rtl-deps.sh

- name: Configure
run: |
# Without pipefail the step's exit status is tee's, so a cmake failure
# would look green. Set it in the script rather than via `shell: bash`
# (which also implies -eo pipefail) so the guard is self-contained and
# survives anyone editing the step's shell.
set -o pipefail
export PKG_CONFIG_PATH="$PWD/third_party/rtl-deps/lib/pkgconfig:${PKG_CONFIG_PATH:-}"
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_HD_FM=OFF \
-DENABLE_RTL=ON -DREQUIRE_RTL=ON \
-DCMAKE_INSTALL_PREFIX=/usr \
-DREQUIRE_SERIALPORT=ON \
-DREQUIRE_KEYCHAIN=ON \
Expand All @@ -267,7 +273,7 @@ jobs:
-DAETHER_GPU_SPECTRUM=${{ matrix.gpu_spectrum }} \
-DMQTT_TLS=OFF \
-DENABLE_NVIDIA_AFX=ON \
-DCMAKE_PREFIX_PATH="$QT_ROOT" 2>&1 | tee configure.log
-DCMAKE_PREFIX_PATH="$PWD/third_party/rtl-deps;$QT_ROOT" 2>&1 | tee configure.log

# AETHER_GPU_SPECTRUM=ON is a request, not a guarantee: CMake silently
# flips it back OFF if Qt6GuiPrivate can't be located, and CMakeLists.txt
Expand Down Expand Up @@ -304,6 +310,8 @@ jobs:
convert AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png \
-resize 256x256 AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png

mkdir -p AppDir/usr/share/aethersdr
cp -R third_party/rtl-deps/share/aethersdr-rtl-sources AppDir/usr/share/aethersdr/
# Bundle DeepFilterNet3 model for DFNR
if [ -f third_party/deepfilter/models/DeepFilterNet3_onnx.tar.gz ]; then
cp third_party/deepfilter/models/DeepFilterNet3_onnx.tar.gz \
Expand Down Expand Up @@ -408,13 +416,14 @@ jobs:
# libsherpa-onnx-c-api.so, and libonnxruntime.so (the app links sherpa's
# copy). Qt6 DBus, which the keychain backend needs at runtime, is pulled
# in transitively.
export LD_LIBRARY_PATH="${{ github.workspace }}/third_party/qtkeychain/lib:${{ github.workspace }}/third_party/onnxruntime/lib:${{ github.workspace }}/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}"
export LD_LIBRARY_PATH="${{ github.workspace }}/third_party/rtl-deps/lib:${{ github.workspace }}/third_party/qtkeychain/lib:${{ github.workspace }}/third_party/onnxruntime/lib:${{ github.workspace }}/third_party/sherpa-onnx/lib:${LD_LIBRARY_PATH:-}"
./linuxdeploy-${{ matrix.arch }}.AppImage \
--appdir AppDir \
--plugin qt \
--desktop-file AppDir/usr/share/applications/AetherSDR.desktop \
--icon-file AppDir/usr/share/icons/hicolor/256x256/apps/aethersdr.png \
--output appimage
python3 tools/check_rtl_package.py AppDir --platform linux

# Nothing else in the build would notice these plugins going missing: Qt
# dlopen()s them at runtime rather than linking them, so no dependency walk
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/macos-dmg.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ jobs:
# bakes in that nothing downstream can correct, so a 14.0 tree must
# never restore into a 12.0 build — that would silently recreate the
# bug this change exists to remove.
key: macos-deps-${{ matrix.label }}-deploy${{ matrix.deploy_target }}-${{ hashFiles('scripts/setup/setup-macos-deps.sh') }}
key: macos-deps-${{ matrix.label }}-deploy${{ matrix.deploy_target }}-${{ hashFiles('scripts/setup/setup-macos-deps.sh', 'scripts/setup/setup-rtl-deps.sh', 'scripts/setup/rtl-dependencies.json') }}

# ~8 min cold, then cached until the script or the target changes — the
# same deal third_party/qtkeychain already has. That cost buys the DMG a
Expand Down Expand Up @@ -362,6 +362,8 @@ jobs:
KEYCHAIN_FLAG="-DREQUIRE_KEYCHAIN=ON"
cmake -B build -G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Release \
-DENABLE_HD_FM=OFF \
-DENABLE_RTL=ON -DREQUIRE_RTL=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OSX_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}" \
Expand Down Expand Up @@ -466,6 +468,9 @@ jobs:
"$DEPLOYQT" build/AetherSDR.app \
-verbose=1 \
-always-overwrite
mkdir -p build/AetherSDR.app/Contents/Resources
cp -R third_party/macos-deps/share/aethersdr-rtl-sources build/AetherSDR.app/Contents/Resources/
python3 tools/check_rtl_package.py build/AetherSDR.app --platform macos

# The artifact-side half of the pin. Everything upstream of here asserts
# what we *asked* for; this asserts what the DMG actually carries, which
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/static-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,9 @@ jobs:
with:
python-version: "3.12"

- name: RTL release requirement combinations
run: python tools/test_require_rtl.py

# ── aetherd-migration ratchet (docs/aetherd-headless-engine-design.md §10)
# EB1/EB2 — the engine (src/core/ + src/models/) must never depend on
# the UI: no gui/ header includes, no QtWidgets.
Expand Down
21 changes: 19 additions & 2 deletions .github/workflows/windows-installer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,23 @@ jobs:
id: cache-fftw
with:
path: third_party/fftw3
key: fftw-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-fftw.ps1') }}
key: fftw-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-fftw.ps1', 'scripts/setup/rtl-dependencies.json') }}

- name: Setup FFTW3 float (NR4 dependency)
if: steps.cache-fftw.outputs.cache-hit != 'true'
run: .\scripts\setup\setup-fftw.ps1

- name: Cache RTL-SDR and libusb
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
id: cache-rtl
with:
path: third_party/rtl-deps
key: rtl-${{ runner.os }}-${{ hashFiles('scripts/setup/setup-rtl-deps.ps1', 'scripts/setup/rtl-dependencies.json') }}

- name: Setup RTL-SDR and libusb
if: steps.cache-rtl.outputs.cache-hit != 'true'
run: .\scripts\setup\setup-rtl-deps.ps1

- name: Cache hidapi
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9
id: cache-hidapi
Expand Down Expand Up @@ -248,7 +259,7 @@ jobs:
# native command), so a configure failure still fails this step —
# the reason the bash legs need `set -o pipefail` and this one does
# not.
cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log
cmake -B build -G "Ninja" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_HD_FM=OFF -DENABLE_RTL=ON -DREQUIRE_RTL=ON -DMQTT_TLS=OFF -DREQUIRE_KEYCHAIN=ON -DREQUIRE_SERIALPORT=ON -DREQUIRE_ASR_ONNX=ON -DREQUIRE_ASR_SHERPA=ON -DREQUIRE_ASR_GPU=ON -DAETHER_GPU_SPECTRUM=ON -DCMAKE_PREFIX_PATH="$env:VULKAN_SDK" -DENABLE_NVIDIA_AFX=ON -DCMAKE_TOOLCHAIN_FILE="$env:CMAKE_TOOLCHAIN_FILE" -DVCPKG_TARGET_TRIPLET="$env:VCPKG_TARGET_TRIPLET" | Tee-Object -FilePath configure.log

# AETHER_GPU_SPECTRUM=ON is a request, not a guarantee: CMake flips it
# back OFF without failing when Qt6GuiPrivate can't be located, and
Expand Down Expand Up @@ -399,6 +410,12 @@ jobs:
Write-Host "Bundling ASR runtime DLL: $($_.Name)"
Copy-Item $_.FullName deploy\ -Force
}
Copy-Item third_party\rtl-deps\bin\*.dll deploy\
New-Item -ItemType Directory -Force deploy\licenses\rtl | Out-Null
Copy-Item third_party\rtl-deps\share\aethersdr-rtl-sources deploy\licenses\rtl\ -Recurse
Copy-Item third_party\fftw3\share\aethersdr-rtl-sources deploy\licenses\fftw -Recurse
python tools/check_rtl_package.py deploy --platform windows
if ($LASTEXITCODE -ne 0) { throw "RTL payload validation failed" }
# Vulkan loader for the whisper GPU backend. ggml-vulkan hard-links
# vulkan-1, so the DLL must ship or the exe fails to start even on
# CPU-only machines (the loader then enumerates zero devices and ggml
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,10 @@ third_party/qtkeychain-build/
# never be tracked or show up as untracked noise in the parent checkout.
.worktrees/

# Built pinned RTL dependency prefix and intermediate files
third_party/rtl-deps/
third_party/macos-deps/
.rtl-deps-build/

# macOS Finder metadata
.DS_Store
56 changes: 51 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ if(NOT AETHERSDR_SANITIZER STREQUAL "none")
unset(_aether_san_list)
endif()
option(ENABLE_RTL "Enable the experimental receive-only RTL-SDR backend" ON)
option(REQUIRE_RTL "Fail configuration unless the RTL backend and float FFTW are available" OFF)
# Which SSB modulator the Hermes-Lite 2 transmit path is BUILT with. There is
# no runtime switch and there must not be one: two silent transmit paths are
# worse than one, and Hl2TxDsp.cpp records that the prior TXA attempt failed
Expand Down Expand Up @@ -405,6 +406,10 @@ else()
find_package(PkgConfig QUIET)
if(PkgConfig_FOUND)
pkg_check_modules(PORTAUDIO portaudio-2.0)
if(PORTAUDIO_FOUND)
# Preserve the resolved prefix; bare -l names can select Homebrew first.
set(PORTAUDIO_LIBRARIES ${PORTAUDIO_LINK_LIBRARIES})
endif()
endif()
endif()

Expand All @@ -423,6 +428,10 @@ if(WIN32)
else()
if(PkgConfig_FOUND)
pkg_check_modules(FFTW3 fftw3)
if(FFTW3_FOUND)
# Preserve the resolved prefix; bare -l names can select Homebrew first.
set(FFTW3_LIBRARIES ${FFTW3_LINK_LIBRARIES})
endif()
endif()
if(NOT FFTW3_FOUND)
find_library(FFTW3_LIB fftw3)
Expand Down Expand Up @@ -973,6 +982,7 @@ set(CORE_SOURCES
src/core/TciIoWorker.cpp
src/core/TciClient.h
src/core/TciServer.cpp
src/models/DaxReceiveModel.cpp
src/core/TciRxConverter.cpp
src/core/TciPeerProcess.cpp
src/core/TciProtocol.cpp
Expand Down Expand Up @@ -1094,6 +1104,7 @@ endif()

set(MODEL_SOURCES
src/models/DecoderAudioModel.cpp
src/models/Ax25ReceiveModel.cpp
src/models/AprsDigipeaterModel.cpp
src/models/Nr2SettingsModel.cpp
src/models/Rn2SettingsModel.cpp
Expand Down Expand Up @@ -2644,6 +2655,9 @@ else()
if(NOT HIDAPI_FOUND)
pkg_check_modules(HIDAPI hidapi)
endif()
if(HIDAPI_FOUND)
set(HIDAPI_LIBRARIES ${HIDAPI_LINK_LIBRARIES})
endif()
endif()
endif()

Expand All @@ -2657,8 +2671,8 @@ endif()
# plain header+library probe so the switch below is reachable on every
# platform, not only the pkg-config ones.
if(ENABLE_RTL AND NOT RTLSDR_FOUND)
find_path(RTLSDR_INCLUDE_DIR rtl-sdr.h)
find_library(RTLSDR_LIBRARY NAMES rtlsdr librtlsdr)
find_path(RTLSDR_INCLUDE_DIR rtl-sdr.h HINTS "${CMAKE_SOURCE_DIR}/third_party/rtl-deps/include")
find_library(RTLSDR_LIBRARY NAMES rtlsdr librtlsdr HINTS "${CMAKE_SOURCE_DIR}/third_party/rtl-deps/lib")
if(RTLSDR_INCLUDE_DIR AND RTLSDR_LIBRARY)
add_library(RtlSdr::RtlSdr UNKNOWN IMPORTED)
set_target_properties(RtlSdr::RtlSdr PROPERTIES
Expand All @@ -2672,8 +2686,8 @@ else()
endif()

if(ENABLE_RTL AND NOT RTL_FFTW3F_FOUND)
find_path(RTL_FFTW3F_INCLUDE_DIR fftw3.h)
find_library(RTL_FFTW3F_LIBRARY NAMES fftw3f libfftw3f libfftw3f-3)
find_path(RTL_FFTW3F_INCLUDE_DIR fftw3.h HINTS "${CMAKE_SOURCE_DIR}/third_party/fftw3/include")
find_library(RTL_FFTW3F_LIBRARY NAMES fftw3f libfftw3f libfftw3f-3 HINTS "${CMAKE_SOURCE_DIR}/third_party/fftw3/lib")
if(RTL_FFTW3F_INCLUDE_DIR AND RTL_FFTW3F_LIBRARY)
add_library(RtlFftw3f::RtlFftw3f UNKNOWN IMPORTED)
set_target_properties(RtlFftw3f::RtlFftw3f PROPERTIES
Expand Down Expand Up @@ -2706,6 +2720,33 @@ else()
message(STATUS "RTL-SDR ... disabled — librtlsdr not found")
endif()

# The optional decoder is a separate feature proposal (RFC #6079).
option(ENABLE_HD_FM "Build experimental embedded digital FM" OFF)
if(ENABLE_HD_FM)
message(FATAL_ERROR "Digital FM is not included in the analog RTL submission. See RFC #6079; configure with -DENABLE_HD_FM=OFF.")
endif()

include(cmake/AetherRequireRtl.cmake)
aether_require_rtl()

# Stage RTL independently of the optional NR4 and Digital FM features.
if(WIN32 AND AETHER_BACKEND_RTL)
foreach(_rtl_dll
"${CMAKE_SOURCE_DIR}/third_party/rtl-deps/bin/rtlsdr.dll"
"${CMAKE_SOURCE_DIR}/third_party/rtl-deps/bin/libusb-1.0.dll"
"${CMAKE_SOURCE_DIR}/third_party/fftw3/bin/libfftw3f-3.dll")
if(EXISTS "${_rtl_dll}")
foreach(_rtl_app AetherSDR aetherd)
add_custom_command(TARGET ${_rtl_app} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${_rtl_dll}" "$<TARGET_FILE_DIR:${_rtl_app}>")
endforeach()
elseif(REQUIRE_RTL)
message(FATAL_ERROR "Required RTL runtime missing: ${_rtl_dll}; run scripts/setup/setup-rtl-deps.ps1 and setup-fftw.ps1")
endif()
endforeach()
endif()

# Ulanzi Dial backend — one per platform. All three implementations
# expose the same Qt signal contract (see UlanziDialBackend.h); the
# mapper dialog and MainWindow dispatcher are unchanged across
Expand Down Expand Up @@ -3070,7 +3111,12 @@ if(ENABLE_SPECBLEACH)
endif()
endif()
# libspecbleach uses fftwf (float precision FFTW3)
find_library(FFTW3F_LIB fftw3f HINTS /opt/homebrew/lib /usr/local/lib ${CMAKE_SOURCE_DIR}/third_party/fftw3/lib)
if(RTL_FFTW3F_TARGET AND TARGET "${RTL_FFTW3F_TARGET}")
# NR4 and RTL must use the same float FFTW, including its prefix.
set(FFTW3F_LIB "${RTL_FFTW3F_TARGET}")
else()
find_library(FFTW3F_LIB fftw3f HINTS /opt/homebrew/lib /usr/local/lib ${CMAKE_SOURCE_DIR}/third_party/fftw3/lib)
endif()
if(FFTW3F_LIB)
target_link_libraries(aethercore PRIVATE ${FFTW3F_LIB})
else()
Expand Down
6 changes: 6 additions & 0 deletions cmake/AetherRequireRtl.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Release builds must not silently lose hardware support. Developer OFF is valid.
function(aether_require_rtl)
if(REQUIRE_RTL AND (NOT ENABLE_RTL OR NOT RTLSDR_FOUND OR NOT RTL_FFTW3F_FOUND))
message(FATAL_ERROR "REQUIRE_RTL needs ENABLE_RTL=ON, librtlsdr and float FFTW (fftw3f). Run the platform dependency setup; use REQUIRE_RTL=OFF only for intentional developer builds.")
endif()
endfunction()
12 changes: 12 additions & 0 deletions docs/architecture/aetherd-touchpoint-tags.json
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,12 @@
"split": "",
"confidence": "high"
},
"models/DaxReceiveModel.h": {
"tag": "universal",
"note": "Owner-thread adapter from typed native receive PCM and stable TCI receiver bindings to the existing 24 kHz virtual audio bridges; no radio wire protocol.",
"split": "",
"confidence": "high"
},
"models/MeterModel.h": {
"tag": "mixed(flex)",
"note": "Meter registry+values are core-profile; VITA-49 raw scaling, TGXL/PGXL amp routing, COMPPEAK quirks are Flex",
Expand Down Expand Up @@ -1438,5 +1444,11 @@
"note": "Selected-receiver fixed24 decoder PCM and bounded delivery, independent of speaker controls. Native ingress uses normalized typed slice frames; the compatibility DAX lane uses RadioModel's existing stream and holder registry.",
"split": "Core: receiver selection, epochs, bounded backlog, rate conversion and decoder resets. Flex compatibility: DAX channel selection and central consumer holds; no wire commands are generated here.",
"confidence": "high"
},
"models/Ax25ReceiveModel.h": {
"tag": "universal",
"note": "Selected AX.25 receive model over the existing typed decoder route; continuous mono24 conversion and bounded modem worker results retain source leases and generation retirement. Existing transmit ownership is separate.",
"split": "",
"confidence": "high"
}
}
Loading
Loading