mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-07-08 18:15:21 +02:00
Compare commits
1 Commits
nightly
...
new_source
Author | SHA1 | Date | |
---|---|---|---|
c488d72ce2 |
7
.github/pull_request_template.md
vendored
7
.github/pull_request_template.md
vendored
@ -1,7 +0,0 @@
|
||||
# Important
|
||||
|
||||
Only minor bug fixes and bandplans are accepted.
|
||||
|
||||
Pull requests adding features or any bug fix that requires significant code changes will be automatically rejected.
|
||||
|
||||
Open an issue requesting a feature or discussing a possible bugfix instead.
|
79
.github/workflows/build_all.yml
vendored
79
.github/workflows/build_all.yml
vendored
@ -34,10 +34,10 @@ jobs:
|
||||
|
||||
- name: Patch Pothos with earlier libusb version
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
|
||||
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/"
|
||||
|
||||
- name: Download SDRPlay API
|
||||
run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu&confirm=t" -OutFile ${{runner.workspace}}/SDRPlay.zip
|
||||
run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu" -OutFile ${{runner.workspace}}/SDRPlay.zip
|
||||
|
||||
- name: Install SDRPlay API
|
||||
run: 7z x ${{runner.workspace}}/SDRPlay.zip -o"C:/Program Files/"
|
||||
@ -58,17 +58,14 @@ jobs:
|
||||
run: mkdir "C:/Program Files/codec2" ; mkdir "C:/Program Files/codec2/include" ; mkdir "C:/Program Files/codec2/include/codec2" ; mkdir "C:/Program Files/codec2/lib" ; cd "codec2" ; xcopy "src" "C:/Program Files/codec2/include" ; cd "build" ; xcopy "src" "C:/Program Files/codec2/lib" ; xcopy "codec2" "C:/Program Files/codec2/include/codec2"
|
||||
|
||||
- name: Install vcpkg dependencies
|
||||
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows
|
||||
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows
|
||||
|
||||
- name: Install rtaudio
|
||||
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
|
||||
|
||||
- name: Install libperseus-sdr
|
||||
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
|
||||
|
||||
- name: Prepare CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@ -96,8 +93,11 @@ jobs:
|
||||
- name: Update brew repositories
|
||||
run: brew update
|
||||
|
||||
- name: Fix stuff
|
||||
run: rm -f /usr/local/bin/2to3* /usr/local/bin/idle3* /usr/local/bin/pydoc3* /usr/local/bin/python3* /usr/local/bin/python3-config* && brew reinstall gettext
|
||||
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
|
||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf rtl-sdr libbladerf codec2 zstd && pip3 install mako
|
||||
|
||||
- name: Install volk
|
||||
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
@ -106,7 +106,7 @@ jobs:
|
||||
run: wget https://www.sdrplay.com/software/SDRplay_RSP_API-MacOSX-3.07.3.pkg && sudo installer -pkg SDRplay_RSP_API-MacOSX-3.07.3.pkg -target /
|
||||
|
||||
- name: Install libiio
|
||||
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
run: git clone https://github.com/analogdevicesinc/libiio && cd libiio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
|
||||
- name: Install libad9361
|
||||
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
@ -114,15 +114,9 @@ jobs:
|
||||
- name: Install LimeSuite
|
||||
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
|
||||
- name: Install libperseus
|
||||
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
|
||||
|
||||
- name: Install more recent librtlsdr
|
||||
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
|
||||
|
||||
- name: Prepare CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@ -137,13 +131,13 @@ jobs:
|
||||
with:
|
||||
name: sdrpp_macos_intel
|
||||
path: ${{runner.workspace}}/sdrpp_macos_intel.zip
|
||||
|
||||
|
||||
build_debian_buster:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Create Docker Image
|
||||
run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build
|
||||
|
||||
@ -182,28 +176,6 @@ jobs:
|
||||
name: sdrpp_debian_bullseye_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_debian_bookworm:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Create Docker Image
|
||||
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bookworm && docker build . --tag sdrpp_build
|
||||
|
||||
- name: Run Container
|
||||
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||
|
||||
- name: Recover Deb Archive
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||
|
||||
- name: Save Deb Archive
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sdrpp_debian_bookworm_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_debian_sid:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@ -226,6 +198,28 @@ jobs:
|
||||
name: sdrpp_debian_sid_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
# build_ubuntu_bionic:
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
|
||||
# - name: Create Docker Image
|
||||
# run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_bionic && docker build . --tag sdrpp_build
|
||||
|
||||
# - name: Run Container
|
||||
# run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||
|
||||
# - name: Recover Deb Archive
|
||||
# working-directory: ${{runner.workspace}}
|
||||
# run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||
|
||||
# - name: Save Deb Archive
|
||||
# uses: actions/upload-artifact@v3
|
||||
# with:
|
||||
# name: sdrpp_ubuntu_bionic_amd64
|
||||
# path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_ubuntu_focal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@ -281,7 +275,7 @@ jobs:
|
||||
|
||||
- name: Prepare CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_USRP_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_USRP_SOURCE=ON
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@ -325,7 +319,7 @@ jobs:
|
||||
path: ${{runner.workspace}}/sdrpp.apk
|
||||
|
||||
create_full_archive:
|
||||
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_raspios_bullseye_armhf', 'build_android']
|
||||
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_raspios_bullseye_armhf', 'build_android']
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -339,7 +333,6 @@ jobs:
|
||||
mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ &&
|
||||
mv sdrpp_debian_buster_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_amd64.deb &&
|
||||
mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb &&
|
||||
mv sdrpp_debian_bookworm_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_amd64.deb &&
|
||||
mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
|
||||
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
|
||||
mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb &&
|
||||
|
@ -17,25 +17,24 @@ option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
|
||||
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
||||
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
|
||||
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
|
||||
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON)
|
||||
option(OPT_BUILD_SDRPP_SERVER_SOURCE "Build SDR++ Server Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_RFSPACE_SOURCE "Build RFspace Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_RTL_SDR_SOURCE "Build RTL-SDR Source Module (Dependencies: librtlsdr)" ON)
|
||||
option(OPT_BUILD_RTL_TCP_SOURCE "Build RTL-TCP Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_SDRPP_SERVER_SOURCE "Build SDR++ Server Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_SDRPLAY_SOURCE "Build SDRplay Source Module (Dependencies: libsdrplay)" OFF)
|
||||
option(OPT_BUILD_SOAPY_SOURCE "Build SoapySDR Source Module (Dependencies: soapysdr)" ON)
|
||||
option(OPT_BUILD_SPECTRAN_SOURCE "Build Spectran Source Module (Dependencies: Aaronia RTSA Suite)" OFF)
|
||||
option(OPT_BUILD_SPECTRAN_HTTP_SOURCE "Build Spectran HTTP Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_SPYSERVER_SOURCE "Build SpyServer Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_PLUTOSDR_SOURCE "Build PlutoSDR Source Module (Dependencies: libiio, libad9361)" ON)
|
||||
option(OPT_BUILD_USRP_SOURCE "Build USRP Source Module (libuhd)" OFF)
|
||||
|
||||
# Sinks
|
||||
option(OPT_BUILD_ANDROID_AUDIO_SINK "Build Android Audio Sink Module (Dependencies: AAudio, only for android)" OFF)
|
||||
option(OPT_BUILD_AUDIO_SINK "Build Audio Sink Module (Dependencies: rtaudio)" ON)
|
||||
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
|
||||
option(OPT_BUILD_NETWORK_SINK "Build Audio Sink Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Dependencies: portaudio)" OFF)
|
||||
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
|
||||
|
||||
# Decoders
|
||||
option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF)
|
||||
@ -142,13 +141,9 @@ if (OPT_BUILD_LIMESDR_SOURCE)
|
||||
add_subdirectory("source_modules/limesdr_source")
|
||||
endif (OPT_BUILD_LIMESDR_SOURCE)
|
||||
|
||||
if (OPT_BUILD_PERSEUS_SOURCE)
|
||||
add_subdirectory("source_modules/perseus_source")
|
||||
endif (OPT_BUILD_PERSEUS_SOURCE)
|
||||
|
||||
if (OPT_BUILD_PLUTOSDR_SOURCE)
|
||||
add_subdirectory("source_modules/plutosdr_source")
|
||||
endif (OPT_BUILD_PLUTOSDR_SOURCE)
|
||||
if (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
||||
add_subdirectory("source_modules/sdrpp_server_source")
|
||||
endif (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
||||
|
||||
if (OPT_BUILD_RFSPACE_SOURCE)
|
||||
add_subdirectory("source_modules/rfspace_source")
|
||||
@ -162,10 +157,6 @@ if (OPT_BUILD_RTL_TCP_SOURCE)
|
||||
add_subdirectory("source_modules/rtl_tcp_source")
|
||||
endif (OPT_BUILD_RTL_TCP_SOURCE)
|
||||
|
||||
if (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
||||
add_subdirectory("source_modules/sdrpp_server_source")
|
||||
endif (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
||||
|
||||
if (OPT_BUILD_SDRPLAY_SOURCE)
|
||||
add_subdirectory("source_modules/sdrplay_source")
|
||||
endif (OPT_BUILD_SDRPLAY_SOURCE)
|
||||
@ -186,6 +177,10 @@ if (OPT_BUILD_SPYSERVER_SOURCE)
|
||||
add_subdirectory("source_modules/spyserver_source")
|
||||
endif (OPT_BUILD_SPYSERVER_SOURCE)
|
||||
|
||||
if (OPT_BUILD_PLUTOSDR_SOURCE)
|
||||
add_subdirectory("source_modules/plutosdr_source")
|
||||
endif (OPT_BUILD_PLUTOSDR_SOURCE)
|
||||
|
||||
if (OPT_BUILD_USRP_SOURCE)
|
||||
add_subdirectory("source_modules/usrp_source")
|
||||
endif (OPT_BUILD_USRP_SOURCE)
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Pull Requests
|
||||
|
||||
**I DO NOT ACCEPT PULL-REQUEST FOR FEATURES OR BUGFIXES REQUIRING SIGNIFICANT CODE/STRUCTURE CHANGES.**
|
||||
**SUCH PULL REQUESTS WILL BE CLOSED AUTOMATICALLY. OPEN AN ISSUE DETAILING FEATURE REQUESTS OR POTENTIAL BUGFIX INSTEAD.**
|
||||
TODO
|
||||
|
||||
# Code Style
|
||||
|
||||
@ -123,4 +122,4 @@ Please follow this guide to properly format the JSON files for custom color maps
|
||||
|
||||
* All additions and/or bug fixes to the core must not add additional dependencies.
|
||||
* Use VSCode for development, VS seems to cause issues.
|
||||
* DO NOT use libboost for any code meant for this repository
|
||||
* DO NOT use libboost for any code meant for this repository
|
@ -13,7 +13,6 @@ endif (USE_BUNDLE_DEFAULTS)
|
||||
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
||||
|
||||
add_definitions(-DSDRPP_IS_CORE)
|
||||
add_definitions(-DFLOG_ANDROID_TAG="SDR++")
|
||||
if (MSVC)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
endif ()
|
||||
@ -108,6 +107,7 @@ elseif (ANDROID)
|
||||
)
|
||||
|
||||
target_link_libraries(sdrpp_core PUBLIC
|
||||
/sdr-kit/${ANDROID_ABI}/lib/libcpu_features.a
|
||||
/sdr-kit/${ANDROID_ABI}/lib/libvolk.so
|
||||
/sdr-kit/${ANDROID_ABI}/lib/libfftw3f.so
|
||||
/sdr-kit/${ANDROID_ABI}/lib/libzstd.so
|
||||
|
@ -117,10 +117,6 @@ int sdrpp_main(int argc, char* argv[]) {
|
||||
defConfig["colorMap"] = "Classic";
|
||||
defConfig["fftHold"] = false;
|
||||
defConfig["fftHoldSpeed"] = 60;
|
||||
defConfig["fftSmoothing"] = false;
|
||||
defConfig["fftSmoothingSpeed"] = 100;
|
||||
defConfig["snrSmoothing"] = false;
|
||||
defConfig["snrSmoothingSpeed"] = 20;
|
||||
defConfig["fastFFT"] = false;
|
||||
defConfig["fftHeight"] = 300;
|
||||
defConfig["fftRate"] = 20;
|
||||
@ -181,8 +177,6 @@ int sdrpp_main(int argc, char* argv[]) {
|
||||
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
|
||||
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
|
||||
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
|
||||
defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";
|
||||
|
@ -12,7 +12,6 @@ namespace sdrpp_credits {
|
||||
"Howard0su",
|
||||
"John Donkersley",
|
||||
"Joshua Kimsey",
|
||||
"Manawyrm",
|
||||
"Martin Hauke",
|
||||
"Marvin Sinister",
|
||||
"Maxime Biette",
|
||||
@ -22,6 +21,7 @@ namespace sdrpp_credits {
|
||||
"Shuyuan Liu",
|
||||
"Syne Ardwin (WI9SYN)",
|
||||
"Szymon Zakrent",
|
||||
"Tobias Mädel",
|
||||
"Youssef Touil",
|
||||
"Zimm"
|
||||
};
|
||||
@ -55,33 +55,24 @@ namespace sdrpp_credits {
|
||||
"Dale L Puckett (K0HYD)",
|
||||
"Daniele D'Agnelli",
|
||||
"D. Jones",
|
||||
"Dexruus",
|
||||
"EB3FRN",
|
||||
"Eric Johnson",
|
||||
"Ernest Murphy (NH7L)",
|
||||
"Flinger Films",
|
||||
"Frank Werner (HB9FXQ)",
|
||||
"gringogrigio",
|
||||
"Jeff Moe",
|
||||
"Joe Cupano",
|
||||
"KD1SQ",
|
||||
"Kezza",
|
||||
"Krys Kamieniecki",
|
||||
"Lee Donaghy",
|
||||
"Lee KD1SQ",
|
||||
".lozenge. (Hank Hill)",
|
||||
"Martin Herren (HB9FXX)",
|
||||
"ON4MU",
|
||||
"Passion-Radio.com",
|
||||
"Paul Maine",
|
||||
"Peter Betz",
|
||||
"Scanner School",
|
||||
"Scott Palmer",
|
||||
"SignalsEverywhere",
|
||||
"Syne Ardwin (WI9SYN)",
|
||||
"W4IPA",
|
||||
"William Arcand (W1WRA)",
|
||||
"Yves Rougy",
|
||||
"Zipper"
|
||||
};
|
||||
|
||||
|
@ -67,6 +67,10 @@ namespace dsp::buffer {
|
||||
sizes[writeCur] = count;
|
||||
writeCur++;
|
||||
writeCur = ((writeCur) % TEST_BUFFER_SIZE);
|
||||
|
||||
// if (((writeCur - readCur + TEST_BUFFER_SIZE) % TEST_BUFFER_SIZE) >= (TEST_BUFFER_SIZE-2)) {
|
||||
// flog::warn("Overflow");
|
||||
// }
|
||||
}
|
||||
cnd.notify_all();
|
||||
_in->flush();
|
||||
|
@ -3,8 +3,6 @@
|
||||
#include "quadrature.h"
|
||||
#include "../filter/fir.h"
|
||||
#include "../taps/low_pass.h"
|
||||
#include "../taps/high_pass.h"
|
||||
#include "../taps/band_pass.h"
|
||||
#include "../convert/mono_to_stereo.h"
|
||||
|
||||
namespace dsp::demod {
|
||||
@ -19,26 +17,22 @@ namespace dsp::demod {
|
||||
~FM() {
|
||||
if (!base_type::_block_init) { return; }
|
||||
base_type::stop();
|
||||
dsp::taps::free(filterTaps);
|
||||
dsp::taps::free(lpfTaps);
|
||||
}
|
||||
|
||||
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass, bool highPass) {
|
||||
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass) {
|
||||
_samplerate = samplerate;
|
||||
_bandwidth = bandwidth;
|
||||
_lowPass = lowPass;
|
||||
_highPass = highPass;
|
||||
|
||||
demod.init(NULL, bandwidth / 2.0, _samplerate);
|
||||
loadDummyTaps();
|
||||
fir.init(NULL, filterTaps);
|
||||
|
||||
// Initialize taps
|
||||
updateFilter(lowPass, highPass);
|
||||
lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
|
||||
lpf.init(NULL, lpfTaps);
|
||||
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
demod.out.free();
|
||||
}
|
||||
fir.out.free();
|
||||
lpf.out.free();
|
||||
|
||||
base_type::init(in);
|
||||
}
|
||||
@ -49,7 +43,9 @@ namespace dsp::demod {
|
||||
base_type::tempStop();
|
||||
_samplerate = samplerate;
|
||||
demod.setDeviation(_bandwidth / 2.0, _samplerate);
|
||||
updateFilter(_lowPass, _highPass);
|
||||
dsp::taps::free(lpfTaps);
|
||||
lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
|
||||
lpf.setTaps(lpfTaps);
|
||||
base_type::tempStart();
|
||||
}
|
||||
|
||||
@ -58,20 +54,19 @@ namespace dsp::demod {
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
if (bandwidth == _bandwidth) { return; }
|
||||
_bandwidth = bandwidth;
|
||||
std::lock_guard<std::mutex> lck2(lpfMtx);
|
||||
demod.setDeviation(_bandwidth / 2.0, _samplerate);
|
||||
updateFilter(_lowPass, _highPass);
|
||||
dsp::taps::free(lpfTaps);
|
||||
lpfTaps = dsp::taps::lowPass(_bandwidth / 2, (_bandwidth / 2) * 0.1, _samplerate);
|
||||
lpf.setTaps(lpfTaps);
|
||||
}
|
||||
|
||||
void setLowPass(bool lowPass) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
updateFilter(lowPass, _highPass);
|
||||
}
|
||||
|
||||
void setHighPass(bool highPass) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
updateFilter(_lowPass, highPass);
|
||||
std::lock_guard<std::mutex> lck2(lpfMtx);
|
||||
_lowPass = lowPass;
|
||||
lpf.reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
@ -79,23 +74,23 @@ namespace dsp::demod {
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
base_type::tempStop();
|
||||
demod.reset();
|
||||
fir.reset();
|
||||
lpf.reset();
|
||||
base_type::tempStart();
|
||||
}
|
||||
|
||||
inline int process(int count, dsp::complex_t* in, T* out) {
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
demod.process(count, in, out);
|
||||
if (filtering) {
|
||||
std::lock_guard<std::mutex> lck(filterMtx);
|
||||
fir.process(count, out, out);
|
||||
if (_lowPass) {
|
||||
std::lock_guard<std::mutex> lck(lpfMtx);
|
||||
lpf.process(count, out, out);
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<T, stereo_t>) {
|
||||
demod.process(count, in, demod.out.writeBuf);
|
||||
if (filtering) {
|
||||
std::lock_guard<std::mutex> lck(filterMtx);
|
||||
fir.process(count, demod.out.writeBuf, demod.out.writeBuf);
|
||||
if (_lowPass) {
|
||||
std::lock_guard<std::mutex> lck(lpfMtx);
|
||||
lpf.process(count, demod.out.writeBuf, demod.out.writeBuf);
|
||||
}
|
||||
convert::MonoToStereo::process(count, demod.out.writeBuf, out);
|
||||
}
|
||||
@ -114,50 +109,13 @@ namespace dsp::demod {
|
||||
}
|
||||
|
||||
private:
|
||||
void updateFilter(bool lowPass, bool highPass) {
|
||||
std::lock_guard<std::mutex> lck(filterMtx);
|
||||
|
||||
// Update values
|
||||
_lowPass = lowPass;
|
||||
_highPass = highPass;
|
||||
filtering = (lowPass || highPass);
|
||||
|
||||
// Free filter taps
|
||||
dsp::taps::free(filterTaps);
|
||||
|
||||
// Generate filter depending on low and high pass settings
|
||||
if (_lowPass && _highPass) {
|
||||
filterTaps = dsp::taps::bandPass<float>(300.0, _bandwidth / 2.0, 100.0, _samplerate);
|
||||
}
|
||||
else if (_highPass) {
|
||||
filterTaps = dsp::taps::highPass(300.0, 100.0, _samplerate);
|
||||
}
|
||||
else if (_lowPass) {
|
||||
filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
|
||||
}
|
||||
else {
|
||||
loadDummyTaps();
|
||||
}
|
||||
|
||||
// Set filter to use new taps
|
||||
fir.setTaps(filterTaps);
|
||||
fir.reset();
|
||||
}
|
||||
|
||||
void loadDummyTaps() {
|
||||
float dummyTap = 1.0f;
|
||||
filterTaps = dsp::taps::fromArray<float>(1, &dummyTap);
|
||||
}
|
||||
|
||||
double _samplerate;
|
||||
double _bandwidth;
|
||||
bool _lowPass;
|
||||
bool _highPass;
|
||||
bool filtering;
|
||||
|
||||
Quadrature demod;
|
||||
tap<float> filterTaps;
|
||||
filter::FIR<float, float> fir;
|
||||
std::mutex filterMtx;
|
||||
tap<float> lpfTaps;
|
||||
filter::FIR<float, float> lpf;
|
||||
std::mutex lpfMtx;
|
||||
};
|
||||
}
|
@ -2,6 +2,5 @@
|
||||
#include "../multirate/rrc_interpolator.h"
|
||||
|
||||
namespace dsp::mod {
|
||||
// TODO: Check if resample before RRC is better than using the RRC taps as a filter (bandwidth probably not correct for alias-free resampling)
|
||||
typedef multirate::RRCInterpolator<complex_t> PSK;
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
#include "../math/hz_to_rads.h"
|
||||
|
||||
namespace dsp::mod {
|
||||
class Quadrature : public Processor<float, complex_t> {
|
||||
class Quadrature : Processor<float, complex_t> {
|
||||
using base_type = Processor<float, complex_t>;
|
||||
public:
|
||||
Quadrature() {}
|
||||
|
@ -83,6 +83,8 @@ namespace dsp::multirate {
|
||||
int interp = OutSR / gcd;
|
||||
int decim = InSR / gcd;
|
||||
|
||||
flog::warn("interp: {0}, decim: {1}", interp, decim);
|
||||
|
||||
// Configure resampler
|
||||
double tapSamplerate = _symbolrate * (double)interp;
|
||||
rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount * interp, _rrcBeta, _symbolrate, tapSamplerate);
|
||||
|
@ -15,7 +15,7 @@ namespace dsp::taps {
|
||||
if (oddTapCount && !(count % 2)) { count++; }
|
||||
return windowedSinc<T>(count, (bandStop - bandStart) / 2.0, sampleRate, [=](double n, double N) {
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
return 2.0f * cosf(offsetOmega * (float)n) * window::nuttall(n, N);
|
||||
return cosf(offsetOmega * (float)n) * window::nuttall(n, N);
|
||||
}
|
||||
if constexpr (std::is_same_v<T, complex_t>) {
|
||||
// The offset is negative to flip the taps. Complex bandpass are asymetric
|
||||
|
@ -82,7 +82,7 @@ namespace dsp {
|
||||
|
||||
inline float fastAmplitude() {
|
||||
float re_abs = fabsf(re);
|
||||
float im_abs = fabsf(im);
|
||||
float im_abs = fabsf(re);
|
||||
if (re_abs > im_abs) { return re_abs + 0.4f * im_abs; }
|
||||
return im_abs + 0.4f * re_abs;
|
||||
}
|
||||
@ -125,4 +125,4 @@ namespace dsp {
|
||||
float l;
|
||||
float r;
|
||||
};
|
||||
}
|
||||
}
|
@ -574,22 +574,10 @@ void MainWindow::draw() {
|
||||
// Handle scrollwheel
|
||||
int wheel = ImGui::GetIO().MouseWheel;
|
||||
if (wheel != 0 && (gui::waterfall.mouseInFFT || gui::waterfall.mouseInWaterfall)) {
|
||||
// Select factor depending on modifier keys
|
||||
double interval;
|
||||
if (ImGui::IsKeyDown(ImGuiKey_LeftShift)) {
|
||||
interval = vfo->snapInterval * 10.0;
|
||||
}
|
||||
else if (ImGui::IsKeyDown(ImGuiKey_LeftAlt)) {
|
||||
interval = vfo->snapInterval * 0.1;
|
||||
}
|
||||
else {
|
||||
interval = vfo->snapInterval;
|
||||
}
|
||||
|
||||
double nfreq;
|
||||
if (vfo != NULL) {
|
||||
nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (interval * wheel);
|
||||
nfreq = roundl(nfreq / interval) * interval;
|
||||
nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (vfo->snapInterval * wheel);
|
||||
nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval;
|
||||
}
|
||||
else {
|
||||
nfreq = gui::waterfall.getCenterFrequency() - (gui::waterfall.getViewBandwidth() * wheel / 20.0);
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <gui/style.h>
|
||||
#include <utils/optionlist.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace displaymenu {
|
||||
bool showWaterfall;
|
||||
@ -23,10 +22,6 @@ namespace displaymenu {
|
||||
bool restartRequired = false;
|
||||
bool fftHold = false;
|
||||
int fftHoldSpeed = 60;
|
||||
bool fftSmoothing = false;
|
||||
int fftSmoothingSpeed = 100;
|
||||
bool snrSmoothing = false;
|
||||
int snrSmoothingSpeed = 20;
|
||||
|
||||
OptionList<float, float> uiScales;
|
||||
|
||||
@ -62,10 +57,8 @@ namespace displaymenu {
|
||||
IQFrontEnd::FFTWindow::NUTTALL
|
||||
};
|
||||
|
||||
void updateFFTSpeeds() {
|
||||
gui::waterfall.setFFTHoldSpeed((float)fftHoldSpeed / ((float)fftRate * 10.0f));
|
||||
gui::waterfall.setFFTSmoothingSpeed(std::min<float>((float)fftSmoothingSpeed / (float)(fftRate * 10.0f), 1.0f));
|
||||
gui::waterfall.setSNRSmoothingSpeed(std::min<float>((float)snrSmoothingSpeed / (float)(fftRate * 10.0f), 1.0f));
|
||||
void updateFFTHoldSpeed() {
|
||||
gui::waterfall.setFFTHoldSpeed(fftHoldSpeed / (fftRate * 10.0f));
|
||||
}
|
||||
|
||||
void init() {
|
||||
@ -111,13 +104,7 @@ namespace displaymenu {
|
||||
fftHold = core::configManager.conf["fftHold"];
|
||||
fftHoldSpeed = core::configManager.conf["fftHoldSpeed"];
|
||||
gui::waterfall.setFFTHold(fftHold);
|
||||
fftSmoothing = core::configManager.conf["fftSmoothing"];
|
||||
fftSmoothingSpeed = core::configManager.conf["fftSmoothingSpeed"];
|
||||
gui::waterfall.setFFTSmoothing(fftSmoothing);
|
||||
snrSmoothing = core::configManager.conf["snrSmoothing"];
|
||||
snrSmoothingSpeed = core::configManager.conf["snrSmoothingSpeed"];
|
||||
gui::waterfall.setSNRSmoothing(snrSmoothing);
|
||||
updateFFTSpeeds();
|
||||
updateFFTHoldSpeed();
|
||||
|
||||
// Define and load UI scales
|
||||
uiScales.define(1.0f, "100%", 1.0f);
|
||||
@ -157,47 +144,16 @@ namespace displaymenu {
|
||||
core::configManager.conf["fftHold"] = fftHold;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::LeftLabel("FFT Hold Speed");
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::InputInt("##sdrpp_fft_hold_speed", &fftHoldSpeed)) {
|
||||
updateFFTSpeeds();
|
||||
updateFFTHoldSpeed();
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["fftHoldSpeed"] = fftHoldSpeed;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox("FFT Smoothing##_sdrpp", &fftSmoothing)) {
|
||||
gui::waterfall.setFFTSmoothing(fftSmoothing);
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["fftSmoothing"] = fftSmoothing;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::InputInt("##sdrpp_fft_smoothing_speed", &fftSmoothingSpeed)) {
|
||||
fftSmoothingSpeed = std::max<int>(fftSmoothingSpeed, 1);
|
||||
updateFFTSpeeds();
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["fftSmoothingSpeed"] = fftSmoothingSpeed;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox("SNR Smoothing##_sdrpp", &snrSmoothing)) {
|
||||
gui::waterfall.setSNRSmoothing(snrSmoothing);
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["snrSmoothing"] = snrSmoothing;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::InputInt("##sdrpp_snr_smoothing_speed", &snrSmoothingSpeed)) {
|
||||
snrSmoothingSpeed = std::max<int>(snrSmoothingSpeed, 1);
|
||||
updateFFTSpeeds();
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["snrSmoothingSpeed"] = snrSmoothingSpeed;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
|
||||
ImGui::LeftLabel("High-DPI Scaling");
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::Combo("##sdrpp_ui_scale", &uiScaleId, uiScales.txt)) {
|
||||
@ -212,7 +168,7 @@ namespace displaymenu {
|
||||
if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) {
|
||||
fftRate = std::max<int>(1, fftRate);
|
||||
sigpath::iqFrontEnd.setFFTRate(fftRate);
|
||||
updateFFTSpeeds();
|
||||
updateFFTHoldSpeed();
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["fftRate"] = fftRate;
|
||||
core::configManager.release(true);
|
||||
@ -254,4 +210,4 @@ namespace displaymenu {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Restart required.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -15,10 +15,6 @@ namespace sourcemenu {
|
||||
bool iqCorrection = false;
|
||||
bool invertIQ = false;
|
||||
|
||||
EventHandler<std::string> sourceRegisteredHandler;
|
||||
EventHandler<std::string> sourceUnregisterHandler;
|
||||
EventHandler<std::string> sourceUnregisteredHandler;
|
||||
|
||||
std::vector<std::string> sourceNames;
|
||||
std::string sourceNamesTxt;
|
||||
std::string selectedSource;
|
||||
@ -99,10 +95,10 @@ namespace sourcemenu {
|
||||
}
|
||||
sourceId = std::distance(sourceNames.begin(), it);
|
||||
selectedSource = sourceNames[sourceId];
|
||||
sigpath::sourceManager.selectSource(sourceNames[sourceId]);
|
||||
sigpath::sourceManager.select(sourceNames[sourceId]);
|
||||
}
|
||||
|
||||
void onSourceRegistered(std::string name, void* ctx) {
|
||||
void onSourceRegistered(std::string name) {
|
||||
refreshSources();
|
||||
|
||||
if (selectedSource.empty()) {
|
||||
@ -114,13 +110,13 @@ namespace sourcemenu {
|
||||
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
|
||||
}
|
||||
|
||||
void onSourceUnregister(std::string name, void* ctx) {
|
||||
void onSourceUnregister(std::string name) {
|
||||
if (name != selectedSource) { return; }
|
||||
|
||||
// TODO: Stop everything
|
||||
}
|
||||
|
||||
void onSourceUnregistered(std::string name, void* ctx) {
|
||||
void onSourceUnregistered(std::string name) {
|
||||
refreshSources();
|
||||
|
||||
if (sourceNames.empty()) {
|
||||
@ -153,12 +149,9 @@ namespace sourcemenu {
|
||||
selectSource(selected);
|
||||
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
|
||||
|
||||
sourceRegisteredHandler.handler = onSourceRegistered;
|
||||
sourceUnregisterHandler.handler = onSourceUnregister;
|
||||
sourceUnregisteredHandler.handler = onSourceUnregistered;
|
||||
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
|
||||
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
|
||||
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
|
||||
sigpath::sourceManager.onSourceRegistered.bind(onSourceRegistered);
|
||||
sigpath::sourceManager.onSourceUnregister.bind(onSourceUnregister);
|
||||
sigpath::sourceManager.onSourceUnregistered.bind(onSourceUnregistered);
|
||||
|
||||
core::configManager.release();
|
||||
}
|
||||
@ -179,7 +172,7 @@ namespace sourcemenu {
|
||||
|
||||
if (running) { style::endDisabled(); }
|
||||
|
||||
sigpath::sourceManager.showSelectedMenu();
|
||||
sigpath::sourceManager.showMenu();
|
||||
|
||||
if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) {
|
||||
sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
|
||||
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@ -145,4 +144,4 @@ namespace SmGui {
|
||||
// Config configs
|
||||
void ForceSyncForNext();
|
||||
|
||||
}
|
||||
}
|
@ -689,7 +689,6 @@ namespace ImGui {
|
||||
|
||||
void WaterFall::onResize() {
|
||||
std::lock_guard<std::recursive_mutex> lck(latestFFTMtx);
|
||||
std::lock_guard<std::mutex> lck2(smoothingBufMtx);
|
||||
// return if widget is too small
|
||||
if (widgetSize.x < 100 || widgetSize.y < 100) {
|
||||
return;
|
||||
@ -741,23 +740,14 @@ namespace ImGui {
|
||||
}
|
||||
latestFFTHold = new float[dataWidth];
|
||||
|
||||
// Reallocate smoothing buffer
|
||||
if (fftSmoothing) {
|
||||
if (smoothingBuf) { delete[] smoothingBuf; }
|
||||
smoothingBuf = new float[dataWidth];
|
||||
for (int i = 0; i < dataWidth; i++) {
|
||||
smoothingBuf[i] = -1000.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (waterfallVisible) {
|
||||
delete[] waterfallFb;
|
||||
waterfallFb = new uint32_t[dataWidth * waterfallHeight];
|
||||
memset(waterfallFb, 0, dataWidth * waterfallHeight * sizeof(uint32_t));
|
||||
}
|
||||
for (int i = 0; i < dataWidth; i++) {
|
||||
latestFFT[i] = -1000.0f; // Hide everything
|
||||
latestFFTHold[i] = -1000.0f;
|
||||
latestFFT[i] = -1000.0; // Hide everything
|
||||
latestFFTHold[i] = -1000.0;
|
||||
}
|
||||
|
||||
fftAreaMin = ImVec2(widgetPos.x + (50.0f * style::uiScale), widgetPos.y + (9.0f * style::uiScale));
|
||||
@ -883,25 +873,9 @@ namespace ImGui {
|
||||
fftLines = 1;
|
||||
}
|
||||
|
||||
// Apply smoothing if enabled
|
||||
if (fftSmoothing && latestFFT != NULL && smoothingBuf != NULL && fftLines != 0) {
|
||||
std::lock_guard<std::mutex> lck2(smoothingBufMtx);
|
||||
volk_32f_s32f_multiply_32f(latestFFT, latestFFT, fftSmoothingAlpha, dataWidth);
|
||||
volk_32f_s32f_multiply_32f(smoothingBuf, smoothingBuf, fftSmoothingBeta, dataWidth);
|
||||
volk_32f_x2_add_32f(smoothingBuf, latestFFT, smoothingBuf, dataWidth);
|
||||
memcpy(latestFFT, smoothingBuf, dataWidth * sizeof(float));
|
||||
}
|
||||
|
||||
if (selectedVFO != "" && vfos.size() > 0) {
|
||||
float dummy;
|
||||
if (snrSmoothing) {
|
||||
float newSNR = 0.0f;
|
||||
calculateVFOSignalInfo(waterfallVisible ? &rawFFTs[currentFFTLine * rawFFTSize] : rawFFTs, vfos[selectedVFO], dummy, newSNR);
|
||||
selectedVFOSNR = (snrSmoothingBeta*selectedVFOSNR) + (snrSmoothingAlpha*newSNR);
|
||||
}
|
||||
else {
|
||||
calculateVFOSignalInfo(waterfallVisible ? &rawFFTs[currentFFTLine * rawFFTSize] : rawFFTs, vfos[selectedVFO], dummy, selectedVFOSNR);
|
||||
}
|
||||
calculateVFOSignalInfo(waterfallVisible ? &rawFFTs[currentFFTLine * rawFFTSize] : rawFFTs, vfos[selectedVFO], dummy, selectedVFOSNR);
|
||||
}
|
||||
|
||||
// If FFT hold is enabled, update it
|
||||
@ -1136,45 +1110,6 @@ namespace ImGui {
|
||||
fftHoldSpeed = speed;
|
||||
}
|
||||
|
||||
void WaterFall::setFFTSmoothing(bool enabled) {
|
||||
std::lock_guard<std::mutex> lck(smoothingBufMtx);
|
||||
fftSmoothing = enabled;
|
||||
|
||||
// Free buffer if not null
|
||||
if (smoothingBuf) {delete[] smoothingBuf; }
|
||||
|
||||
// If disabled, stop here
|
||||
if (!enabled) {
|
||||
smoothingBuf = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate and copy existing FFT into it
|
||||
smoothingBuf = new float[dataWidth];
|
||||
if (latestFFT) {
|
||||
std::lock_guard<std::recursive_mutex> lck2(latestFFTMtx);
|
||||
memcpy(smoothingBuf, latestFFT, dataWidth * sizeof(float));
|
||||
}
|
||||
else {
|
||||
memset(smoothingBuf, 0, dataWidth * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
void WaterFall::setFFTSmoothingSpeed(float speed) {
|
||||
std::lock_guard<std::mutex> lck(smoothingBufMtx);
|
||||
fftSmoothingAlpha = speed;
|
||||
fftSmoothingBeta = 1.0f - speed;
|
||||
}
|
||||
|
||||
void WaterFall::setSNRSmoothing(bool enabled) {
|
||||
snrSmoothing = enabled;
|
||||
}
|
||||
|
||||
void WaterFall::setSNRSmoothingSpeed(float speed) {
|
||||
snrSmoothingAlpha = speed;
|
||||
snrSmoothingBeta = 1.0f - speed;
|
||||
}
|
||||
|
||||
float* WaterFall::acquireLatestFFT(int& width) {
|
||||
latestFFTMtx.lock();
|
||||
if (!latestFFT) {
|
||||
|
@ -169,12 +169,6 @@ namespace ImGui {
|
||||
void setFFTHold(bool hold);
|
||||
void setFFTHoldSpeed(float speed);
|
||||
|
||||
void setFFTSmoothing(bool enabled);
|
||||
void setFFTSmoothingSpeed(float speed);
|
||||
|
||||
void setSNRSmoothing(bool enabled);
|
||||
void setSNRSmoothingSpeed(float speed);
|
||||
|
||||
float* acquireLatestFFT(int& width);
|
||||
void releaseLatestFFT();
|
||||
|
||||
@ -188,7 +182,7 @@ namespace ImGui {
|
||||
bool mouseInFFT = false;
|
||||
bool mouseInWaterfall = false;
|
||||
|
||||
float selectedVFOSNR = 0.0f;
|
||||
float selectedVFOSNR = NAN;
|
||||
|
||||
bool centerFrequencyLocked = false;
|
||||
|
||||
@ -276,7 +270,6 @@ namespace ImGui {
|
||||
std::recursive_mutex buf_mtx;
|
||||
std::recursive_mutex latestFFTMtx;
|
||||
std::mutex texMtx;
|
||||
std::mutex smoothingBufMtx;
|
||||
|
||||
float vRange;
|
||||
|
||||
@ -311,9 +304,8 @@ namespace ImGui {
|
||||
//std::vector<std::vector<float>> rawFFTs;
|
||||
int rawFFTSize;
|
||||
float* rawFFTs = NULL;
|
||||
float* latestFFT = NULL;
|
||||
float* latestFFTHold = NULL;
|
||||
float* smoothingBuf = NULL;
|
||||
float* latestFFT;
|
||||
float* latestFFTHold;
|
||||
int currentFFTLine = 0;
|
||||
int fftLines = 0;
|
||||
|
||||
@ -333,14 +325,6 @@ namespace ImGui {
|
||||
bool fftHold = false;
|
||||
float fftHoldSpeed = 0.3f;
|
||||
|
||||
bool fftSmoothing = false;
|
||||
float fftSmoothingAlpha = 0.5;
|
||||
float fftSmoothingBeta = 0.5;
|
||||
|
||||
bool snrSmoothing = false;
|
||||
float snrSmoothingAlpha = 0.5;
|
||||
float snrSmoothingBeta = 0.5;
|
||||
|
||||
// UI Select elements
|
||||
bool fftResizeSelect = false;
|
||||
bool freqScaleSelect = false;
|
||||
|
@ -33,7 +33,7 @@ ModuleManager::Module_t ModuleManager::loadModule(std::string path) {
|
||||
#else
|
||||
mod.handle = dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (mod.handle == NULL) {
|
||||
flog::error("Couldn't load {0}: {1}", path, dlerror());
|
||||
flog::error("Couldn't load {0}.", path);
|
||||
mod.handle = NULL;
|
||||
return mod;
|
||||
}
|
||||
@ -183,4 +183,4 @@ void ModuleManager::doPostInitAll() {
|
||||
flog::info("Running post-init for {0}", name);
|
||||
inst.instance->postInit();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
#include <utils/flog.h>
|
||||
|
||||
bool ModuleComManager::registerInterface(std::string moduleName, std::string name, void (*handler)(int code, void* in, void* out, void* ctx), void* ctx) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
if (interfaces.find(name) != interfaces.end()) {
|
||||
flog::error("Tried creating module interface with an existing name: {0}", name);
|
||||
return false;
|
||||
@ -16,7 +16,7 @@ bool ModuleComManager::registerInterface(std::string moduleName, std::string nam
|
||||
}
|
||||
|
||||
bool ModuleComManager::unregisterInterface(std::string name) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
if (interfaces.find(name) == interfaces.end()) {
|
||||
flog::error("Tried to erase module interface with unknown name: {0}", name);
|
||||
return false;
|
||||
@ -26,13 +26,13 @@ bool ModuleComManager::unregisterInterface(std::string name) {
|
||||
}
|
||||
|
||||
bool ModuleComManager::interfaceExists(std::string name) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
if (interfaces.find(name) == interfaces.end()) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ModuleComManager::getModuleName(std::string name) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
if (interfaces.find(name) == interfaces.end()) {
|
||||
flog::error("Tried to call unknown module interface: {0}", name);
|
||||
return "";
|
||||
@ -41,7 +41,7 @@ std::string ModuleComManager::getModuleName(std::string name) {
|
||||
}
|
||||
|
||||
bool ModuleComManager::callInterface(std::string name, int code, void* in, void* out) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
if (interfaces.find(name) == interfaces.end()) {
|
||||
flog::error("Tried to call unknown module interface: {0}", name);
|
||||
return false;
|
||||
|
@ -18,6 +18,6 @@ public:
|
||||
bool callInterface(std::string name, int code, void* in, void* out);
|
||||
|
||||
private:
|
||||
std::recursive_mutex mtx;
|
||||
std::mutex mtx;
|
||||
std::map<std::string, ModuleComInterface> interfaces;
|
||||
};
|
@ -146,7 +146,7 @@ namespace server {
|
||||
// Load sourceId from config
|
||||
sourceId = 0;
|
||||
if (sourceList.keyExists(sourceName)) { sourceId = sourceList.keyId(sourceName); }
|
||||
sigpath::sourceManager.selectSource(sourceList[sourceId]);
|
||||
sigpath::sourceManager.select(sourceList[sourceId]);
|
||||
|
||||
// TODO: Use command line option
|
||||
std::string host = (std::string)core::args["addr"];
|
||||
@ -280,8 +280,7 @@ namespace server {
|
||||
}
|
||||
}
|
||||
else if (cmd == COMMAND_START) {
|
||||
sigpath::sourceManager.start();
|
||||
running = true;
|
||||
running = sigpath::sourceManager.start();
|
||||
}
|
||||
else if (cmd == COMMAND_STOP) {
|
||||
sigpath::sourceManager.stop();
|
||||
@ -309,14 +308,14 @@ namespace server {
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Combo("##sdrpp_server_src_sel", &sourceId, sourceList.txt)) {
|
||||
sigpath::sourceManager.selectSource(sourceList[sourceId]);
|
||||
sigpath::sourceManager.select(sourceList[sourceId]);
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["source"] = sourceList.key(sourceId);
|
||||
core::configManager.release(true);
|
||||
}
|
||||
if (running) { SmGui::EndDisabled(); }
|
||||
|
||||
sigpath::sourceManager.showSelectedMenu();
|
||||
sigpath::sourceManager.showMenu();
|
||||
}
|
||||
|
||||
void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue) {
|
||||
|
@ -1,106 +1,186 @@
|
||||
#include <server.h>
|
||||
#include <signal_path/source.h>
|
||||
#include "source.h"
|
||||
#include <utils/flog.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
|
||||
SourceManager::SourceManager() {
|
||||
}
|
||||
void SourceManager::registerSource(const std::string& name, Source* source) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
void SourceManager::registerSource(std::string name, SourceHandler* handler) {
|
||||
// Check arguments
|
||||
if (source || name.empty()) {
|
||||
flog::error("Invalid argument to register source", name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that a source with that name doesn't already exist
|
||||
if (sources.find(name) != sources.end()) {
|
||||
flog::error("Tried to register new source with existing name: {0}", name);
|
||||
flog::error("Tried to register source with existing name: {}", name);
|
||||
return;
|
||||
}
|
||||
sources[name] = handler;
|
||||
onSourceRegistered.emit(name);
|
||||
|
||||
// Add source to map
|
||||
sources[name] = source;
|
||||
|
||||
// Add source to lists
|
||||
sourceNames.push_back(name);
|
||||
onSourceRegistered(name);
|
||||
}
|
||||
|
||||
void SourceManager::unregisterSource(std::string name) {
|
||||
void SourceManager::unregisterSource(const std::string& name) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Check that a source with that name exists
|
||||
if (sources.find(name) == sources.end()) {
|
||||
flog::error("Tried to unregister non existent source: {0}", name);
|
||||
flog::error("Tried to unregister a non-existent source: {}", name);
|
||||
return;
|
||||
}
|
||||
onSourceUnregister.emit(name);
|
||||
if (name == selectedName) {
|
||||
if (selectedHandler != NULL) {
|
||||
sources[selectedName]->deselectHandler(sources[selectedName]->ctx);
|
||||
}
|
||||
sigpath::iqFrontEnd.setInput(&nullSource);
|
||||
selectedHandler = NULL;
|
||||
}
|
||||
|
||||
// Notify event listeners of the imminent deletion
|
||||
onSourceUnregister(name);
|
||||
|
||||
// Delete from lists
|
||||
sourceNames.erase(std::find(sourceNames.begin(), sourceNames.end(), name));
|
||||
sources.erase(name);
|
||||
onSourceUnregistered.emit(name);
|
||||
|
||||
// Notify event listeners of the deletion
|
||||
onSourceUnregistered(name);
|
||||
}
|
||||
|
||||
std::vector<std::string> SourceManager::getSourceNames() {
|
||||
std::vector<std::string> names;
|
||||
for (auto const& [name, src] : sources) { names.push_back(name); }
|
||||
return names;
|
||||
const std::vector<std::string>& SourceManager::getSourceNames() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
return sourceNames;
|
||||
}
|
||||
|
||||
void SourceManager::selectSource(std::string name) {
|
||||
void SourceManager::select(const std::string& name) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// make sure that source isn't currently selected
|
||||
if (selectedSourceName == name) { return; }
|
||||
|
||||
// Deselect current source
|
||||
deselect();
|
||||
|
||||
// Check that a source with that name exists
|
||||
if (sources.find(name) == sources.end()) {
|
||||
flog::error("Tried to select non existent source: {0}", name);
|
||||
flog::error("Tried to select a non-existent source: {}", name);
|
||||
return;
|
||||
}
|
||||
if (selectedHandler != NULL) {
|
||||
sources[selectedName]->deselectHandler(sources[selectedName]->ctx);
|
||||
}
|
||||
selectedHandler = sources[name];
|
||||
selectedHandler->selectHandler(selectedHandler->ctx);
|
||||
selectedName = name;
|
||||
if (core::args["server"].b()) {
|
||||
server::setInput(selectedHandler->stream);
|
||||
}
|
||||
else {
|
||||
sigpath::iqFrontEnd.setInput(selectedHandler->stream);
|
||||
}
|
||||
// Set server input here
|
||||
|
||||
// Select the source
|
||||
selectedSourceName = name;
|
||||
selectedSource = sources[name];
|
||||
|
||||
// Call the selected source
|
||||
selectedSource->select();
|
||||
|
||||
// Retune to make sure the source has the latest frequency
|
||||
tune(frequency);
|
||||
}
|
||||
|
||||
void SourceManager::showSelectedMenu() {
|
||||
if (selectedHandler == NULL) {
|
||||
return;
|
||||
}
|
||||
selectedHandler->menuHandler(selectedHandler->ctx);
|
||||
const std::string& SourceManager::getSelected() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
return selectedSourceName;
|
||||
}
|
||||
|
||||
void SourceManager::start() {
|
||||
if (selectedHandler == NULL) {
|
||||
return;
|
||||
}
|
||||
selectedHandler->startHandler(selectedHandler->ctx);
|
||||
bool SourceManager::start() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Check if not already running
|
||||
if (running) { return true; }
|
||||
|
||||
// Call source if selected and save if started
|
||||
running = (!selectedSource) ? false : selectedSource->start();
|
||||
|
||||
return running;
|
||||
}
|
||||
|
||||
void SourceManager::stop() {
|
||||
if (selectedHandler == NULL) {
|
||||
return;
|
||||
}
|
||||
selectedHandler->stopHandler(selectedHandler->ctx);
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Check if running
|
||||
if (!running) { return; }
|
||||
|
||||
// Call source if selected and save state
|
||||
if (selectedSource) { selectedSource->stop(); }
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool SourceManager::isRunning() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
return running;
|
||||
}
|
||||
|
||||
void SourceManager::tune(double freq) {
|
||||
if (selectedHandler == NULL) {
|
||||
return;
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Save frequency
|
||||
frequency = freq;
|
||||
|
||||
// Call source if selected
|
||||
if (selectedSource) {
|
||||
selectedSource->tune(((mode == TUNING_MODE_NORMAL) ? freq : ifFrequency) + offset);
|
||||
}
|
||||
// TODO: No need to always retune the hardware in panadpter mode
|
||||
selectedHandler->tuneHandler(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset, selectedHandler->ctx);
|
||||
onRetune.emit(freq);
|
||||
currentFreq = freq;
|
||||
}
|
||||
|
||||
void SourceManager::showMenu() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Call source if selected
|
||||
if (selectedSource) { selectedSource->showMenu(); }
|
||||
}
|
||||
|
||||
double SourceManager::getSamplerate() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
return samplerate;
|
||||
}
|
||||
|
||||
// =========== TODO: These functions should not happen in this class ===========
|
||||
|
||||
void SourceManager::setTuningOffset(double offset) {
|
||||
tuneOffset = offset;
|
||||
tune(currentFreq);
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Update offset
|
||||
this->offset = offset;
|
||||
|
||||
// Retune to take affect
|
||||
tune(frequency);
|
||||
}
|
||||
|
||||
void SourceManager::setTuningMode(TuningMode mode) {
|
||||
tuneMode = mode;
|
||||
tune(currentFreq);
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Update mode
|
||||
this->mode = mode;
|
||||
|
||||
// Retune to take affect
|
||||
tune(frequency);
|
||||
}
|
||||
|
||||
void SourceManager::setPanadpterIF(double freq) {
|
||||
ifFreq = freq;
|
||||
tune(currentFreq);
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Update offset
|
||||
ifFrequency = freq;
|
||||
|
||||
// Return to take affect if in panadapter mode
|
||||
if (mode == TUNING_MODE_PANADAPTER) { tune(frequency); }
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
void SourceManager::deselect() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Call source if selected
|
||||
if (selectedSource) { selectedSource->deselect(); }
|
||||
|
||||
// Mark as deselected
|
||||
selectedSourceName.clear();
|
||||
selectedSource = NULL;
|
||||
}
|
||||
|
||||
void SourceManager::setSamplerate(double samplerate) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// Save samplerate and emit event
|
||||
this->samplerate = samplerate;
|
||||
onSamplerateChanged(samplerate);
|
||||
}
|
@ -1,56 +1,153 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <dsp/stream.h>
|
||||
#include <mutex>
|
||||
#include <dsp/types.h>
|
||||
#include <dsp/stream.h>
|
||||
#include <utils/event.h>
|
||||
|
||||
enum TuningMode {
|
||||
TUNING_MODE_NORMAL,
|
||||
TUNING_MODE_PANADAPTER
|
||||
};
|
||||
|
||||
class Source;
|
||||
|
||||
class SourceManager {
|
||||
friend Source;
|
||||
public:
|
||||
SourceManager();
|
||||
/**
|
||||
* Register a source.
|
||||
* @param name Name of the source.
|
||||
* @param source Pointer to the source instance.
|
||||
*/
|
||||
void registerSource(const std::string& name, Source* source);
|
||||
|
||||
struct SourceHandler {
|
||||
dsp::stream<dsp::complex_t>* stream;
|
||||
void (*menuHandler)(void* ctx);
|
||||
void (*selectHandler)(void* ctx);
|
||||
void (*deselectHandler)(void* ctx);
|
||||
void (*startHandler)(void* ctx);
|
||||
void (*stopHandler)(void* ctx);
|
||||
void (*tuneHandler)(double freq, void* ctx);
|
||||
void* ctx;
|
||||
};
|
||||
/**
|
||||
* Unregister a source.
|
||||
* @param name Name of the source.
|
||||
*/
|
||||
void unregisterSource(const std::string& name);
|
||||
|
||||
enum TuningMode {
|
||||
NORMAL,
|
||||
PANADAPTER
|
||||
};
|
||||
/**
|
||||
* Get a list of source names.
|
||||
* @return List of source names.
|
||||
*/
|
||||
const std::vector<std::string>& getSourceNames();
|
||||
|
||||
void registerSource(std::string name, SourceHandler* handler);
|
||||
void unregisterSource(std::string name);
|
||||
void selectSource(std::string name);
|
||||
void showSelectedMenu();
|
||||
void start();
|
||||
/**
|
||||
* Select a source.
|
||||
* @param name Name of the source.
|
||||
*/
|
||||
void select(const std::string& name);
|
||||
|
||||
/**
|
||||
* Get the name of the currently selected source.
|
||||
* @return Name of the source or empty if no source is selected.
|
||||
*/
|
||||
const std::string& getSelected();
|
||||
|
||||
/**
|
||||
* Start the radio.
|
||||
* @return True if the radio started successfully, false if not.
|
||||
*/
|
||||
bool start();
|
||||
|
||||
/**
|
||||
* Stop the radio.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Check if the radio is running.
|
||||
* @return True if the radio is running, false if not.
|
||||
*/
|
||||
bool isRunning();
|
||||
|
||||
/**
|
||||
* Tune the radio.
|
||||
* @param freq Frequency in Hz.
|
||||
*/
|
||||
void tune(double freq);
|
||||
|
||||
/**
|
||||
* Tune the radio.
|
||||
* @param freq Frequency to tune the radio to.
|
||||
*/
|
||||
void showMenu();
|
||||
|
||||
/**
|
||||
* Get the current samplerate of the radio.
|
||||
* @return Samplerate in Hz.
|
||||
*/
|
||||
double getSamplerate();
|
||||
|
||||
// =========== TODO: These functions should not happen in this class ===========
|
||||
|
||||
/**
|
||||
* Set offset to add to the tuned frequency.
|
||||
* @param offset Offset in Hz.
|
||||
*/
|
||||
void setTuningOffset(double offset);
|
||||
|
||||
/**
|
||||
* Set tuning mode.
|
||||
* @param mode Tuning mode.
|
||||
*/
|
||||
void setTuningMode(TuningMode mode);
|
||||
|
||||
/**
|
||||
* Set panadapter mode IF frequency.
|
||||
* @param freq IF frequency in Hz.
|
||||
*/
|
||||
void setPanadpterIF(double freq);
|
||||
|
||||
std::vector<std::string> getSourceNames();
|
||||
// =============================================================================
|
||||
|
||||
// Emitted after a new source has been registered.
|
||||
Event<std::string> onSourceRegistered;
|
||||
|
||||
// Emitted when a source is about to be unregistered.
|
||||
Event<std::string> onSourceUnregister;
|
||||
|
||||
// Emitted after a source has been unregistered.
|
||||
Event<std::string> onSourceUnregistered;
|
||||
|
||||
// Emitted when the samplerate of the incoming IQ has changed.
|
||||
Event<double> onSamplerateChanged;
|
||||
|
||||
// Emitted when the source manager is instructed to tune the radio.
|
||||
Event<double> onRetune;
|
||||
|
||||
private:
|
||||
std::map<std::string, SourceHandler*> sources;
|
||||
std::string selectedName;
|
||||
SourceHandler* selectedHandler = NULL;
|
||||
double tuneOffset;
|
||||
double currentFreq;
|
||||
double ifFreq = 0.0;
|
||||
TuningMode tuneMode = TuningMode::NORMAL;
|
||||
dsp::stream<dsp::complex_t> nullSource;
|
||||
void deselect();
|
||||
void setSamplerate(double samplerate);
|
||||
|
||||
std::vector<std::string> sourceNames;
|
||||
std::map<std::string, Source*> sources;
|
||||
|
||||
std::string selectedSourceName = "";
|
||||
Source* selectedSource = NULL;
|
||||
|
||||
bool running = false;
|
||||
double samplerate = 1e6;
|
||||
double frequency = 100e6;
|
||||
double offset = 0;
|
||||
double ifFrequency = 8.830e6;
|
||||
TuningMode mode = TUNING_MODE_NORMAL;
|
||||
|
||||
std::recursive_mutex mtx;
|
||||
};
|
||||
|
||||
class Source {
|
||||
public:
|
||||
virtual void showMenu() {}
|
||||
virtual void select() = 0;
|
||||
virtual void deselect() {}
|
||||
virtual bool start() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual void tune(double freq) {}
|
||||
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
};
|
@ -1,43 +1,51 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <utils/flog.h>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
|
||||
template <class T>
|
||||
struct EventHandler {
|
||||
EventHandler() {}
|
||||
EventHandler(void (*handler)(T, void*), void* ctx) {
|
||||
this->handler = handler;
|
||||
this->ctx = ctx;
|
||||
}
|
||||
typedef int HandlerID;
|
||||
|
||||
void (*handler)(T, void*);
|
||||
void* ctx;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
template <typename... Args>
|
||||
class Event {
|
||||
using Handler = std::function<void(Args...)>;
|
||||
public:
|
||||
Event() {}
|
||||
~Event() {}
|
||||
|
||||
void emit(T value) {
|
||||
for (auto const& handler : handlers) {
|
||||
handler->handler(value, handler->ctx);
|
||||
}
|
||||
HandlerID bind(Handler handler) {
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
HandlerID id = genID();
|
||||
handlers[id] = handler;
|
||||
return id;
|
||||
}
|
||||
|
||||
void bindHandler(EventHandler<T>* handler) {
|
||||
handlers.push_back(handler);
|
||||
template<typename MHandler, class T>
|
||||
HandlerID bind(MHandler handler, T* ctx) {
|
||||
return bind([=](Args... args){
|
||||
(ctx->*handler)(args...);
|
||||
});
|
||||
}
|
||||
|
||||
void unbindHandler(EventHandler<T>* handler) {
|
||||
if (std::find(handlers.begin(), handlers.end(), handler) == handlers.end()) {
|
||||
flog::error("Tried to remove a non-existent event handler");
|
||||
return;
|
||||
void unbind(HandlerID id) {
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
if (handlers.find(id) == handlers.end()) {
|
||||
throw std::runtime_error("Could not unbind handler, unknown ID");
|
||||
}
|
||||
handlers.erase(id);
|
||||
}
|
||||
|
||||
void operator()(Args... args) {
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
for (const auto& [desc, handler] : handlers) {
|
||||
handler(args...);
|
||||
}
|
||||
handlers.erase(std::remove(handlers.begin(), handlers.end(), handler), handlers.end());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<EventHandler<T>*> handlers;
|
||||
HandlerID genID() {
|
||||
int id;
|
||||
for (id = 1; handlers.find(id) != handlers.end(); id++);
|
||||
return id;
|
||||
}
|
||||
|
||||
std::map<HandlerID, Handler> handlers;
|
||||
std::mutex mtx;
|
||||
};
|
@ -169,7 +169,7 @@ namespace flog {
|
||||
fprintf(outStream, "] %s\n", out.c_str());
|
||||
#elif defined(__ANDROID__)
|
||||
// Print format string
|
||||
__android_log_print(TYPE_PRIORITIES[type], FLOG_ANDROID_TAG, COLOR_WHITE "[%02d/%02d/%02d %02d:%02d:%02d.%03d] [%s%s" COLOR_WHITE "] %s\n",
|
||||
__android_log_buf_print(LOG_ID_DEFAULT, TYPE_PRIORITIES[type], FLOG_ANDROID_TAG, COLOR_WHITE "[%02d/%02d/%02d %02d:%02d:%02d.%03d] [%s%s" COLOR_WHITE "] %s\n",
|
||||
nowc->tm_mday, nowc->tm_mon + 1, nowc->tm_year + 1900, nowc->tm_hour, nowc->tm_min, nowc->tm_sec, 0, TYPE_COLORS[type], TYPE_STR[type], out.c_str());
|
||||
#else
|
||||
// Print format string
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "net.h"
|
||||
#include <string.h>
|
||||
#include <codecvt>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WOULD_BLOCK (WSAGetLastError() == WSAEWOULDBLOCK)
|
||||
@ -289,7 +288,6 @@ namespace net {
|
||||
|
||||
// Save data
|
||||
for (auto iface = addresses; iface; iface = iface->ifa_next) {
|
||||
if (!iface->ifa_addr || !iface->ifa_netmask) { continue; }
|
||||
if (iface->ifa_addr->sa_family != AF_INET) { continue; }
|
||||
InterfaceInfo info;
|
||||
info.address = ntohl(*(uint32_t*)&iface->ifa_addr->sa_data[2]);
|
||||
@ -404,4 +402,4 @@ namespace net {
|
||||
std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost, int lport) {
|
||||
return openudp(Address(rhost, rport), Address(lhost, lport));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <utils/networking.h>
|
||||
#include <assert.h>
|
||||
#include <utils/flog.h>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace net {
|
||||
|
||||
|
@ -1,52 +0,0 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
|
||||
typedef int HandlerID;
|
||||
|
||||
template <typename... Args>
|
||||
class NewEvent {
|
||||
public:
|
||||
using Handler = std::function<void(Args...)>;
|
||||
|
||||
HandlerID bind(const Handler& handler) {
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
HandlerID id = genID();
|
||||
handlers[id] = handler;
|
||||
return id;
|
||||
}
|
||||
|
||||
template<typename MHandler, class T>
|
||||
HandlerID bind(MHandler handler, T* ctx) {
|
||||
return bind([=](Args... args){
|
||||
(ctx->*handler)(args...);
|
||||
});
|
||||
}
|
||||
|
||||
void unbind(HandlerID id) {
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
if (handlers.find(id) == handlers.end()) {
|
||||
throw std::runtime_error("Could not unbind handler, unknown ID");
|
||||
}
|
||||
handlers.erase(id);
|
||||
}
|
||||
|
||||
void operator()(Args... args) {
|
||||
std::lock_guard<std::mutex> lck(mtx);
|
||||
for (const auto& [desc, handler] : handlers) {
|
||||
handler(args...);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
HandlerID genID() {
|
||||
int id;
|
||||
for (id = 1; handlers.find(id) != handlers.end(); id++);
|
||||
return id;
|
||||
}
|
||||
|
||||
std::map<HandlerID, Handler> handlers;
|
||||
std::mutex mtx;
|
||||
};
|
@ -8,7 +8,7 @@ class OptionList {
|
||||
public:
|
||||
OptionList() { updateText(); }
|
||||
|
||||
void define(const K& key, const std::string& name, const T& value) {
|
||||
void define(K key, std::string name, T value) {
|
||||
if (keyExists(key)) { throw std::runtime_error("Key already exists"); }
|
||||
if (nameExists(name)) { throw std::runtime_error("Name already exists"); }
|
||||
if (valueExists(value)) { throw std::runtime_error("Value already exists"); }
|
||||
@ -18,27 +18,27 @@ public:
|
||||
updateText();
|
||||
}
|
||||
|
||||
void define(const std::string& name, const T& value) {
|
||||
void define(std::string name, T value) {
|
||||
define(name, name, value);
|
||||
}
|
||||
|
||||
void undefine(int id) {
|
||||
void undefined(int id) {
|
||||
keys.erase(keys.begin() + id);
|
||||
names.erase(names.begin() + id);
|
||||
values.erase(values.begin() + id);
|
||||
updateText();
|
||||
}
|
||||
|
||||
void undefineKey(const K& key) {
|
||||
undefine(keyId(key));
|
||||
void undefineKey(K key) {
|
||||
undefined(keyId(key));
|
||||
}
|
||||
|
||||
void undefineName(const std::string& name) {
|
||||
undefine(nameId(name));
|
||||
void undefineName(std::string name) {
|
||||
undefined(nameId(name));
|
||||
}
|
||||
|
||||
void undefineValue(const T& value) {
|
||||
undefine(valueId(value));
|
||||
void undefineValue(T value) {
|
||||
undefined(valueId(value));
|
||||
}
|
||||
|
||||
void clear() {
|
||||
@ -48,61 +48,61 @@ public:
|
||||
updateText();
|
||||
}
|
||||
|
||||
int size() const {
|
||||
int size() {
|
||||
return keys.size();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
bool empty() {
|
||||
return keys.empty();
|
||||
}
|
||||
|
||||
bool keyExists(const K& key) const {
|
||||
bool keyExists(K key) {
|
||||
if (std::find(keys.begin(), keys.end(), key) != keys.end()) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nameExists(const std::string& name) const {
|
||||
bool nameExists(std::string name) {
|
||||
if (std::find(names.begin(), names.end(), name) != names.end()) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
bool valueExists(const T& value) const {
|
||||
bool valueExists(T value) {
|
||||
if (std::find(values.begin(), values.end(), value) != values.end()) { return true; }
|
||||
return false;
|
||||
}
|
||||
|
||||
int keyId(const K& key) const {
|
||||
int keyId(K key) {
|
||||
auto it = std::find(keys.begin(), keys.end(), key);
|
||||
if (it == keys.end()) { throw std::runtime_error("Key doesn't exists"); }
|
||||
return std::distance(keys.begin(), it);
|
||||
}
|
||||
|
||||
int nameId(const std::string& name) const {
|
||||
int nameId(std::string name) {
|
||||
auto it = std::find(names.begin(), names.end(), name);
|
||||
if (it == names.end()) { throw std::runtime_error("Name doesn't exists"); }
|
||||
return std::distance(names.begin(), it);
|
||||
}
|
||||
|
||||
int valueId(const T& value) const {
|
||||
int valueId(T value) {
|
||||
auto it = std::find(values.begin(), values.end(), value);
|
||||
if (it == values.end()) { throw std::runtime_error("Value doesn't exists"); }
|
||||
return std::distance(values.begin(), it);
|
||||
}
|
||||
|
||||
inline const K& key(int id) const {
|
||||
K key(int id) {
|
||||
return keys[id];
|
||||
}
|
||||
|
||||
inline const std::string& name(int id) const {
|
||||
std::string name(int id) {
|
||||
return names[id];
|
||||
}
|
||||
|
||||
inline const T& value(int id) const {
|
||||
T value(int id) {
|
||||
return values[id];
|
||||
}
|
||||
|
||||
inline const T& operator[](int& id) const {
|
||||
return values[id];
|
||||
T operator[](int& id) {
|
||||
return value(id);
|
||||
}
|
||||
|
||||
const char* txt = NULL;
|
||||
|
@ -91,9 +91,9 @@ namespace riff {
|
||||
file.write((char*)&desc.hdr.size, sizeof(desc.hdr.size));
|
||||
file.seekp(pos);
|
||||
|
||||
// If parent chunk, increment its size by the size of the sub-chunk plus the size of its header)
|
||||
// If parent chunk, increment its size
|
||||
if (!chunks.empty()) {
|
||||
chunks.top().hdr.size += desc.hdr.size + sizeof(ChunkHeader);
|
||||
chunks.top().hdr.size += desc.hdr.size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,10 +214,10 @@ private:
|
||||
|
||||
if (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) {
|
||||
if (_this->showLines) {
|
||||
_this->diag.lines.push_back(-1.0);
|
||||
_this->diag.lines.push_back(-1.0/3.0);
|
||||
_this->diag.lines.push_back(1.0/3.0);
|
||||
_this->diag.lines.push_back(1.0);
|
||||
_this->diag.lines.push_back(-0.75f);
|
||||
_this->diag.lines.push_back(-0.25f);
|
||||
_this->diag.lines.push_back(0.25f);
|
||||
_this->diag.lines.push_back(0.75f);
|
||||
}
|
||||
else {
|
||||
_this->diag.lines.clear();
|
||||
|
@ -57,13 +57,10 @@ public:
|
||||
if (config.conf[name].contains("brokenModulation")) {
|
||||
brokenModulation = config.conf[name]["brokenModulation"];
|
||||
}
|
||||
if (config.conf[name].contains("oqpsk")) {
|
||||
oqpsk = config.conf[name]["oqpsk"];
|
||||
}
|
||||
config.release();
|
||||
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, true);
|
||||
demod.init(vfo->output, 72000.0f, INPUT_SAMPLE_RATE, 33, 0.6f, 0.1f, 0.005f, brokenModulation, oqpsk, 1e-6, 0.01);
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 150000, INPUT_SAMPLE_RATE, 150000, 150000, true);
|
||||
demod.init(vfo->output, 72000.0f, INPUT_SAMPLE_RATE, 33, 0.6f, 0.1f, 0.005f, brokenModulation, 1e-6, 0.01);
|
||||
split.init(&demod.out);
|
||||
split.bindStream(&symSinkStream);
|
||||
split.bindStream(&sinkStream);
|
||||
@ -102,7 +99,6 @@ public:
|
||||
double bw = gui::waterfall.getBandwidth();
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), 150000, INPUT_SAMPLE_RATE, 150000, 150000, true);
|
||||
|
||||
demod.setBrokenModulation(brokenModulation);
|
||||
demod.setInput(vfo->output);
|
||||
|
||||
demod.start();
|
||||
@ -155,13 +151,6 @@ private:
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
if (ImGui::Checkbox(CONCAT("OQPSK##oqpsk", _this->name), &_this->oqpsk)) {
|
||||
_this->demod.setOQPSK(_this->oqpsk);
|
||||
config.acquire();
|
||||
config.conf[_this->name]["oqpsk"] = _this->oqpsk;
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
if (!_this->folderSelect.pathIsValid() && _this->enabled) { style::beginDisabled(); }
|
||||
|
||||
if (_this->recording) {
|
||||
@ -256,7 +245,7 @@ private:
|
||||
uint64_t dataWritten = 0;
|
||||
std::ofstream recFile;
|
||||
bool brokenModulation = false;
|
||||
bool oqpsk = false;
|
||||
|
||||
int8_t* writeBuffer;
|
||||
};
|
||||
|
||||
|
@ -11,8 +11,8 @@ namespace dsp::demod {
|
||||
public:
|
||||
Meteor() {}
|
||||
|
||||
Meteor(stream<complex_t>* in, double symbolrate, double samplerate, int rrcTapCount, double rrcBeta, double agcRate, double costasBandwidth, bool brokenModulation, bool oqpsk, double omegaGain, double muGain, double omegaRelLimit = 0.01) {
|
||||
init(in, symbolrate, samplerate, rrcTapCount, rrcBeta, agcRate, costasBandwidth, brokenModulation, oqpsk, omegaGain, muGain);
|
||||
Meteor(stream<complex_t>* in, double symbolrate, double samplerate, int rrcTapCount, double rrcBeta, double agcRate, double costasBandwidth, bool brokenModulation, double omegaGain, double muGain, double omegaRelLimit = 0.01) {
|
||||
init(in, symbolrate, samplerate, rrcTapCount, rrcBeta, agcRate, costasBandwidth, brokenModulation, omegaGain, muGain);
|
||||
}
|
||||
|
||||
~Meteor() {
|
||||
@ -21,12 +21,11 @@ namespace dsp::demod {
|
||||
taps::free(rrcTaps);
|
||||
}
|
||||
|
||||
void init(stream<complex_t>* in, double symbolrate, double samplerate, int rrcTapCount, double rrcBeta, double agcRate, double costasBandwidth, bool brokenModulation, bool oqpsk, double omegaGain, double muGain, double omegaRelLimit = 0.01) {
|
||||
void init(stream<complex_t>* in, double symbolrate, double samplerate, int rrcTapCount, double rrcBeta, double agcRate, double costasBandwidth, bool brokenModulation, double omegaGain, double muGain, double omegaRelLimit = 0.01) {
|
||||
_symbolrate = symbolrate;
|
||||
_samplerate = samplerate;
|
||||
_rrcTapCount = rrcTapCount;
|
||||
_rrcBeta = rrcBeta;
|
||||
_oqpsk = oqpsk;
|
||||
|
||||
rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount, _rrcBeta, _symbolrate, _samplerate);
|
||||
rrc.init(NULL, rrcTaps);
|
||||
@ -130,12 +129,6 @@ namespace dsp::demod {
|
||||
costas.setBrokenModulation(enabled);
|
||||
}
|
||||
|
||||
void setOQPSK(bool enabled) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
_oqpsk = enabled;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
@ -151,18 +144,6 @@ namespace dsp::demod {
|
||||
rrc.process(count, in, out);
|
||||
agc.process(count, out, out);
|
||||
costas.process(count, out, out);
|
||||
|
||||
if (_oqpsk) {
|
||||
// Single sample delay + deinterleave
|
||||
for (int i = 0; i < count; i++) {
|
||||
float tmp = out[i].im;
|
||||
out[i].im = lastI;
|
||||
lastI = tmp;
|
||||
}
|
||||
|
||||
// TODO: Additional 1/24th sample delay
|
||||
}
|
||||
|
||||
return recov.process(count, out, out);
|
||||
}
|
||||
|
||||
@ -185,8 +166,6 @@ namespace dsp::demod {
|
||||
double _samplerate;
|
||||
int _rrcTapCount;
|
||||
double _rrcBeta;
|
||||
float lastI = 0.0f;
|
||||
bool _oqpsk = false;
|
||||
|
||||
tap<float> rrcTaps;
|
||||
filter::FIR<complex_t, float> rrc;
|
||||
|
@ -19,17 +19,15 @@ namespace demod {
|
||||
|
||||
// Load config
|
||||
_config->acquire();
|
||||
bool modified = false;
|
||||
if (config->conf[name][getName()].contains("lowPass")) {
|
||||
_lowPass = config->conf[name][getName()]["lowPass"];
|
||||
}
|
||||
if (config->conf[name][getName()].contains("highPass")) {
|
||||
_highPass = config->conf[name][getName()]["highPass"];
|
||||
}
|
||||
_config->release();
|
||||
_config->release(modified);
|
||||
|
||||
|
||||
// Define structure
|
||||
demod.init(input, getIFSampleRate(), bandwidth, _lowPass, _highPass);
|
||||
demod.init(input, getIFSampleRate(), bandwidth, _lowPass);
|
||||
}
|
||||
|
||||
void start() { demod.start(); }
|
||||
@ -43,12 +41,6 @@ namespace demod {
|
||||
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
||||
_config->release(true);
|
||||
}
|
||||
if (ImGui::Checkbox(("High Pass##_radio_wfm_highpass_" + name).c_str(), &_highPass)) {
|
||||
demod.setHighPass(_highPass);
|
||||
_config->acquire();
|
||||
_config->conf[name][getName()]["highPass"] = _highPass;
|
||||
_config->release(true);
|
||||
}
|
||||
}
|
||||
|
||||
void setBandwidth(double bandwidth) {
|
||||
@ -83,7 +75,6 @@ namespace demod {
|
||||
ConfigManager* _config = NULL;
|
||||
|
||||
bool _lowPass = true;
|
||||
bool _highPass = false;
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
@ -1,4 +0,0 @@
|
||||
FROM debian:bookworm
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
COPY do_build.sh /root
|
||||
RUN chmod +x /root/do_build.sh
|
@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cd /root
|
||||
|
||||
# Install dependencies and tools
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.07.1
|
||||
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev, libzstd-dev'
|
@ -6,7 +6,7 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
libcodec2-dev
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
@ -15,20 +15,10 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -6,7 +6,7 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
libcodec2-dev
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
@ -15,20 +15,10 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -4,9 +4,9 @@ cd /root
|
||||
|
||||
# Install dependencies and tools
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
libcodec2-dev
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
@ -15,21 +15,11 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev, libzstd-dev'
|
@ -12,7 +12,7 @@ apt update
|
||||
# Install dependencies and tools
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libsoapysdr-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev libudev-dev autoconf libtool xxd
|
||||
libcodec2-dev libudev-dev
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
@ -41,16 +41,6 @@ make install
|
||||
ldconfig
|
||||
cd ../../
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Fix missing .pc file for codec2
|
||||
echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc
|
||||
echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc
|
||||
@ -66,7 +56,7 @@ echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
# Generate package
|
||||
|
@ -6,7 +6,7 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
libcodec2-dev
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
@ -15,20 +15,10 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -6,7 +6,7 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
libcodec2-dev
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
@ -15,20 +15,10 @@ wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
|
||||
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -81,7 +81,7 @@ bundle_find_full_path() {
|
||||
|
||||
# Correct dep path
|
||||
echo $RPATH/$RPATH_NEXT
|
||||
return -1
|
||||
return
|
||||
done
|
||||
|
||||
# Search other common paths
|
||||
|
@ -38,7 +38,6 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfspace_source/rfspace_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_sdr_source/rtl_sdr_source.dylib
|
||||
@ -63,7 +62,6 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_module
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/discord_integration/discord_integration.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/frequency_manager/frequency_manager.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/recorder/recorder.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/rigctl_client/rigctl_client.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/rigctl_server/rigctl_server.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/misc_modules/scanner/scanner.dylib
|
||||
|
||||
|
@ -32,9 +32,6 @@ cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windo
|
||||
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/
|
||||
|
@ -5,5 +5,4 @@ file(GLOB SRC "src/*.cpp")
|
||||
|
||||
include(${SDRPP_MODULE_CMAKE})
|
||||
|
||||
target_include_directories(recorder PRIVATE "src/")
|
||||
target_include_directories(recorder PRIVATE "../../decoder_modules/radio/src")
|
||||
target_include_directories(recorder PRIVATE "src/")
|
@ -21,11 +21,9 @@
|
||||
#include <core.h>
|
||||
#include <utils/optionlist.h>
|
||||
#include <utils/wav.h>
|
||||
#include <radio_interface.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
#define SILENCE_LVL 10e-6
|
||||
#define SILENCE_LVL 10e-20
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "recorder",
|
||||
@ -169,7 +167,7 @@ public:
|
||||
|
||||
// Open file
|
||||
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
||||
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
|
||||
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? gui::waterfall.selectedVFO : "";
|
||||
std::string extension = ".wav";
|
||||
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
|
||||
if (!writer.open(expandedPath)) {
|
||||
@ -317,11 +315,11 @@ private:
|
||||
}
|
||||
if (_this->recording) { style::endDisabled(); }
|
||||
|
||||
if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ignore_silence_", _this->name), &_this->ignoreSilence)) {
|
||||
config.acquire();
|
||||
config.conf[_this->name]["ignoreSilence"] = _this->ignoreSilence;
|
||||
config.release(true);
|
||||
}
|
||||
// if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ignore_silence_", _this->name), &_this->ignoreSilence)) {
|
||||
// config.acquire();
|
||||
// config.conf[_this->name]["ignoreSilence"] = _this->ignoreSilence;
|
||||
// config.release(true);
|
||||
// }
|
||||
}
|
||||
|
||||
// Record button
|
||||
@ -340,13 +338,7 @@ private:
|
||||
uint64_t seconds = _this->writer.getSamplesWritten() / _this->samplerate;
|
||||
time_t diff = seconds;
|
||||
tm* dtm = gmtime(&diff);
|
||||
|
||||
if (_this->ignoreSilence && _this->ignoringSilence) {
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Paused %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
|
||||
}
|
||||
else {
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
|
||||
}
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,17 +430,6 @@ private:
|
||||
if (dbLvl.r > lvl.r) { lvl.r = dbLvl.r; }
|
||||
}
|
||||
|
||||
std::map<int, const char*> radioModeToString = {
|
||||
{ RADIO_IFACE_MODE_NFM, "NFM" },
|
||||
{ RADIO_IFACE_MODE_WFM, "WFM" },
|
||||
{ RADIO_IFACE_MODE_AM, "AM" },
|
||||
{ RADIO_IFACE_MODE_DSB, "DSB" },
|
||||
{ RADIO_IFACE_MODE_USB, "USB" },
|
||||
{ RADIO_IFACE_MODE_CW, "CW" },
|
||||
{ RADIO_IFACE_MODE_LSB, "LSB" },
|
||||
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
||||
};
|
||||
|
||||
std::string genFileName(std::string templ, std::string type, std::string name) {
|
||||
// Get data
|
||||
time_t now = time(0);
|
||||
@ -467,7 +448,6 @@ private:
|
||||
char dayStr[128];
|
||||
char monStr[128];
|
||||
char yearStr[128];
|
||||
const char* modeStr = "Unknown";
|
||||
sprintf(freqStr, "%.0lfHz", freq);
|
||||
sprintf(hourStr, "%02d", ltm->tm_hour);
|
||||
sprintf(minStr, "%02d", ltm->tm_min);
|
||||
@ -475,11 +455,6 @@ private:
|
||||
sprintf(dayStr, "%02d", ltm->tm_mday);
|
||||
sprintf(monStr, "%02d", ltm->tm_mon + 1);
|
||||
sprintf(yearStr, "%02d", ltm->tm_year + 1900);
|
||||
if (core::modComManager.getModuleName(name) == "radio") {
|
||||
int mode;
|
||||
core::modComManager.callInterface(name, RADIO_IFACE_CMD_GET_MODE, NULL, &mode);
|
||||
modeStr = radioModeToString[mode];
|
||||
}
|
||||
|
||||
// Replace in template
|
||||
templ = std::regex_replace(templ, std::regex("\\$t"), type);
|
||||
@ -490,7 +465,6 @@ private:
|
||||
templ = std::regex_replace(templ, std::regex("\\$d"), dayStr);
|
||||
templ = std::regex_replace(templ, std::regex("\\$M"), monStr);
|
||||
templ = std::regex_replace(templ, std::regex("\\$y"), yearStr);
|
||||
templ = std::regex_replace(templ, std::regex("\\$r"), modeStr);
|
||||
return templ;
|
||||
}
|
||||
|
||||
@ -506,31 +480,13 @@ private:
|
||||
|
||||
static void stereoHandler(dsp::stereo_t* data, int count, void* ctx) {
|
||||
RecorderModule* _this = (RecorderModule*)ctx;
|
||||
if (_this->ignoreSilence) {
|
||||
float absMax = 0.0f;
|
||||
float* _data = (float*)data;
|
||||
int _count = count * 2;
|
||||
for (int i = 0; i < _count; i++) {
|
||||
float val = fabsf(_data[i]);
|
||||
if (val > absMax) { absMax = val; }
|
||||
}
|
||||
_this->ignoringSilence = (absMax < SILENCE_LVL);
|
||||
if (_this->ignoringSilence) { return; }
|
||||
}
|
||||
// TODO: Ignore silence
|
||||
_this->writer.write((float*)data, count);
|
||||
}
|
||||
|
||||
static void monoHandler(float* data, int count, void* ctx) {
|
||||
RecorderModule* _this = (RecorderModule*)ctx;
|
||||
if (_this->ignoreSilence) {
|
||||
float absMax = 0.0f;
|
||||
for (int i = 0; i < count; i++) {
|
||||
float val = fabsf(data[i]);
|
||||
if (val > absMax) { absMax = val; }
|
||||
}
|
||||
_this->ignoringSilence = (absMax < SILENCE_LVL);
|
||||
if (_this->ignoringSilence) { return; }
|
||||
}
|
||||
// TODO: Ignore silence
|
||||
_this->writer.write(data, count);
|
||||
}
|
||||
|
||||
@ -573,7 +529,6 @@ private:
|
||||
dsp::stereo_t audioLvl = { -100.0f, -100.0f };
|
||||
|
||||
bool recording = false;
|
||||
bool ignoringSilence = false;
|
||||
wav::Writer writer;
|
||||
std::recursive_mutex recMtx;
|
||||
dsp::stream<dsp::complex_t>* basebandStream;
|
||||
|
@ -45,9 +45,6 @@ public:
|
||||
}
|
||||
config.release();
|
||||
|
||||
_retuneHandler.ctx = this;
|
||||
_retuneHandler.handler = retuneHandler;
|
||||
|
||||
gui::menu.registerEntry(name, menuHandler, this, NULL);
|
||||
}
|
||||
|
||||
@ -87,8 +84,8 @@ public:
|
||||
|
||||
// Switch source to panadapter mode
|
||||
sigpath::sourceManager.setPanadpterIF(ifFreq);
|
||||
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER);
|
||||
sigpath::sourceManager.onRetune.bindHandler(&_retuneHandler);
|
||||
sigpath::sourceManager.setTuningMode(TUNING_MODE_PANADAPTER);
|
||||
retuneHandlerId = sigpath::sourceManager.onRetune.bind(retuneHandler, this);
|
||||
|
||||
running = true;
|
||||
}
|
||||
@ -98,8 +95,8 @@ public:
|
||||
if (!running) { return; }
|
||||
|
||||
// Switch source back to normal mode
|
||||
sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler);
|
||||
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL);
|
||||
sigpath::sourceManager.onRetune.unbind(retuneHandlerId);
|
||||
sigpath::sourceManager.setTuningMode(TUNING_MODE_NORMAL);
|
||||
|
||||
// Disconnect from rigctl server
|
||||
client->close();
|
||||
@ -159,10 +156,9 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
static void retuneHandler(double freq, void* ctx) {
|
||||
RigctlClientModule* _this = (RigctlClientModule*)ctx;
|
||||
if (!_this->client || !_this->client->isOpen()) { return; }
|
||||
if (_this->client->setFreq(freq)) {
|
||||
void retuneHandler(double freq) {
|
||||
if (!client || !client->isOpen()) { return; }
|
||||
if (client->setFreq(freq)) {
|
||||
flog::error("Could not set frequency");
|
||||
}
|
||||
}
|
||||
@ -178,7 +174,7 @@ private:
|
||||
|
||||
double ifFreq = 8830000.0;
|
||||
|
||||
EventHandler<double> _retuneHandler;
|
||||
HandlerID retuneHandlerId;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
|
@ -333,17 +333,6 @@ private:
|
||||
_this->client->readAsync(1024, _this->dataBuf, dataHandler, _this, false);
|
||||
}
|
||||
|
||||
std::map<int, const char*> radioModeToString = {
|
||||
{ RADIO_IFACE_MODE_NFM, "NFM" },
|
||||
{ RADIO_IFACE_MODE_WFM, "WFM" },
|
||||
{ RADIO_IFACE_MODE_AM, "AM" },
|
||||
{ RADIO_IFACE_MODE_DSB, "DSB" },
|
||||
{ RADIO_IFACE_MODE_USB, "USB" },
|
||||
{ RADIO_IFACE_MODE_CW, "CW" },
|
||||
{ RADIO_IFACE_MODE_LSB, "LSB" },
|
||||
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
||||
};
|
||||
|
||||
void commandHandler(std::string cmd) {
|
||||
std::string corr = "";
|
||||
std::vector<std::string> parts;
|
||||
@ -453,18 +442,38 @@ private:
|
||||
pos++;
|
||||
}
|
||||
|
||||
const std::string& newModeStr = parts[1];
|
||||
float newBandwidth = std::atoi(parts[2].c_str());
|
||||
|
||||
auto it = std::find_if(radioModeToString.begin(), radioModeToString.end(), [&newModeStr](const auto& e) {
|
||||
return e.second == newModeStr;
|
||||
});
|
||||
if (it == radioModeToString.end()) {
|
||||
|
||||
int newMode;
|
||||
if (parts[1] == "FM") {
|
||||
newMode = RADIO_IFACE_MODE_NFM;
|
||||
}
|
||||
else if (parts[1] == "WFM") {
|
||||
newMode = RADIO_IFACE_MODE_WFM;
|
||||
}
|
||||
else if (parts[1] == "AM") {
|
||||
newMode = RADIO_IFACE_MODE_AM;
|
||||
}
|
||||
else if (parts[1] == "DSB") {
|
||||
newMode = RADIO_IFACE_MODE_DSB;
|
||||
}
|
||||
else if (parts[1] == "USB") {
|
||||
newMode = RADIO_IFACE_MODE_USB;
|
||||
}
|
||||
else if (parts[1] == "CW") {
|
||||
newMode = RADIO_IFACE_MODE_CW;
|
||||
}
|
||||
else if (parts[1] == "LSB") {
|
||||
newMode = RADIO_IFACE_MODE_LSB;
|
||||
}
|
||||
else if (parts[1] == "RAW") {
|
||||
newMode = RADIO_IFACE_MODE_RAW;
|
||||
}
|
||||
else {
|
||||
resp = "RPRT 1\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
return;
|
||||
}
|
||||
int newMode = it->first;
|
||||
|
||||
// If tuning is enabled, set the mode and optionally the bandwidth
|
||||
if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio" && tuningEnabled) {
|
||||
@ -483,9 +492,31 @@ private:
|
||||
if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio") {
|
||||
int mode;
|
||||
core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_GET_MODE, NULL, &mode);
|
||||
resp = std::string(radioModeToString[mode]) + "\n";
|
||||
|
||||
if (mode == RADIO_IFACE_MODE_NFM) {
|
||||
resp = "FM\n";
|
||||
}
|
||||
else if (mode == RADIO_IFACE_MODE_WFM) {
|
||||
resp = "WFM\n";
|
||||
}
|
||||
else if (mode == RADIO_IFACE_MODE_AM) {
|
||||
resp = "AM\n";
|
||||
}
|
||||
else if (mode == RADIO_IFACE_MODE_DSB) {
|
||||
resp = "DSB\n";
|
||||
}
|
||||
else if (mode == RADIO_IFACE_MODE_USB) {
|
||||
resp = "USB\n";
|
||||
}
|
||||
else if (mode == RADIO_IFACE_MODE_CW) {
|
||||
resp = "CW\n";
|
||||
}
|
||||
else if (mode == RADIO_IFACE_MODE_LSB) {
|
||||
resp = "LSB\n";
|
||||
}
|
||||
}
|
||||
else if (!selectedVfo.empty()) {
|
||||
|
||||
if (!selectedVfo.empty()) {
|
||||
resp += std::to_string((int)sigpath::vfoManager.getBandwidth(selectedVfo)) + "\n";
|
||||
}
|
||||
else {
|
||||
@ -659,11 +690,6 @@ private:
|
||||
"0\n" /* RIG_PARM_NONE */;
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
// This get_powerstat stuff is a wordaround for WSJT-X 2.7.0
|
||||
else if (parts[0] == "\\get_powerstat") {
|
||||
resp = "1\n";
|
||||
client->write(resp.size(), (uint8_t*)resp.c_str());
|
||||
}
|
||||
else {
|
||||
// If command is not recognized, return error
|
||||
flog::error("Rigctl client sent invalid command: '{0}'", cmd);
|
||||
|
40
readme.md
40
readme.md
@ -44,7 +44,7 @@ Download the latest release from [the Releases page](https://github.com/Alexandr
|
||||
Then, run:
|
||||
|
||||
```sh
|
||||
sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev
|
||||
sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev
|
||||
sudo dpkg -i sdrpp_debian_amd64.deb
|
||||
```
|
||||
|
||||
@ -52,9 +52,7 @@ If `libvolk2-dev` is not available, use `libvolk1-dev`.
|
||||
|
||||
### Arch-based
|
||||
|
||||
Install from source following the instructions below.
|
||||
|
||||
**WARNING: The sdrpp-git AUR package is no longer official, it is not recommended to use it.**
|
||||
Install the latest release from the [sdrpp-git](https://aur.archlinux.org/packages/sdrpp-git/) AUR package
|
||||
|
||||
### Other
|
||||
|
||||
@ -76,7 +74,7 @@ The preferred IDE is [VS Code](https://code.visualstudio.com/) in order to have
|
||||
|
||||
* [cmake](https://cmake.org)
|
||||
* [vcpkg](https://vcpkg.io)
|
||||
* [PothosSDR](https://github.com/pothosware/PothosSDR) (This will install libraries for most SDRs. You have to install it in `C:/Program Files/PothosSDR`)
|
||||
* [PothosSDR](https://github.com/pothosware/PothosSDR) (This will install libraries for most SDRs)
|
||||
* [RtAudio](https://www.music.mcgill.ca/~gary/rtaudio/) (You have to build and install it in `C:/Program Files (x86)/RtAudio/`)
|
||||
|
||||
After this, install the following dependencies using vcpkg:
|
||||
@ -115,16 +113,16 @@ You will next need to edit the `root_dev/config.json` file to point to the modul
|
||||
From the top directory, you can simply run:
|
||||
|
||||
```bat
|
||||
./build/Release/sdrpp.exe -r root_dev -c
|
||||
./build/Release/sdrpp.exe -r root_dev -s
|
||||
```
|
||||
|
||||
Or, if you wish to run from the build directory e.g. `build/Release` and adapt the relative path to the `root_dev` folder:
|
||||
|
||||
```bat
|
||||
./sdrpp.exe -r ../../root_dev -c
|
||||
./sdrpp.exe -r ../../root_dev -s
|
||||
```
|
||||
|
||||
The optional `-c` argument is for keeping the console active in order to see the error messages.
|
||||
The optional `-s` argument is for keeping the console active in order to see the error messages.
|
||||
|
||||
Because all the paths are relative, for the rest of the command line instructions we are going to assume you are running from the top directory using the former command.
|
||||
As mentioned previously you need to edit `root_dev/config.json` to add the modules that were built. From the default configuration file you need to add the paths in the `modules` section. Add to this list all the modules you wish to use.
|
||||
@ -304,7 +302,7 @@ Here is an example of build commands that will build almost all modules at the t
|
||||
```sh
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake .. -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j<N>
|
||||
```
|
||||
|
||||
@ -329,12 +327,11 @@ Modules in beta are still included in releases for the most part but not enabled
|
||||
|----------------------|------------|-------------------|--------------------------------|:---------------:|:-----------------------:|:---------------------------:|
|
||||
| airspy_source | Working | libairspy | OPT_BUILD_AIRSPY_SOURCE | ✅ | ✅ | ✅ |
|
||||
| airspyhf_source | Working | libairspyhf | OPT_BUILD_AIRSPYHF_SOURCE | ✅ | ✅ | ✅ |
|
||||
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
|
||||
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ⚠️ (not Debian Buster) | ✅ |
|
||||
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
||||
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
|
||||
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
|
||||
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ⛔ |
|
||||
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
|
||||
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ⛔ | ⛔ |
|
||||
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
|
||||
| rfspace_source | Working | - | OPT_BUILD_RFSPACE_SOURCE | ✅ | ✅ | ✅ |
|
||||
| rtl_sdr_source | Working | librtlsdr | OPT_BUILD_RTL_SDR_SOURCE | ✅ | ✅ | ✅ |
|
||||
@ -351,7 +348,7 @@ Modules in beta are still included in releases for the most part but not enabled
|
||||
|
||||
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|
||||
|--------------------|------------|--------------|------------------------------|:---------------:|:----------------:|:---------------------------:|
|
||||
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
|
||||
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
|
||||
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
|
||||
| new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
@ -377,9 +374,9 @@ Modules in beta are still included in releases for the most part but not enabled
|
||||
| discord_integration | Working | - | OPT_BUILD_DISCORD_PRESENCE | ✅ | ✅ | ⛔ |
|
||||
| frequency_manager | Working | - | OPT_BUILD_FREQUENCY_MANAGER | ✅ | ✅ | ✅ |
|
||||
| recorder | Working | - | OPT_BUILD_RECORDER | ✅ | ✅ | ✅ |
|
||||
| rigctl_client | Unfinished | - | OPT_BUILD_RIGCTL_CLIENT | ✅ | ✅ | ⛔ |
|
||||
| rigctl_client | Unfinished | - | OPT_BUILD_RIGCTL_CLIENT | ⛔ | ⛔ | ⛔ |
|
||||
| rigctl_server | Working | - | OPT_BUILD_RIGCTL_SERVER | ✅ | ✅ | ✅ |
|
||||
| scanner | Beta | - | OPT_BUILD_SCANNER | ✅ | ✅ | ⛔ |
|
||||
| scanner | Beta | - | OPT_BUILD_SCANNER | ✅ | ✅ | ✅ |
|
||||
| scheduler | Unfinished | - | OPT_BUILD_SCHEDULER | ⛔ | ⛔ | ⛔ |
|
||||
|
||||
# Troubleshooting
|
||||
@ -432,34 +429,25 @@ I will soon publish a contributing.md listing the code style to use.
|
||||
* Dale L Puckett (K0HYD)
|
||||
* [Daniele D'Agnelli](https://linkedin.com/in/dagnelli)
|
||||
* D. Jones
|
||||
* Dexruus
|
||||
* [EB3FRN](https://www.eb3frn.net/)
|
||||
* Eric Johnson
|
||||
* Ernest Murphy (NH7L)
|
||||
* Flinger Films
|
||||
* [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ)
|
||||
* gringogrigio
|
||||
* Jeff Moe
|
||||
* Joe Cupano
|
||||
* KD1SQ
|
||||
* Kezza
|
||||
* Krys Kamieniecki
|
||||
* Lee Donaghy
|
||||
* Lee KD1SQ
|
||||
* .lozenge. (Hank Hill)
|
||||
* Martin Herren (HB9FXX)
|
||||
* ON4MU
|
||||
* [Passion-Radio.com](https://passion-radio.com/)
|
||||
* Paul Maine
|
||||
* Peter Betz
|
||||
* [Scanner School](https://scannerschool.com/)
|
||||
* Scott Palmer
|
||||
* [SignalsEverywhere](https://signalseverywhere.com/)
|
||||
* Syne Ardwin (WI9SYN)
|
||||
* [W4IPA](https://twitter.com/W4IPAstroke5)
|
||||
* William Arcand (W1WRA)
|
||||
* [Yves Rougy](https://www.twitch.tv/yorzian)
|
||||
* [Zipper](https://github.com/reppiZ)
|
||||
* [Zipper](github.com/reppiZ)
|
||||
|
||||
## Contributors
|
||||
|
||||
@ -473,7 +461,6 @@ I will soon publish a contributing.md listing the code style to use.
|
||||
* [Howard0su](https://github.com/howard0su)
|
||||
* John Donkersley
|
||||
* [Joshua Kimsey](https://github.com/JoshuaKimsey)
|
||||
* [Manawyrm](https://github.com/Manawyrm)
|
||||
* [Martin Hauke](https://github.com/mnhauke)
|
||||
* [Marvin Sinister](https://github.com/marvin-sinister)
|
||||
* [Maxime Biette](https://github.com/mbiette)
|
||||
@ -483,6 +470,7 @@ I will soon publish a contributing.md listing the code style to use.
|
||||
* [Shuyuan Liu](https://github.com/shuyuan-liu)
|
||||
* [Syne Ardwin (WI9SYN)](https://esaille.me/)
|
||||
* [Szymon Zakrent](https://github.com/zakrent)
|
||||
* [Tobias Mädel](https://github.com/Manawyrm)
|
||||
* Youssef Touil
|
||||
* [Zimm](https://github.com/invader-zimm)
|
||||
|
||||
|
@ -1,280 +0,0 @@
|
||||
{
|
||||
"name": "Belgium",
|
||||
"country_name": "Belgium",
|
||||
"country_code": "BE",
|
||||
"author_name": "Bastien Cabay - ON4BCY",
|
||||
"author_url": "https://qrz.com/db/ON4BCY",
|
||||
"bands": [
|
||||
{
|
||||
"name": "2200m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 135700,
|
||||
"end": 137800
|
||||
},
|
||||
{
|
||||
"name": "630m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 472000,
|
||||
"end": 479000
|
||||
},
|
||||
{
|
||||
"name": "600m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 501000,
|
||||
"end": 504000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 526500,
|
||||
"end": 1606500
|
||||
},
|
||||
{
|
||||
"name": "160m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 1810000,
|
||||
"end": 2000000
|
||||
},
|
||||
{
|
||||
"name": "80m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 3500000,
|
||||
"end": 3800000
|
||||
},
|
||||
{
|
||||
"name": "60m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 5351500,
|
||||
"end": 5366500
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 5950000,
|
||||
"end": 6200000
|
||||
},
|
||||
{
|
||||
"name": "40m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 7000000,
|
||||
"end": 7200000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 7200000,
|
||||
"end": 7300000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 9500000,
|
||||
"end": 9900000
|
||||
},
|
||||
{
|
||||
"name": "30m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 10100000,
|
||||
"end": 10150000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 11650000,
|
||||
"end": 12050000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 13600000,
|
||||
"end": 13800000
|
||||
},
|
||||
{
|
||||
"name": "20m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 14000000,
|
||||
"end": 14350000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 15100000,
|
||||
"end": 15600000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 17550000,
|
||||
"end": 17900000
|
||||
},
|
||||
{
|
||||
"name": "17m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 18068000,
|
||||
"end": 18168000
|
||||
},
|
||||
{
|
||||
"name": "15m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 21000000,
|
||||
"end": 21450000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 21450000,
|
||||
"end": 21850000
|
||||
},
|
||||
{
|
||||
"name": "12m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 24890000,
|
||||
"end": 24990000
|
||||
},
|
||||
{
|
||||
"name": "AM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 25670000,
|
||||
"end": 26100000
|
||||
},
|
||||
{
|
||||
"name": "11m - Citizen Band",
|
||||
"type": "amateur",
|
||||
"start": 26960000,
|
||||
"end": 27410000
|
||||
},
|
||||
{
|
||||
"name": "10m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 28000000,
|
||||
"end": 29700000
|
||||
},
|
||||
{
|
||||
"name": "8m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 40660000,
|
||||
"end": 40690000
|
||||
},
|
||||
{
|
||||
"name": "6m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 50000000,
|
||||
"end": 52000000
|
||||
},
|
||||
{
|
||||
"name": "4m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 69945000,
|
||||
"end": 69955000
|
||||
},
|
||||
{
|
||||
"name": "4m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 70190000,
|
||||
"end": 70412500
|
||||
},
|
||||
{
|
||||
"name": "FM Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 87500000,
|
||||
"end": 108000000
|
||||
},
|
||||
{
|
||||
"name": "Space Exploration / Meteorology Sat. / S-PCS",
|
||||
"type": "satellite",
|
||||
"start": 137000000,
|
||||
"end": 138000000
|
||||
},
|
||||
{
|
||||
"name": "2m - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 144000000,
|
||||
"end": 146000000
|
||||
},
|
||||
|
||||
{
|
||||
"name": "T-DAB Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 174000000,
|
||||
"end": 223000000
|
||||
},
|
||||
{
|
||||
"name": "70cm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 430000000,
|
||||
"end": 440000000
|
||||
},
|
||||
{
|
||||
"name": "PMR446",
|
||||
"type": "amateur",
|
||||
"start": 446000000,
|
||||
"end": 446200000
|
||||
},
|
||||
{
|
||||
"name": "DVB-T - Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 470000000,
|
||||
"end": 790000000
|
||||
},
|
||||
{
|
||||
"name": "23cm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 1240000000,
|
||||
"end": 1300000000
|
||||
},
|
||||
{
|
||||
"name": "13cm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 2300000000,
|
||||
"end": 2450000000
|
||||
},
|
||||
{
|
||||
"name": "6cm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 5650000000,
|
||||
"end": 5850000000
|
||||
},
|
||||
{
|
||||
"name": "3cm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 10000000000,
|
||||
"end": 10500000000
|
||||
},
|
||||
{
|
||||
"name": "1.25cm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 24000000000,
|
||||
"end": 24250000000
|
||||
},
|
||||
{
|
||||
"name": "6mm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 47000000000,
|
||||
"end": 47200000000
|
||||
},
|
||||
{
|
||||
"name": "4mm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 75500000000,
|
||||
"end": 81000000000
|
||||
},
|
||||
{
|
||||
"name": "2.5mm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 122250000000,
|
||||
"end": 123000000000
|
||||
},
|
||||
{
|
||||
"name": "2mm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 142000000000,
|
||||
"end": 149000000000
|
||||
},
|
||||
{
|
||||
"name": "1mm - Amateur",
|
||||
"type": "amateur",
|
||||
"start": 241000000000,
|
||||
"end": 250000000000
|
||||
}
|
||||
]
|
||||
}
|
@ -115,7 +115,7 @@
|
||||
"end": 4995000
|
||||
},
|
||||
{
|
||||
"name": "60m - radiodiffusion",
|
||||
"name": "60m - radiodiffusion",
|
||||
"type": "broadcast",
|
||||
"start": 5005000,
|
||||
"end": 5060000
|
||||
@ -340,7 +340,7 @@
|
||||
"name": "11m - CB",
|
||||
"type": "amateur",
|
||||
"start": 26960000,
|
||||
"end": 27410000
|
||||
"end": 27230000
|
||||
},
|
||||
{
|
||||
"name": "10m - Radioamateur",
|
||||
@ -493,4 +493,4 @@
|
||||
"end": 250000000000
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
"name": "Germany",
|
||||
"country_name": "Germany",
|
||||
"country_code": "DE",
|
||||
"author_name": "Manawyrm",
|
||||
"author_name": "Tobias Mädel",
|
||||
"author_url": "https://tbspace.de",
|
||||
"bands": [
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -529,9 +529,9 @@
|
||||
},
|
||||
{
|
||||
"name": "Train communications",
|
||||
"type": "railway",
|
||||
"start": 151712500,
|
||||
"end": 156012500
|
||||
"type": "aviation",
|
||||
"start": 151775000,
|
||||
"end": 151875000
|
||||
},
|
||||
{
|
||||
"name": "Marine",
|
||||
@ -557,6 +557,12 @@
|
||||
"start": 270000000,
|
||||
"end": 380000000
|
||||
},
|
||||
{
|
||||
"name": "Train communications",
|
||||
"type": "aviation",
|
||||
"start": 299999000,
|
||||
"end": 300001000
|
||||
},
|
||||
{
|
||||
"name": "70cm",
|
||||
"type": "amateur",
|
||||
|
@ -5,12 +5,6 @@
|
||||
"author_name": "John Donkersley",
|
||||
"author_url": "",
|
||||
"bands": [
|
||||
{
|
||||
"name": "2200m Ham Band",
|
||||
"type": "amateur",
|
||||
"start": 135700,
|
||||
"end": 137800
|
||||
},
|
||||
{
|
||||
"name": "Long Wave",
|
||||
"type": "broadcast",
|
||||
@ -215,18 +209,18 @@
|
||||
"start": 15010000,
|
||||
"end": 15100000
|
||||
},
|
||||
{
|
||||
"name": "19m Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 15100000,
|
||||
"end": 15800000
|
||||
},
|
||||
{
|
||||
"name": "Maritime",
|
||||
"type": "marine",
|
||||
"start": 16360000,
|
||||
"end": 17410000
|
||||
},
|
||||
{
|
||||
"name": "19m Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 15100000,
|
||||
"end": 15800000
|
||||
},
|
||||
{
|
||||
"name": "16m Broadcast",
|
||||
"type": "broadcast",
|
||||
@ -351,7 +345,7 @@
|
||||
"name": "Air Band TACAN/ILS",
|
||||
"type": "aviation",
|
||||
"start": 108000000,
|
||||
"end": 117975000
|
||||
"end": 118000000
|
||||
},
|
||||
{
|
||||
"name": "Air Band Voice",
|
||||
@ -377,12 +371,6 @@
|
||||
"start": 147343750,
|
||||
"end": 147500000
|
||||
},
|
||||
{
|
||||
"name": "Satellites",
|
||||
"type": "satellite",
|
||||
"start": 148000000,
|
||||
"end": 150050000
|
||||
},
|
||||
{
|
||||
"name": "Pagers - Flex/POCSAG",
|
||||
"type": "PMR",
|
||||
@ -437,18 +425,6 @@
|
||||
"start": 230000000,
|
||||
"end": 400000000
|
||||
},
|
||||
{
|
||||
"name": "Satellites",
|
||||
"type": "satellite",
|
||||
"start": 399900000,
|
||||
"end": 401000000
|
||||
},
|
||||
{
|
||||
"name": "Weather Balloons",
|
||||
"type": "aviation",
|
||||
"start": 401000000,
|
||||
"end": 406000000
|
||||
},
|
||||
{
|
||||
"name": "Private Mobile Radio inc trams",
|
||||
"type": "PMR",
|
||||
@ -486,34 +462,22 @@
|
||||
"end": 455000000
|
||||
},
|
||||
{
|
||||
"name": "Private Mobile Radio inc OB",
|
||||
"name": "Private Mobile Radio",
|
||||
"type": "PMR",
|
||||
"start": 455000000,
|
||||
"end": 470000000
|
||||
"end": 467200000
|
||||
},
|
||||
{
|
||||
"name": "Outside Broadcast Talkback",
|
||||
"type": "PMR",
|
||||
"start": 467200000,
|
||||
"end": 468600000
|
||||
},
|
||||
{
|
||||
"name": "Digital TV Broadcast",
|
||||
"type": "broadcast",
|
||||
"start": 470000000,
|
||||
"end": 700000000
|
||||
},
|
||||
{
|
||||
"name": "Cell phones",
|
||||
"type": "cellular",
|
||||
"start": 703000000,
|
||||
"end": 788000000
|
||||
},
|
||||
{
|
||||
"name": "Band 20 Cell phone downlink",
|
||||
"type": "cellular",
|
||||
"start": 791000000,
|
||||
"end": 821000000
|
||||
},
|
||||
{
|
||||
"name": "Band 20 Cell phone uplink",
|
||||
"type": "cellular",
|
||||
"start": 832000000,
|
||||
"end": 862000000
|
||||
"end": 790000000
|
||||
},
|
||||
{
|
||||
"name": "Licence Exempt Short Range",
|
||||
@ -521,84 +485,12 @@
|
||||
"start": 862000000,
|
||||
"end": 875800000
|
||||
},
|
||||
{
|
||||
"name": "Band 8 Cell phone uplink",
|
||||
"type": "cellular",
|
||||
"start": 880100000,
|
||||
"end": 914900000
|
||||
},
|
||||
{
|
||||
"name": "Band 8 Cell phone downlink",
|
||||
"type": "cellular",
|
||||
"start": 925100000,
|
||||
"end": 929500000
|
||||
},
|
||||
{
|
||||
"name": "23cm Ham Band",
|
||||
"type": "amateur",
|
||||
"start": 1240000000,
|
||||
"end": 1325000000
|
||||
},
|
||||
{
|
||||
"name": "Band 32 Cell phone",
|
||||
"type": "cellular",
|
||||
"start": 1452000000,
|
||||
"end": 1492000000
|
||||
},
|
||||
{
|
||||
"name": "Satellite L-band",
|
||||
"type": "satellite",
|
||||
"start": 1518000000,
|
||||
"end": 1559000000
|
||||
},
|
||||
{
|
||||
"name": "Satellite L-band",
|
||||
"type": "satellite",
|
||||
"start": 1626500000,
|
||||
"end": 1660500000
|
||||
},
|
||||
{
|
||||
"name": "Satellite L-band",
|
||||
"type": "satellite",
|
||||
"start": 1668000000,
|
||||
"end": 1675000000
|
||||
},
|
||||
{
|
||||
"name": "Band 3 Cell phone uplink",
|
||||
"type": "cellular",
|
||||
"start": 1710000000,
|
||||
"end": 1785000000
|
||||
},
|
||||
{
|
||||
"name": "Band 3 Cell phone downlink",
|
||||
"type": "cellular",
|
||||
"start": 1805100000,
|
||||
"end": 1880000000
|
||||
},
|
||||
{
|
||||
"name": "DECT cordless phones",
|
||||
"type": "cellular",
|
||||
"start": 1880000000,
|
||||
"end": 1900000000
|
||||
},
|
||||
{
|
||||
"name": "Band 3 Cell phones",
|
||||
"type": "cellular",
|
||||
"start": 1900000000,
|
||||
"end": 1920000000
|
||||
},
|
||||
{
|
||||
"name": "Band 1 Cell phone uplink",
|
||||
"type": "cellular",
|
||||
"start": 1920000000,
|
||||
"end": 1979700000
|
||||
},
|
||||
{
|
||||
"name": "Band 1 Cell phone downlink",
|
||||
"type": "cellular",
|
||||
"start": 2110300000,
|
||||
"end": 2169700000
|
||||
},
|
||||
{
|
||||
"name": "13cm Ham Band",
|
||||
"type": "amateur",
|
||||
@ -606,28 +498,10 @@
|
||||
"end": 2302000000
|
||||
},
|
||||
{
|
||||
"name": "ISM - wifi and bluettoth",
|
||||
"type": "ISM",
|
||||
"start": 2400000000,
|
||||
"end": 2483000000
|
||||
},
|
||||
{
|
||||
"name": "Band 38 Cell phones",
|
||||
"type": "cellular",
|
||||
"start": 2500000000,
|
||||
"end": 269000000
|
||||
},
|
||||
{
|
||||
"name": "Band 42 5G Cell phones",
|
||||
"type": "cellular",
|
||||
"start": 3410000000,
|
||||
"end": 3720000000
|
||||
},
|
||||
{
|
||||
"name": "ISM - wifi",
|
||||
"type": "ISM",
|
||||
"start": 5150000000,
|
||||
"end": 5850000000
|
||||
"name": "13cm Ham Band",
|
||||
"type": "amateur",
|
||||
"start": 2310000000,
|
||||
"end": 2450000000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
@ -227,18 +227,6 @@
|
||||
"start": 144000000,
|
||||
"end": 148000000
|
||||
},
|
||||
{
|
||||
"name": "MURS (lower)",
|
||||
"type": "amateur",
|
||||
"start": 151820000,
|
||||
"end": 151940000
|
||||
},
|
||||
{
|
||||
"name": "MURS (upper)",
|
||||
"type": "amateur",
|
||||
"start": 154570000,
|
||||
"end": 154600000
|
||||
},
|
||||
{
|
||||
"name": "Marine",
|
||||
"type": "marine",
|
||||
|
@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "Smoke",
|
||||
"author": "Yaroslav Andrianov",
|
||||
"map": [
|
||||
"#FFFFFF",
|
||||
"#EEEEEE",
|
||||
"#CCCCCC",
|
||||
"#777777",
|
||||
"#555555",
|
||||
"#333333",
|
||||
"#111111",
|
||||
"#000000"
|
||||
]
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"name": "Temper Colors",
|
||||
"author": "Yaroslav Andrianov",
|
||||
"map": [
|
||||
"#000000",
|
||||
"#05011f",
|
||||
"#0f0836",
|
||||
"#2f1436",
|
||||
"#3d114d",
|
||||
"#4e186f",
|
||||
"#592a8f",
|
||||
"#5e43a5",
|
||||
"#5f5eb3",
|
||||
"#6276ba",
|
||||
"#6b8cbf",
|
||||
"#7ba1c2",
|
||||
"#95b5c7",
|
||||
"#b3c6ce",
|
||||
"#d4bcac",
|
||||
"#cca389",
|
||||
"#c68a6d",
|
||||
"#be6f5b",
|
||||
"#b25652",
|
||||
"#a24050",
|
||||
"#8e2c50",
|
||||
"#741e4f"
|
||||
]
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "Vivid",
|
||||
"author": "Yaroslav Andrianov",
|
||||
"map": [
|
||||
"#000000",
|
||||
"#06001c",
|
||||
"#090028",
|
||||
"#12002c",
|
||||
"#230039",
|
||||
"#360143",
|
||||
"#440154",
|
||||
"#472c7a",
|
||||
"#3b518b",
|
||||
"#2c718e",
|
||||
"#21908d",
|
||||
"#27ad81",
|
||||
"#5cc863",
|
||||
"#aadc32",
|
||||
"#f6fd25",
|
||||
"#fdde17",
|
||||
"#fecb31",
|
||||
"#FE9029",
|
||||
"#F56918",
|
||||
"#DC3B07",
|
||||
"#CE2D04",
|
||||
"#AC1701",
|
||||
"#980E01"
|
||||
]
|
||||
}
|
22
rpi_install.sh
Normal file
22
rpi_install.sh
Normal file
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
[ $(id -u) = 0 ] && echo "Please do not run this script as root" && exit 100
|
||||
|
||||
echo "Installing dependencies"
|
||||
sudo apt update
|
||||
sudo apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget
|
||||
|
||||
echo "Preparing build"
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_LIMESDR_SOURCE=ON
|
||||
|
||||
echo "Building"
|
||||
make
|
||||
|
||||
echo "Installing"
|
||||
sudo make install
|
||||
|
||||
echo "Done!"
|
@ -45,20 +45,16 @@ public:
|
||||
int count = audio.getDeviceCount();
|
||||
RtAudio::DeviceInfo info;
|
||||
for (int i = 0; i < count; i++) {
|
||||
try {
|
||||
info = audio.getDeviceInfo(i);
|
||||
if (!info.probed) { continue; }
|
||||
if (info.outputChannels == 0) { continue; }
|
||||
if (info.isDefaultOutput) { defaultDevId = devList.size(); }
|
||||
devList.push_back(info);
|
||||
deviceIds.push_back(i);
|
||||
txtDevList += info.name;
|
||||
txtDevList += '\0';
|
||||
}
|
||||
catch (std::exception e) {
|
||||
flog::error("AudioSinkModule Error getting audio device info: {0}", e.what());
|
||||
}
|
||||
info = audio.getDeviceInfo(i);
|
||||
if (!info.probed) { continue; }
|
||||
if (info.outputChannels == 0) { continue; }
|
||||
if (info.isDefaultOutput) { defaultDevId = devList.size(); }
|
||||
devList.push_back(info);
|
||||
deviceIds.push_back(i);
|
||||
txtDevList += info.name;
|
||||
txtDevList += '\0';
|
||||
}
|
||||
|
||||
selectByName(device);
|
||||
}
|
||||
|
||||
@ -294,4 +290,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ SDRPP_MOD_INFO{
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
class AirspySourceModule : public ModuleManager::Instance {
|
||||
class AirspySourceModule : public ModuleManager::Instance, public Source {
|
||||
public:
|
||||
AirspySourceModule(std::string name) {
|
||||
this->name = name;
|
||||
@ -34,15 +34,6 @@ public:
|
||||
|
||||
sampleRate = 10000000.0;
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
handler.deselectHandler = menuDeselected;
|
||||
handler.menuHandler = menuHandler;
|
||||
handler.startHandler = start;
|
||||
handler.stopHandler = stop;
|
||||
handler.tuneHandler = tune;
|
||||
handler.stream = &stream;
|
||||
|
||||
refresh();
|
||||
if (sampleRateList.size() > 0) {
|
||||
sampleRate = sampleRateList[0];
|
||||
@ -54,11 +45,11 @@ public:
|
||||
config.release();
|
||||
selectByString(devSerial);
|
||||
|
||||
sigpath::sourceManager.registerSource("Airspy", &handler);
|
||||
sigpath::sourceManager.registerSource("Airspy", this);
|
||||
}
|
||||
|
||||
~AirspySourceModule() {
|
||||
stop(this);
|
||||
stop();
|
||||
sigpath::sourceManager.unregisterSource("Airspy");
|
||||
airspy_exit();
|
||||
}
|
||||
@ -231,6 +222,315 @@ public:
|
||||
airspy_close(dev);
|
||||
}
|
||||
|
||||
void select() {
|
||||
core::setInputSampleRate(sampleRate);
|
||||
flog::info("AirspySourceModule '{0}': Select!", name);
|
||||
}
|
||||
|
||||
void deselect() {
|
||||
flog::info("AirspySourceModule '{0}': Deselect!", name);
|
||||
}
|
||||
|
||||
bool start() {
|
||||
if (running) { return true; }
|
||||
if (selectedSerial == 0) {
|
||||
flog::error("Tried to start Airspy source with null serial");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
int err = airspy_open_sn(&openDev, selectedSerial);
|
||||
#else
|
||||
int err = airspy_open_fd(&openDev, devFd);
|
||||
#endif
|
||||
if (err != 0) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%016" PRIX64, selectedSerial);
|
||||
flog::error("Could not open Airspy {0}", buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
airspy_set_samplerate(openDev, sampleRateList[srId]);
|
||||
airspy_set_freq(openDev, freq);
|
||||
|
||||
if (gainMode == 0) {
|
||||
airspy_set_lna_agc(openDev, 0);
|
||||
airspy_set_mixer_agc(openDev, 0);
|
||||
airspy_set_sensitivity_gain(openDev, sensitiveGain);
|
||||
}
|
||||
else if (gainMode == 1) {
|
||||
airspy_set_lna_agc(openDev, 0);
|
||||
airspy_set_mixer_agc(openDev, 0);
|
||||
airspy_set_linearity_gain(openDev, linearGain);
|
||||
}
|
||||
else if (gainMode == 2) {
|
||||
if (lnaAgc) {
|
||||
airspy_set_lna_agc(openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(openDev, 0);
|
||||
airspy_set_lna_gain(openDev, lnaGain);
|
||||
}
|
||||
if (mixerAgc) {
|
||||
airspy_set_mixer_agc(openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(openDev, 0);
|
||||
airspy_set_mixer_gain(openDev, mixerGain);
|
||||
}
|
||||
airspy_set_vga_gain(openDev, vgaGain);
|
||||
}
|
||||
|
||||
airspy_set_rf_bias(openDev, biasT);
|
||||
|
||||
airspy_start_rx(openDev, callback, this);
|
||||
|
||||
running = true;
|
||||
flog::info("AirspySourceModule '{0}': Start!", name);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (!running) { return; }
|
||||
running = false;
|
||||
stream.stopWriter();
|
||||
airspy_close(openDev);
|
||||
stream.clearWriteStop();
|
||||
flog::info("AirspySourceModule '{0}': Stop!", name);
|
||||
}
|
||||
|
||||
void tune(double freq) {
|
||||
this->freq = freq;
|
||||
if (running) {
|
||||
airspy_set_freq(openDev, freq);
|
||||
}
|
||||
flog::info("AirspySourceModule '{0}': Tune: {1}!", name, freq);
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
if (running) { SmGui::BeginDisabled(); }
|
||||
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", name), &devId, devListTxt.c_str())) {
|
||||
selectBySerial(devList[devId]);
|
||||
core::setInputSampleRate(sampleRate);
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["device"] = selectedSerStr;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", name), &srId, sampleRateListTxt.c_str())) {
|
||||
sampleRate = sampleRateList[srId];
|
||||
core::setInputSampleRate(sampleRate);
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["sampleRate"] = sampleRate;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
SmGui::SameLine();
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", name))) {
|
||||
refresh();
|
||||
config.acquire();
|
||||
std::string devSerial = config.conf["device"];
|
||||
config.release();
|
||||
selectByString(devSerial);
|
||||
core::setInputSampleRate(sampleRate);
|
||||
}
|
||||
|
||||
if (running) { SmGui::EndDisabled(); }
|
||||
|
||||
SmGui::BeginGroup();
|
||||
SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", name), false);
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", name), gainMode == 0)) {
|
||||
gainMode = 0;
|
||||
if (running) {
|
||||
airspy_set_lna_agc(openDev, 0);
|
||||
airspy_set_mixer_agc(openDev, 0);
|
||||
airspy_set_sensitivity_gain(openDev, sensitiveGain);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["gainMode"] = 0;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
SmGui::NextColumn();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", name), gainMode == 1)) {
|
||||
gainMode = 1;
|
||||
if (running) {
|
||||
airspy_set_lna_agc(openDev, 0);
|
||||
airspy_set_mixer_agc(openDev, 0);
|
||||
airspy_set_linearity_gain(openDev, linearGain);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["gainMode"] = 1;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
SmGui::NextColumn();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", name), gainMode == 2)) {
|
||||
gainMode = 2;
|
||||
if (running) {
|
||||
if (lnaAgc) {
|
||||
airspy_set_lna_agc(openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(openDev, 0);
|
||||
airspy_set_lna_gain(openDev, lnaGain);
|
||||
}
|
||||
if (mixerAgc) {
|
||||
airspy_set_mixer_agc(openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(openDev, 0);
|
||||
airspy_set_mixer_gain(openDev, mixerGain);
|
||||
}
|
||||
airspy_set_vga_gain(openDev, vgaGain);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["gainMode"] = 2;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", name), false);
|
||||
SmGui::EndGroup();
|
||||
|
||||
// Gain menus
|
||||
|
||||
if (gainMode == 0) {
|
||||
SmGui::LeftLabel("Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", name), &sensitiveGain, 0, 21)) {
|
||||
if (running) {
|
||||
airspy_set_sensitivity_gain(openDev, sensitiveGain);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["sensitiveGain"] = sensitiveGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gainMode == 1) {
|
||||
SmGui::LeftLabel("Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", name), &linearGain, 0, 21)) {
|
||||
if (running) {
|
||||
airspy_set_linearity_gain(openDev, linearGain);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["linearGain"] = linearGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gainMode == 2) {
|
||||
// TODO: Switch to a table for alignment
|
||||
if (lnaAgc) { SmGui::BeginDisabled(); }
|
||||
SmGui::LeftLabel("LNA Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", name), &lnaGain, 0, 15)) {
|
||||
if (running) {
|
||||
airspy_set_lna_gain(openDev, lnaGain);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["lnaGain"] = lnaGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
if (lnaAgc) { SmGui::EndDisabled(); }
|
||||
|
||||
if (mixerAgc) { SmGui::BeginDisabled(); }
|
||||
SmGui::LeftLabel("Mixer Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", name), &mixerGain, 0, 15)) {
|
||||
if (running) {
|
||||
airspy_set_mixer_gain(openDev, mixerGain);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["mixerGain"] = mixerGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
if (mixerAgc) { SmGui::EndDisabled(); }
|
||||
|
||||
SmGui::LeftLabel("VGA Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", name), &vgaGain, 0, 15)) {
|
||||
if (running) {
|
||||
airspy_set_vga_gain(openDev, vgaGain);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["vgaGain"] = vgaGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
// AGC Control
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", name), &lnaAgc)) {
|
||||
if (running) {
|
||||
if (lnaAgc) {
|
||||
airspy_set_lna_agc(openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(openDev, 0);
|
||||
airspy_set_lna_gain(openDev, lnaGain);
|
||||
}
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["lnaAgc"] = lnaAgc;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", name), &mixerAgc)) {
|
||||
if (running) {
|
||||
if (mixerAgc) {
|
||||
airspy_set_mixer_agc(openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(openDev, 0);
|
||||
airspy_set_mixer_gain(openDev, mixerGain);
|
||||
}
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["mixerAgc"] = mixerAgc;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bias T
|
||||
if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", name), &biasT)) {
|
||||
if (running) {
|
||||
airspy_set_rf_bias(openDev, biasT);
|
||||
}
|
||||
if (selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][selectedSerStr]["biasT"] = biasT;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string getBandwdithScaled(double bw) {
|
||||
char buf[1024];
|
||||
@ -246,322 +546,6 @@ private:
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
flog::info("AirspySourceModule '{0}': Menu Select!", _this->name);
|
||||
}
|
||||
|
||||
static void menuDeselected(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
flog::info("AirspySourceModule '{0}': Menu Deselect!", _this->name);
|
||||
}
|
||||
|
||||
static void start(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
if (_this->running) { return; }
|
||||
if (_this->selectedSerial == 0) {
|
||||
flog::error("Tried to start Airspy source with null serial");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
int err = airspy_open_sn(&_this->openDev, _this->selectedSerial);
|
||||
#else
|
||||
int err = airspy_open_fd(&_this->openDev, _this->devFd);
|
||||
#endif
|
||||
if (err != 0) {
|
||||
char buf[1024];
|
||||
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
|
||||
flog::error("Could not open Airspy {0}", buf);
|
||||
return;
|
||||
}
|
||||
|
||||
airspy_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]);
|
||||
airspy_set_freq(_this->openDev, _this->freq);
|
||||
|
||||
if (_this->gainMode == 0) {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||
}
|
||||
else if (_this->gainMode == 1) {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||
}
|
||||
else if (_this->gainMode == 2) {
|
||||
if (_this->lnaAgc) {
|
||||
airspy_set_lna_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
if (_this->mixerAgc) {
|
||||
airspy_set_mixer_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||
}
|
||||
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||
}
|
||||
|
||||
airspy_set_rf_bias(_this->openDev, _this->biasT);
|
||||
|
||||
airspy_start_rx(_this->openDev, callback, _this);
|
||||
|
||||
_this->running = true;
|
||||
flog::info("AirspySourceModule '{0}': Start!", _this->name);
|
||||
}
|
||||
|
||||
static void stop(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
if (!_this->running) { return; }
|
||||
_this->running = false;
|
||||
_this->stream.stopWriter();
|
||||
airspy_close(_this->openDev);
|
||||
_this->stream.clearWriteStop();
|
||||
flog::info("AirspySourceModule '{0}': Stop!", _this->name);
|
||||
}
|
||||
|
||||
static void tune(double freq, void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
airspy_set_freq(_this->openDev, freq);
|
||||
}
|
||||
_this->freq = freq;
|
||||
flog::info("AirspySourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||
}
|
||||
|
||||
static void menuHandler(void* ctx) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)ctx;
|
||||
|
||||
if (_this->running) { SmGui::BeginDisabled(); }
|
||||
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) {
|
||||
_this->selectBySerial(_this->devList[_this->devId]);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["device"] = _this->selectedSerStr;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) {
|
||||
_this->sampleRate = _this->sampleRateList[_this->srId];
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->sampleRate;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
SmGui::SameLine();
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", _this->name))) {
|
||||
_this->refresh();
|
||||
config.acquire();
|
||||
std::string devSerial = config.conf["device"];
|
||||
config.release();
|
||||
_this->selectByString(devSerial);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
if (_this->running) { SmGui::EndDisabled(); }
|
||||
|
||||
SmGui::BeginGroup();
|
||||
SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", _this->name), false);
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", _this->name), _this->gainMode == 0)) {
|
||||
_this->gainMode = 0;
|
||||
if (_this->running) {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
SmGui::NextColumn();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", _this->name), _this->gainMode == 1)) {
|
||||
_this->gainMode = 1;
|
||||
if (_this->running) {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
SmGui::NextColumn();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", _this->name), _this->gainMode == 2)) {
|
||||
_this->gainMode = 2;
|
||||
if (_this->running) {
|
||||
if (_this->lnaAgc) {
|
||||
airspy_set_lna_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
if (_this->mixerAgc) {
|
||||
airspy_set_mixer_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||
}
|
||||
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false);
|
||||
SmGui::EndGroup();
|
||||
|
||||
// Gain menus
|
||||
|
||||
if (_this->gainMode == 0) {
|
||||
SmGui::LeftLabel("Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) {
|
||||
if (_this->running) {
|
||||
airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_this->gainMode == 1) {
|
||||
SmGui::LeftLabel("Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) {
|
||||
if (_this->running) {
|
||||
airspy_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_this->gainMode == 2) {
|
||||
// TODO: Switch to a table for alignment
|
||||
if (_this->lnaAgc) { SmGui::BeginDisabled(); }
|
||||
SmGui::LeftLabel("LNA Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
|
||||
if (_this->running) {
|
||||
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
if (_this->lnaAgc) { SmGui::EndDisabled(); }
|
||||
|
||||
if (_this->mixerAgc) { SmGui::BeginDisabled(); }
|
||||
SmGui::LeftLabel("Mixer Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) {
|
||||
if (_this->running) {
|
||||
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
if (_this->mixerAgc) { SmGui::EndDisabled(); }
|
||||
|
||||
SmGui::LeftLabel("VGA Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
|
||||
if (_this->running) {
|
||||
airspy_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
// AGC Control
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", _this->name), &_this->lnaAgc)) {
|
||||
if (_this->running) {
|
||||
if (_this->lnaAgc) {
|
||||
airspy_set_lna_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_lna_agc(_this->openDev, 0);
|
||||
airspy_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", _this->name), &_this->mixerAgc)) {
|
||||
if (_this->running) {
|
||||
if (_this->mixerAgc) {
|
||||
airspy_set_mixer_agc(_this->openDev, 1);
|
||||
}
|
||||
else {
|
||||
airspy_set_mixer_agc(_this->openDev, 0);
|
||||
airspy_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||
}
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bias T
|
||||
if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", _this->name), &_this->biasT)) {
|
||||
if (_this->running) {
|
||||
airspy_set_rf_bias(_this->openDev, _this->biasT);
|
||||
}
|
||||
if (_this->selectedSerStr != "") {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int callback(airspy_transfer_t* transfer) {
|
||||
AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx;
|
||||
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
||||
@ -574,7 +558,6 @@ private:
|
||||
bool enabled = true;
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
double sampleRate;
|
||||
SourceManager::SourceHandler handler;
|
||||
bool running = false;
|
||||
double freq;
|
||||
uint64_t selectedSerial = 0;
|
||||
|
@ -25,7 +25,7 @@ ConfigManager config;
|
||||
struct DeviceInfo {
|
||||
RtAudio::DeviceInfo info;
|
||||
int id;
|
||||
bool operator==(const struct DeviceInfo& other) const {
|
||||
bool operator==(const struct DeviceInfo& other) {
|
||||
return other.id == id;
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,3 @@
|
||||
#define NOMINMAX
|
||||
#include <imgui.h>
|
||||
#include <utils/flog.h>
|
||||
#include <module.h>
|
||||
@ -10,8 +9,6 @@
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <gui/tuner.h>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
@ -124,12 +121,6 @@ private:
|
||||
}
|
||||
try {
|
||||
_this->reader = new WavReader(_this->fileSelect.path);
|
||||
if (_this->reader->getSampleRate() == 0) {
|
||||
_this->reader->close();
|
||||
delete _this->reader;
|
||||
_this->reader = NULL;
|
||||
throw std::runtime_error("Sample rate may not be zero");
|
||||
}
|
||||
_this->sampleRate = _this->reader->getSampleRate();
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
std::string filename = std::filesystem::path(_this->fileSelect.path).filename().string();
|
||||
@ -139,7 +130,7 @@ private:
|
||||
//gui::freqSelect.maxFreq = _this->centerFreq + (_this->sampleRate/2);
|
||||
//gui::freqSelect.limitFreq = true;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
catch (std::exception e) {
|
||||
flog::error("Error: {0}", e.what());
|
||||
}
|
||||
config.acquire();
|
||||
@ -153,8 +144,8 @@ private:
|
||||
|
||||
static void worker(void* ctx) {
|
||||
FileSourceModule* _this = (FileSourceModule*)ctx;
|
||||
double sampleRate = std::max(_this->reader->getSampleRate(), (uint32_t)1);
|
||||
int blockSize = std::min((int)(sampleRate / 200.0f), (int)STREAM_BUFFER_SIZE);
|
||||
double sampleRate = _this->reader->getSampleRate();
|
||||
int blockSize = sampleRate / 200.0f;
|
||||
int16_t* inBuf = new int16_t[blockSize * 2];
|
||||
|
||||
while (true) {
|
||||
@ -168,8 +159,8 @@ private:
|
||||
|
||||
static void floatWorker(void* ctx) {
|
||||
FileSourceModule* _this = (FileSourceModule*)ctx;
|
||||
double sampleRate = std::max(_this->reader->getSampleRate(), (uint32_t)1);
|
||||
int blockSize = std::min((int)(sampleRate / 200.0f), (int)STREAM_BUFFER_SIZE);
|
||||
double sampleRate = _this->reader->getSampleRate();
|
||||
int blockSize = sampleRate / 200.0f;
|
||||
dsp::complex_t* inBuf = new dsp::complex_t[blockSize];
|
||||
|
||||
while (true) {
|
||||
@ -223,4 +214,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
||||
}
|
@ -137,10 +137,6 @@ public:
|
||||
hackrf_device_list_t* _devList = hackrf_device_list();
|
||||
|
||||
for (int i = 0; i < _devList->devicecount; i++) {
|
||||
// Skip devices that are in use
|
||||
if (_devList->serial_numbers[i] == NULL) { continue; }
|
||||
|
||||
// Save the device serial number
|
||||
devList.push_back(_devList->serial_numbers[i]);
|
||||
devListTxt += (char*)(_devList->serial_numbers[i] + 16);
|
||||
devListTxt += '\0';
|
||||
|
@ -205,7 +205,6 @@ namespace hermes {
|
||||
}
|
||||
|
||||
std::vector<Info> discover() {
|
||||
// TODO: Maybe try to instead detect on each interface as a work around for 0.0.0.0 not receiving anything?
|
||||
auto sock = net::openudp("0.0.0.0", 1024);
|
||||
|
||||
// Build discovery packet
|
||||
|
@ -39,7 +39,7 @@ namespace hermes {
|
||||
uint8_t gatewareVerMin;
|
||||
BoardID boardId;
|
||||
|
||||
bool operator==(const Info& b) const {
|
||||
bool operator==(const Info& b) {
|
||||
return !memcmp(mac, b.mac, 6);
|
||||
}
|
||||
};
|
||||
|
@ -319,7 +319,6 @@ private:
|
||||
static void start(void* ctx) {
|
||||
LimeSDRSourceModule* _this = (LimeSDRSourceModule*)ctx;
|
||||
if (_this->running) { return; }
|
||||
if (_this->selectedDevName.empty()) { return; }
|
||||
|
||||
// Open device
|
||||
_this->openDev = NULL;
|
||||
@ -330,10 +329,7 @@ private:
|
||||
if (err) {
|
||||
LMS_Close(_this->openDev);
|
||||
LMS_Open(&_this->openDev, _this->devList[_this->devId], NULL);
|
||||
if (err = LMS_Init(_this->openDev)) {
|
||||
flog::error("Failed to re-initialize device ({})", err);
|
||||
return;
|
||||
}
|
||||
LMS_Init(_this->openDev);
|
||||
}
|
||||
|
||||
flog::warn("Channel count: {0}", LMS_GetNumChannels(_this->openDev, false));
|
||||
@ -550,4 +546,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(perseus_source)
|
||||
|
||||
file(GLOB SRC "src/*.cpp")
|
||||
|
||||
include(${SDRPP_MODULE_CMAKE})
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_link_directories(perseus_source PRIVATE "C:/Program Files/PothosSDR/bin/")
|
||||
|
||||
target_include_directories(perseus_source PUBLIC "C:/Program Files/PothosSDR/include/perseus-sdr")
|
||||
|
||||
target_link_libraries(perseus_source PRIVATE perseus-sdr)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(LIBPERSEUSSDR REQUIRED libperseus-sdr)
|
||||
|
||||
target_include_directories(perseus_source PRIVATE ${LIBPERSEUSSDR_INCLUDE_DIRS})
|
||||
target_link_directories(perseus_source PRIVATE ${LIBPERSEUSSDR_LIBRARY_DIRS})
|
||||
target_link_libraries(perseus_source PRIVATE ${LIBPERSEUSSDR_LIBRARIES})
|
||||
|
||||
# Include it because for some reason pkgconfig doesn't look here?
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
target_include_directories(perseus_source PRIVATE "/usr/local/include")
|
||||
endif()
|
||||
|
||||
endif ()
|
@ -1,468 +0,0 @@
|
||||
#include <utils/flog.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <config.h>
|
||||
#include <gui/smgui.h>
|
||||
#include <gui/widgets/stepped_slider.h>
|
||||
#include <perseus-sdr.h>
|
||||
#include <utils/optionlist.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "perseus_source",
|
||||
/* Description: */ "Perseus SDR source module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ 1
|
||||
};
|
||||
|
||||
#define MAX_SAMPLERATE_COUNT 128
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
class PerseusSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
PerseusSourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
sampleRate = 768000;
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
handler.deselectHandler = menuDeselected;
|
||||
handler.menuHandler = menuHandler;
|
||||
handler.startHandler = start;
|
||||
handler.stopHandler = stop;
|
||||
handler.tuneHandler = tune;
|
||||
handler.stream = &stream;
|
||||
|
||||
perseus_set_debug(9);
|
||||
|
||||
refresh();
|
||||
|
||||
config.acquire();
|
||||
std::string serial = config.conf["device"];
|
||||
config.release();
|
||||
select(serial);
|
||||
|
||||
sigpath::sourceManager.registerSource("Perseus", &handler);
|
||||
}
|
||||
|
||||
~PerseusSourceModule() {
|
||||
stop(this);
|
||||
sigpath::sourceManager.unregisterSource("Perseus");
|
||||
if (libInit) { perseus_exit(); }
|
||||
}
|
||||
|
||||
void postInit() {}
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
// Re-initialize driver
|
||||
if (libInit) { perseus_exit(); }
|
||||
int devCount = perseus_init();
|
||||
if (devCount < 0) {
|
||||
libInit = false;
|
||||
flog::error("Could not initialize libperseus: {}", perseus_errorstr());
|
||||
return;
|
||||
}
|
||||
libInit = true;
|
||||
|
||||
// Open each device to get the serial number
|
||||
for (int i = 0; i < devCount; i++) {
|
||||
// Open device
|
||||
perseus_descr* dev = perseus_open(i);
|
||||
if (!dev) {
|
||||
flog::error("Failed to open Perseus device with ID {}: {}", i, perseus_errorstr());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Load firmware
|
||||
int err = perseus_firmware_download(dev, NULL);
|
||||
if (err) {
|
||||
flog::error("Could not upload firmware to device {}: {}", i, perseus_errorstr());
|
||||
perseus_close(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get info
|
||||
eeprom_prodid prodId;
|
||||
err = perseus_get_product_id(dev, &prodId);
|
||||
if (err) {
|
||||
flog::error("Could not getproduct info from device {}: {}", i, perseus_errorstr());
|
||||
perseus_close(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create entry
|
||||
char serial[128];
|
||||
char buf[128];
|
||||
sprintf(serial, "%05d", (int)prodId.sn);
|
||||
sprintf(buf, "Perseus %d.%d [%s]", (int)prodId.hwver, (int)prodId.hwrel, serial);
|
||||
devList.define(serial, buf, i);
|
||||
|
||||
// Close device
|
||||
perseus_close(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void select(const std::string& serial) {
|
||||
// If there are no devices, give up
|
||||
if (devList.empty()) {
|
||||
selectedSerial.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the serial number is not available, select first instead
|
||||
if (!devList.keyExists(serial)) {
|
||||
select(devList.key(0));
|
||||
return;
|
||||
}
|
||||
|
||||
// Open device
|
||||
selectedSerial = serial;
|
||||
selectedPerseusId = devList.value(devList.keyId(serial));
|
||||
perseus_descr* dev = perseus_open(selectedPerseusId);
|
||||
if (!dev) {
|
||||
flog::error("Failed to open device {}: {}", selectedPerseusId, perseus_errorstr());
|
||||
selectedSerial.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Load firmware
|
||||
int err = perseus_firmware_download(dev, NULL);
|
||||
if (err) {
|
||||
flog::error("Could not upload firmware to device: {}", perseus_errorstr());
|
||||
perseus_close(dev);
|
||||
selectedSerial.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get info
|
||||
eeprom_prodid prodId;
|
||||
err = perseus_get_product_id(dev, &prodId);
|
||||
if (err) {
|
||||
flog::error("Could not getproduct info from device: {}", perseus_errorstr());
|
||||
perseus_close(dev);
|
||||
selectedSerial.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// List samplerates
|
||||
srList.clear();
|
||||
int samplerates[MAX_SAMPLERATE_COUNT];
|
||||
memset(samplerates, 0, sizeof(int)*MAX_SAMPLERATE_COUNT);
|
||||
err = perseus_get_sampling_rates(dev, samplerates, MAX_SAMPLERATE_COUNT);
|
||||
if (err) {
|
||||
flog::error("Could not get samplerate list: {}", perseus_errorstr());
|
||||
perseus_close(dev);
|
||||
selectedSerial.clear();
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < MAX_SAMPLERATE_COUNT; i++) {
|
||||
if (!samplerates[i]) { break; }
|
||||
srList.define(samplerates[i], getBandwdithScaled(samplerates[i]), samplerates[i]);
|
||||
}
|
||||
|
||||
// TODO: List attenuator values
|
||||
|
||||
// Load options
|
||||
srId = 0;
|
||||
dithering = false;
|
||||
preamp = false;
|
||||
preselector = true;
|
||||
atten = 0;
|
||||
config.acquire();
|
||||
if (config.conf["devices"][selectedSerial].contains("samplerate")) {
|
||||
int sr = config.conf["devices"][selectedSerial]["samplerate"];
|
||||
if (srList.keyExists(sr)) {
|
||||
srId = srList.keyId(sr);
|
||||
}
|
||||
}
|
||||
if (config.conf["devices"][selectedSerial].contains("dithering")) {
|
||||
dithering = config.conf["devices"][selectedSerial]["dithering"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerial].contains("preamp")) {
|
||||
preamp = config.conf["devices"][selectedSerial]["preamp"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerial].contains("preselector")) {
|
||||
preselector = config.conf["devices"][selectedSerial]["preselector"];
|
||||
}
|
||||
if (config.conf["devices"][selectedSerial].contains("attenuation")) {
|
||||
atten = config.conf["devices"][selectedSerial]["attenuation"];
|
||||
}
|
||||
config.release();
|
||||
|
||||
// Update samplerate
|
||||
sampleRate = srList[srId];
|
||||
core::setInputSampleRate(sampleRate);
|
||||
|
||||
// Close device
|
||||
perseus_close(dev);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string getBandwdithScaled(double bw) {
|
||||
char buf[1024];
|
||||
if (bw >= 1000000.0) {
|
||||
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
|
||||
}
|
||||
else if (bw >= 1000.0) {
|
||||
sprintf(buf, "%.1lfKHz", bw / 1000.0);
|
||||
}
|
||||
else {
|
||||
sprintf(buf, "%.1lfHz", bw);
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
flog::info("PerseusSourceModule '{0}': Menu Select!", _this->name);
|
||||
}
|
||||
|
||||
static void menuDeselected(void* ctx) {
|
||||
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
|
||||
flog::info("PerseusSourceModule '{0}': Menu Deselect!", _this->name);
|
||||
}
|
||||
|
||||
static void start(void* ctx) {
|
||||
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
|
||||
if (_this->running) { return; }
|
||||
if (_this->selectedSerial.empty()) {
|
||||
flog::error("No device is selected");
|
||||
return;
|
||||
}
|
||||
|
||||
// Open device
|
||||
_this->openDev = perseus_open(_this->selectedPerseusId);
|
||||
if (!_this->openDev) {
|
||||
flog::error("Failed to open device {}: {}", _this->selectedPerseusId, perseus_errorstr());
|
||||
return;
|
||||
}
|
||||
|
||||
// Load firmware
|
||||
int err = perseus_firmware_download(_this->openDev, NULL);
|
||||
if (err) {
|
||||
flog::error("Could not upload firmware to device: {}", perseus_errorstr());
|
||||
perseus_close(_this->openDev);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set samplerate
|
||||
err = perseus_set_sampling_rate(_this->openDev, _this->sampleRate);
|
||||
if (err) {
|
||||
flog::error("Could not set samplerate: {}", perseus_errorstr());
|
||||
perseus_close(_this->openDev);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set options
|
||||
perseus_set_adc(_this->openDev, _this->dithering, _this->preamp);
|
||||
perseus_set_attenuator_in_db(_this->openDev, _this->atten);
|
||||
perseus_set_ddc_center_freq(_this->openDev, _this->freq, _this->preselector);
|
||||
|
||||
// Start stream
|
||||
int idealBufferSize = _this->sampleRate / 200;
|
||||
int multipleOf1024 = std::clamp<int>(idealBufferSize / 1024, 1, 2);
|
||||
int bufferSize = multipleOf1024 * 1024;
|
||||
int bufferBytes = bufferSize*6;
|
||||
err = perseus_start_async_input(_this->openDev, bufferBytes, callback, _this);
|
||||
if (err) {
|
||||
flog::error("Could not start stream: {}", perseus_errorstr());
|
||||
perseus_close(_this->openDev);
|
||||
return;
|
||||
}
|
||||
|
||||
_this->running = true;
|
||||
flog::info("PerseusSourceModule '{0}': Start!", _this->name);
|
||||
}
|
||||
|
||||
static void stop(void* ctx) {
|
||||
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
|
||||
if (!_this->running) { return; }
|
||||
_this->running = false;
|
||||
|
||||
// Stop stream
|
||||
_this->stream.stopWriter();
|
||||
perseus_stop_async_input(_this->openDev);
|
||||
_this->stream.clearWriteStop();
|
||||
|
||||
// Close device
|
||||
perseus_close(_this->openDev);
|
||||
|
||||
flog::info("PerseusSourceModule '{0}': Stop!", _this->name);
|
||||
}
|
||||
|
||||
static void tune(double freq, void* ctx) {
|
||||
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
perseus_set_ddc_center_freq(_this->openDev, freq, _this->preselector);
|
||||
}
|
||||
_this->freq = freq;
|
||||
flog::info("PerseusSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||
}
|
||||
|
||||
static void menuHandler(void* ctx) {
|
||||
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
|
||||
|
||||
if (_this->running) { SmGui::BeginDisabled(); }
|
||||
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Combo(CONCAT("##_airspyhf_dev_sel_", _this->name), &_this->devId, _this->devList.txt)) {
|
||||
std::string serial = _this->devList.key(_this->devId);
|
||||
_this->select(serial);
|
||||
config.acquire();
|
||||
config.conf["device"] = serial;
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
if (SmGui::Combo(CONCAT("##_airspyhf_sr_sel_", _this->name), &_this->srId, _this->srList.txt)) {
|
||||
_this->sampleRate = _this->srList[_this->srId];
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->sampleRate;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
SmGui::SameLine();
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Button(CONCAT("Refresh##_airspyhf_refr_", _this->name))) {
|
||||
_this->refresh();
|
||||
_this->select(_this->selectedSerial);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
if (_this->running) { SmGui::EndDisabled(); }
|
||||
|
||||
SmGui::LeftLabel("Attenuation");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderFloatWithSteps(CONCAT("##_airspyhf_atten_", _this->name), &_this->atten, 0, 30, 10, SmGui::FMT_STR_FLOAT_DB_NO_DECIMAL)) {
|
||||
if (_this->running) {
|
||||
perseus_set_attenuator_in_db(_this->openDev, _this->atten);
|
||||
}
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["attenuation"] = _this->atten;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (SmGui::Checkbox(CONCAT("Preamp##_airspyhf_preamp_", _this->name), &_this->preamp)) {
|
||||
if (_this->running) {
|
||||
perseus_set_adc(_this->openDev, _this->dithering, _this->preamp);
|
||||
}
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["preamp"] = _this->preamp;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (SmGui::Checkbox(CONCAT("Dithering##_airspyhf_dither_", _this->name), &_this->dithering)) {
|
||||
if (_this->running) {
|
||||
perseus_set_adc(_this->openDev, _this->dithering, _this->preamp);
|
||||
}
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["dithering"] = _this->dithering;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (SmGui::Checkbox(CONCAT("Preselector##_airspyhf_presel_", _this->name), &_this->preselector)) {
|
||||
if (_this->running) {
|
||||
perseus_set_ddc_center_freq(_this->openDev, _this->freq, _this->preselector);
|
||||
}
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["preselector"] = _this->preselector;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int callback(void* buf, int bufferSize, void* ctx) {
|
||||
PerseusSourceModule* _this = (PerseusSourceModule*)ctx;
|
||||
uint8_t* samples = (uint8_t*)buf;
|
||||
int sampleCount = bufferSize / 6;
|
||||
for (int i = 0; i < sampleCount; i++) {
|
||||
int32_t re, im;
|
||||
re = *(samples++);
|
||||
re |= *(samples++) << 8;
|
||||
re |= *(samples++) << 16;
|
||||
re |= (re >> 23) * (0xFF << 24); // Sign extend
|
||||
im = *(samples++);
|
||||
im |= *(samples++) << 8;
|
||||
im |= *(samples++) << 16;
|
||||
im |= (im >> 23) * (0xFF << 24); // Sign extend
|
||||
_this->stream.writeBuf[i].re = ((float)re / (float)0x7FFFFF);
|
||||
_this->stream.writeBuf[i].im = ((float)im / (float)0x7FFFFF);
|
||||
}
|
||||
_this->stream.swap(sampleCount);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
int sampleRate;
|
||||
SourceManager::SourceHandler handler;
|
||||
bool running = false;
|
||||
double freq;
|
||||
int devId = 0;
|
||||
int srId = 0;
|
||||
bool libInit = false;
|
||||
perseus_descr* openDev;
|
||||
std::string selectedSerial = "";
|
||||
int selectedPerseusId;
|
||||
float atten = 0;
|
||||
bool preamp = false;
|
||||
bool dithering = false;
|
||||
bool preselector = true;
|
||||
|
||||
OptionList<std::string, int> devList;
|
||||
OptionList<int, int> srList;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
json def = json({});
|
||||
def["devices"] = json({});
|
||||
def["device"] = "";
|
||||
config.setPath(core::args["root"].s() + "/perseus_config.json");
|
||||
config.load(def);
|
||||
config.enableAutoSave();
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new PerseusSourceModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
delete (PerseusSourceModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
@ -108,12 +108,12 @@ namespace rfspace {
|
||||
}
|
||||
break;
|
||||
case RFSPACE_DEV_ID_NET_SDR:
|
||||
case RFSPACE_DEV_ID_SDR_IP:
|
||||
default:
|
||||
for (int n = 80000000 / (4 * 25); n >= 32000; n /= 2) {
|
||||
sr.push_back(n);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return sr;
|
||||
|
@ -171,7 +171,7 @@ public:
|
||||
#ifndef __ANDROID__
|
||||
int oret = rtlsdr_open(&openDev, id);
|
||||
#else
|
||||
int oret = rtlsdr_open_sys_dev(&openDev, devFd);
|
||||
int oret = rtlsdr_open_fd(&openDev, devFd);
|
||||
#endif
|
||||
|
||||
if (oret < 0) {
|
||||
@ -285,7 +285,7 @@ private:
|
||||
#ifndef __ANDROID__
|
||||
int oret = rtlsdr_open(&_this->openDev, _this->devId);
|
||||
#else
|
||||
int oret = rtlsdr_open_sys_dev(&_this->openDev, _this->devFd);
|
||||
int oret = rtlsdr_open_fd(&_this->openDev, _this->devFd);
|
||||
#endif
|
||||
|
||||
if (oret < 0) {
|
||||
@ -523,8 +523,8 @@ private:
|
||||
RTLSDRSourceModule* _this = (RTLSDRSourceModule*)ctx;
|
||||
int sampCount = len / 2;
|
||||
for (int i = 0; i < sampCount; i++) {
|
||||
_this->stream.writeBuf[i].re = ((float)buf[i * 2] - 127.4) / 128.0f;
|
||||
_this->stream.writeBuf[i].im = ((float)buf[(i * 2) + 1] - 127.4) / 128.0f;
|
||||
_this->stream.writeBuf[i].re = (float)(buf[i * 2] - 127) / 128.0f;
|
||||
_this->stream.writeBuf[i].im = (float)(buf[(i * 2) + 1] - 127) / 128.0f;
|
||||
}
|
||||
if (!_this->stream.swap(sampCount)) { return; }
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ public:
|
||||
this->name = name;
|
||||
|
||||
strcpy(hostname, "localhost");
|
||||
sampleRate = 5750000.0;
|
||||
sampleRate = 41000000.0;
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
@ -103,14 +103,8 @@ private:
|
||||
|
||||
static void tune(double freq, void* ctx) {
|
||||
SpectranHTTPSourceModule* _this = (SpectranHTTPSourceModule*)ctx;
|
||||
bool connected = (_this->client && _this->client->isOpen());
|
||||
if (connected) {
|
||||
int64_t newfreq = round(freq);
|
||||
if (newfreq != _this->lastReportedFreq && _this->gotReport) {
|
||||
flog::debug("Sending tuning command");
|
||||
_this->lastReportedFreq = newfreq;
|
||||
_this->client->setCenterFrequency(newfreq);
|
||||
}
|
||||
if (_this->running) {
|
||||
// TODO
|
||||
}
|
||||
_this->freq = freq;
|
||||
flog::info("SpectranHTTPSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||
@ -144,8 +138,6 @@ private:
|
||||
_this->tryConnect();
|
||||
}
|
||||
else if (connected && SmGui::Button("Disconnect##spectran_http_source")) {
|
||||
_this->client->onCenterFrequencyChanged.unbind(_this->onFreqChangedId);
|
||||
_this->client->onCenterFrequencyChanged.unbind(_this->onSamplerateChangedId);
|
||||
_this->client->close();
|
||||
}
|
||||
if (_this->running) { style::endDisabled(); }
|
||||
@ -162,28 +154,13 @@ private:
|
||||
|
||||
void tryConnect() {
|
||||
try {
|
||||
gotReport = false;
|
||||
client = std::make_shared<SpectranHTTPClient>(hostname, port, &stream);
|
||||
onFreqChangedId = client->onCenterFrequencyChanged.bind(&SpectranHTTPSourceModule::onFreqChanged, this);
|
||||
onSamplerateChangedId = client->onSamplerateChanged.bind(&SpectranHTTPSourceModule::onSamplerateChanged, this);
|
||||
client->startWorker();
|
||||
}
|
||||
catch (std::runtime_error e) {
|
||||
flog::error("Could not connect: {0}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void onFreqChanged(double newFreq) {
|
||||
if (lastReportedFreq == newFreq) { return; }
|
||||
lastReportedFreq = newFreq;
|
||||
tuner::tune(tuner::TUNER_MODE_IQ_ONLY, "", newFreq);
|
||||
gotReport = true;
|
||||
}
|
||||
|
||||
void onSamplerateChanged(double newSr) {
|
||||
core::setInputSampleRate(newSr);
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
double sampleRate;
|
||||
@ -191,16 +168,11 @@ private:
|
||||
bool running = false;
|
||||
|
||||
std::shared_ptr<SpectranHTTPClient> client;
|
||||
HandlerID onFreqChangedId;
|
||||
HandlerID onSamplerateChangedId;
|
||||
|
||||
double freq;
|
||||
|
||||
int64_t lastReportedFreq = 0;
|
||||
bool gotReport;
|
||||
|
||||
char hostname[1024];
|
||||
int port = 54664;
|
||||
int port = 80;
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
|
||||
};
|
||||
|
@ -5,8 +5,6 @@ SpectranHTTPClient::SpectranHTTPClient(std::string host, int port, dsp::stream<d
|
||||
this->stream = stream;
|
||||
|
||||
// Connect to server
|
||||
this->host = host;
|
||||
this->port = port;
|
||||
sock = net::connect(host, port);
|
||||
http = net::http::Client(sock);
|
||||
|
||||
@ -16,13 +14,6 @@ SpectranHTTPClient::SpectranHTTPClient(std::string host, int port, dsp::stream<d
|
||||
net::http::ResponseHeader rshdr;
|
||||
http.recvResponseHeader(rshdr, 5000);
|
||||
|
||||
if (rshdr.getStatusCode() != net::http::STATUS_CODE_OK) {
|
||||
flog::error("HTTP request did not return ok: {}", rshdr.getStatusString());
|
||||
throw std::runtime_error("HTTP request did not return ok");
|
||||
}
|
||||
}
|
||||
|
||||
void SpectranHTTPClient::startWorker() {
|
||||
// Start chunk worker
|
||||
workerThread = std::thread(&SpectranHTTPClient::worker, this);
|
||||
}
|
||||
@ -42,27 +33,6 @@ void SpectranHTTPClient::close() {
|
||||
stream->clearWriteStop();
|
||||
}
|
||||
|
||||
void SpectranHTTPClient::setCenterFrequency(uint64_t freq) {
|
||||
// Connect to control endpoint (TODO: Switch to an always connected endpoint)
|
||||
auto controlSock = net::connect(host, port);
|
||||
auto controlHttp = net::http::Client(controlSock);
|
||||
|
||||
// Make request
|
||||
net::http::RequestHeader rqhdr(net::http::METHOD_PUT, "/control", host);
|
||||
char buf[1024];
|
||||
sprintf(buf, "{\"frequencyCenter\":%d,\"frequencySpan\":%d,\"type\":\"capture\"}", freq, _samplerate);
|
||||
std::string data = buf;
|
||||
char lenBuf[16];
|
||||
sprintf(lenBuf, "%d", data.size());
|
||||
rqhdr.setField("Content-Length", lenBuf);
|
||||
controlHttp.sendRequestHeader(rqhdr);
|
||||
controlSock->sendstr(data);
|
||||
net::http::ResponseHeader rshdr;
|
||||
controlHttp.recvResponseHeader(rshdr, 5000);
|
||||
|
||||
flog::debug("Response: {}", rshdr.getStatusString());
|
||||
}
|
||||
|
||||
void SpectranHTTPClient::worker() {
|
||||
while (sock->isOpen()) {
|
||||
// Get chunk header
|
||||
@ -82,41 +52,6 @@ void SpectranHTTPClient::worker() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Decode JSON (yes, this is hacky, but it must be extremely fast)
|
||||
auto startFreqBegin = jsonData.find("\"startFrequency\":");
|
||||
auto startFreqEnd = jsonData.find(',', startFreqBegin);
|
||||
std::string startFreqStr = jsonData.substr(startFreqBegin + 17, startFreqEnd - startFreqBegin - 17);
|
||||
int64_t startFreq = std::stoll(startFreqStr);
|
||||
|
||||
auto endFreqBegin = jsonData.find("\"endFrequency\":");
|
||||
auto endFreqEnd = jsonData.find(',', endFreqBegin);
|
||||
std::string endFreqStr = jsonData.substr(endFreqBegin + 15, endFreqEnd - endFreqBegin - 15);
|
||||
int64_t endFreq = std::stoll(endFreqStr);
|
||||
|
||||
auto sampleFreqBegin = jsonData.find("\"sampleFrequency\":");
|
||||
bool sampleFreqReceived = (sampleFreqBegin != -1);
|
||||
int64_t sampleFreq;
|
||||
if (sampleFreqReceived) {
|
||||
auto sampleFreqEnd = jsonData.find(',', sampleFreqBegin);
|
||||
std::string sampleFreqStr = jsonData.substr(sampleFreqBegin + 18, sampleFreqEnd - sampleFreqBegin - 18);
|
||||
sampleFreq = std::stoll(sampleFreqStr);
|
||||
//flog::debug("{}", jsonData);
|
||||
}
|
||||
|
||||
// Calculate and update center freq
|
||||
int64_t samplerate = /* sampleFreqReceived ? sampleFreq : */(endFreq - startFreq);
|
||||
int64_t centerFreq = round(((double)endFreq + (double)startFreq) / 2.0);
|
||||
if (centerFreq != _centerFreq) {
|
||||
flog::debug("New center freq: {}", centerFreq);
|
||||
_centerFreq = centerFreq;
|
||||
onCenterFrequencyChanged(centerFreq);
|
||||
}
|
||||
if (samplerate != _samplerate) {
|
||||
flog::debug("New samplerate: {}", samplerate);
|
||||
_samplerate = samplerate;
|
||||
onSamplerateChanged(samplerate);
|
||||
}
|
||||
|
||||
// Read (and check for) record separator
|
||||
uint8_t rs;
|
||||
int rslen = sock->recv(&rs, 1, true, 5000);
|
||||
@ -137,11 +72,10 @@ void SpectranHTTPClient::worker() {
|
||||
i += read;
|
||||
sampLen += read;
|
||||
}
|
||||
int sampCount = sampLen / 8;
|
||||
|
||||
// Swap to stream
|
||||
if (streamingEnabled) {
|
||||
if (!stream->swap(sampCount)) { return; }
|
||||
if (!stream->swap(sampLen / 8)) { return; }
|
||||
}
|
||||
|
||||
// Read trailing CRLF
|
||||
|
@ -4,35 +4,22 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utils/proto/http.h>
|
||||
#include <utils/new_event.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class SpectranHTTPClient {
|
||||
public:
|
||||
SpectranHTTPClient(std::string host, int port, dsp::stream<dsp::complex_t>* stream);
|
||||
|
||||
void startWorker();
|
||||
void streaming(bool enabled);
|
||||
bool isOpen();
|
||||
void close();
|
||||
|
||||
void setCenterFrequency(uint64_t freq);
|
||||
|
||||
NewEvent<uint64_t> onCenterFrequencyChanged;
|
||||
NewEvent<uint64_t> onSamplerateChanged;
|
||||
|
||||
private:
|
||||
void worker();
|
||||
|
||||
std::string host;
|
||||
int port;
|
||||
|
||||
std::shared_ptr<net::Socket> sock;
|
||||
net::http::Client http;
|
||||
dsp::stream<dsp::complex_t>* stream;
|
||||
std::thread workerThread;
|
||||
|
||||
bool streamingEnabled = false;
|
||||
int64_t _centerFreq = 0;
|
||||
uint64_t _samplerate = 0;
|
||||
};
|
@ -503,7 +503,7 @@ private:
|
||||
float refStep = 0.5;
|
||||
|
||||
struct SRCombo {
|
||||
bool operator==(const SRCombo& b) const {
|
||||
bool operator==(const SRCombo& b) {
|
||||
return baseId == b.baseId && decimId == b.decimId;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user