mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 00:48:11 +01:00 
			
		
		
		
	Compare commits
	
		
			92 Commits
		
	
	
		
			noise_redu
			...
			nightly
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 052167962d | ||
|  | 6fc41a81a7 | ||
|  | e710b6c6dc | ||
|  | a91434c5fe | ||
|  | ead2ac6128 | ||
|  | 505cbb0ba2 | ||
|  | b1030cbdfb | ||
|  | f3c5b2c31f | ||
|  | 794d6ff5ac | ||
|  | 4803271115 | ||
|  | 78daed7879 | ||
|  | 5f297b1a69 | ||
|  | 7c5d4226eb | ||
|  | ec086ebbdf | ||
|  | d10d420467 | ||
|  | 5bf989f49d | ||
|  | 4b3b6976d6 | ||
|  | 55ddd383d2 | ||
|  | a043ab2dd3 | ||
|  | d270e1c5e8 | ||
|  | e41f24a95e | ||
|  | 766b3db363 | ||
|  | 0632342bb7 | ||
|  | 27b07ed0e8 | ||
|  | a824c83848 | ||
|  | 0e50ee0e67 | ||
|  | a55d1d9c06 | ||
|  | 97187b790f | ||
|  | 9c1361a8a9 | ||
|  | 4d0d14856b | ||
|  | 365ab2325e | ||
|  | 4da7e686f3 | ||
|  | c1d9ab64f8 | ||
|  | ea33135bf1 | ||
|  | 2081384905 | ||
|  | 6b31134af2 | ||
|  | 99d2786c25 | ||
|  | 320f4459ed | ||
|  | de5816f79f | ||
|  | 7b9c01ec73 | ||
|  | f06eccd97c | ||
|  | 88baa8a48e | ||
|  | b436fd0745 | ||
|  | 6fa4299136 | ||
|  | 220dcbcc76 | ||
|  | 15ad065feb | ||
|  | dddf84510e | ||
|  | acd9ad9781 | ||
|  | 168e28cc44 | ||
|  | 3b9867c1d7 | ||
|  | ff7ef78b8f | ||
|  | 87add9ad83 | ||
|  | 6cd09f9b60 | ||
|  | 8d05c1e181 | ||
|  | 1c081cad78 | ||
|  | aa929a1e79 | ||
|  | 5acdab0d22 | ||
|  | 3e3846daa1 | ||
|  | 47617e1acd | ||
|  | 1df51020aa | ||
|  | aa1fd9e573 | ||
|  | 21816fb438 | ||
|  | 3a06612ff5 | ||
|  | a53bf05ed3 | ||
|  | 78c57db116 | ||
|  | c16281f68a | ||
|  | 32c580ba57 | ||
|  | 664b5d85e2 | ||
|  | 6bcb62bfb2 | ||
|  | 0e7c754b8b | ||
|  | 71327cd695 | ||
|  | f296730302 | ||
|  | b89fdba433 | ||
|  | 2c3b522787 | ||
|  | 528763d10e | ||
|  | 582aeed640 | ||
|  | 9c0b57a036 | ||
|  | 604f95fd96 | ||
|  | c892e51000 | ||
|  | 3336ae4aa5 | ||
|  | 190cea8e4e | ||
|  | 13a268a3e1 | ||
|  | 800a8b22c7 | ||
|  | 2eb030dd83 | ||
|  | 365fe1930c | ||
|  | 342a677c3f | ||
|  | b5c41bcb3a | ||
|  | f578adceef | ||
|  | 1e01313612 | ||
|  | e64c343645 | ||
|  | b9effce7d6 | ||
|  | 45a13227de | 
							
								
								
									
										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/" | ||||
|           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/" | ||||
|   | ||||
|         - name: Download SDRPlay API | ||||
|           run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu" -OutFile ${{runner.workspace}}/SDRPlay.zip | ||||
|           run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu&confirm=t" -OutFile ${{runner.workspace}}/SDRPlay.zip | ||||
|  | ||||
|         - name: Install SDRPlay API | ||||
|           run: 7z x ${{runner.workspace}}/SDRPlay.zip -o"C:/Program Files/" | ||||
| @@ -58,14 +58,17 @@ 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 | ||||
|           run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb: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_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_PERSEUS_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 | ||||
| @@ -93,11 +96,8 @@ 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 rtl-sdr libbladerf codec2 zstd && pip3 install mako | ||||
|           run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && 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: git clone https://github.com/analogdevicesinc/libiio && cd libiio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ | ||||
|           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 ../../ | ||||
|  | ||||
|         - 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,9 +114,15 @@ 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_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_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release | ||||
|  | ||||
|         - name: Build | ||||
|           working-directory: ${{runner.workspace}}/build | ||||
| @@ -131,13 +137,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 | ||||
|  | ||||
| @@ -176,6 +182,28 @@ 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 | ||||
|  | ||||
| @@ -198,28 +226,6 @@ 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 | ||||
|  | ||||
| @@ -275,7 +281,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 | ||||
|           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 | ||||
|  | ||||
|         - name: Build | ||||
|           working-directory: ${{runner.workspace}}/build | ||||
| @@ -319,7 +325,7 @@ jobs: | ||||
|               path: ${{runner.workspace}}/sdrpp.apk | ||||
|  | ||||
|     create_full_archive: | ||||
|         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'] | ||||
|         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'] | ||||
|         runs-on: ubuntu-latest | ||||
|  | ||||
|         steps: | ||||
| @@ -333,6 +339,7 @@ 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,24 +17,25 @@ 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_SDRPP_SERVER_SOURCE "Build SDR++ Server Source Module (no dependencies required)" ON) | ||||
| 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_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) | ||||
| @@ -141,9 +142,13 @@ if (OPT_BUILD_LIMESDR_SOURCE) | ||||
| add_subdirectory("source_modules/limesdr_source") | ||||
| endif (OPT_BUILD_LIMESDR_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_SDRPP_SERVER_SOURCE) | ||||
| add_subdirectory("source_modules/sdrpp_server_source") | ||||
| endif (OPT_BUILD_SDRPP_SERVER_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_RFSPACE_SOURCE) | ||||
| add_subdirectory("source_modules/rfspace_source") | ||||
| @@ -157,6 +162,10 @@ 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) | ||||
| @@ -177,10 +186,6 @@ 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,6 +1,7 @@ | ||||
| # Pull Requests | ||||
|  | ||||
| TODO | ||||
| **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.** | ||||
|  | ||||
| # Code Style | ||||
|  | ||||
| @@ -122,4 +123,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 | ||||
|   | ||||
| @@ -108,7 +108,6 @@ 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,6 +117,10 @@ 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; | ||||
| @@ -177,6 +181,8 @@ 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,6 +12,7 @@ namespace sdrpp_credits { | ||||
|         "Howard0su", | ||||
|         "John Donkersley", | ||||
|         "Joshua Kimsey", | ||||
|         "Manawyrm", | ||||
|         "Martin Hauke", | ||||
|         "Marvin Sinister", | ||||
|         "Maxime Biette", | ||||
| @@ -21,7 +22,6 @@ namespace sdrpp_credits { | ||||
|         "Shuyuan Liu", | ||||
|         "Syne Ardwin (WI9SYN)", | ||||
|         "Szymon Zakrent", | ||||
|         "Tobias Mädel", | ||||
|         "Youssef Touil", | ||||
|         "Zimm" | ||||
|     }; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ namespace dsp::convert { | ||||
|  | ||||
|         StereoToMono(stream<stereo_t>* in) { base_type::init(in); } | ||||
|          | ||||
|         static inline int process(int count, const stereo_t* in, float* out) { | ||||
|         inline int process(int count, const stereo_t* in, float* out) { | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 out[i] = (in[i].l + in[i].r) / 2.0f; | ||||
|             } | ||||
|   | ||||
| @@ -1,183 +0,0 @@ | ||||
| #pragma once | ||||
| #include "../processor.h" | ||||
| #include "../window/nuttall.h" | ||||
| #include <fftw3.h> | ||||
| #include "../convert/stereo_to_mono.h" | ||||
|  | ||||
| namespace dsp::noise_reduction { | ||||
|     class Audio : public Processor<stereo_t, stereo_t> { | ||||
|         using base_type = Processor<stereo_t, stereo_t>; | ||||
|     public: | ||||
|         Audio() {} | ||||
|  | ||||
|         Audio(stream<stereo_t>* in, int bins) { init(in, bins); } | ||||
|  | ||||
|         ~Audio() { | ||||
|             if (!base_type::_block_init) { return; } | ||||
|             base_type::stop(); | ||||
|             destroyBuffers(); | ||||
|         } | ||||
|  | ||||
|         void init(stream<stereo_t>* in, int bins) { | ||||
|             _bins = bins; | ||||
|             complexBins = (bins / 2) + 1; | ||||
|             normFactor = 1.0f / (float)_bins; | ||||
|             initBuffers(); | ||||
|             base_type::init(in); | ||||
|         } | ||||
|  | ||||
|         void setBins(int bins) { | ||||
|             assert(base_type::_block_init); | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             base_type::tempStop(); | ||||
|             _bins = bins; | ||||
|             complexBins = (bins / 2) + 1; | ||||
|             normFactor = 1.0f / (float)_bins; | ||||
|             destroyBuffers(); | ||||
|             initBuffers(); | ||||
|             base_type::tempStart(); | ||||
|         } | ||||
|  | ||||
|         void setLevel(float level) { | ||||
|             _level = powf(10.0f, level * 0.1f); | ||||
|         } | ||||
|  | ||||
|         void reset() { | ||||
|             assert(base_type::_block_init); | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             base_type::tempStop(); | ||||
|             buffer::clear(buffer, _bins - 1); | ||||
|             buffer::clear(backFFTIn, _bins); | ||||
|             buffer::clear(noisePrint, _bins); | ||||
|             base_type::tempStart(); | ||||
|         } | ||||
|  | ||||
|         int process(int count, const stereo_t* in, stereo_t* out) { | ||||
|             // Write new input data to buffer | ||||
|             convert::StereoToMono::process(count, in, bufferStart); | ||||
|              | ||||
|             // Iterate the FFT | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 // Apply windows | ||||
|                 volk_32f_x2_multiply_32f(forwFFTIn, &buffer[i], fftWin, _bins); | ||||
|  | ||||
|                 // Do forward FFT | ||||
|                 fftwf_execute(forwardPlan); | ||||
|  | ||||
|                 // Get bin amplitude and square to get power | ||||
|                 volk_32fc_magnitude_32f(ampBuf, (lv_32fc_t*)forwFFTOut, complexBins); | ||||
|  | ||||
|                 // Update noise print using a running average | ||||
|                 volk_32f_s32f_multiply_32f(scaledAmps, ampBuf, alpha, complexBins); | ||||
|                 volk_32f_s32f_multiply_32f(noisePrint, noisePrint, beta, complexBins); | ||||
|                 volk_32f_x2_add_32f(noisePrint, noisePrint, scaledAmps, complexBins); | ||||
|  | ||||
|                 // Clamp amplitudes | ||||
|                 volk_32f_x2_max_32f(ampBuf, ampBuf, noisePrint, complexBins); | ||||
|  | ||||
|                 // Compute Wiener (funny) filter | ||||
|                 volk_32f_x2_subtract_32f(scaledAmps, ampBuf, noisePrint, complexBins); | ||||
|                 volk_32f_x2_divide_32f(scaledAmps, scaledAmps, ampBuf, complexBins); | ||||
|  | ||||
|                 // Apply wiener filter to bins | ||||
|                 volk_32fc_32f_multiply_32fc((lv_32fc_t*)backFFTIn, (lv_32fc_t*)forwFFTOut, scaledAmps, complexBins); | ||||
|  | ||||
|                 // Do reverse FFT and get first element | ||||
|                 fftwf_execute(backwardPlan); | ||||
|                 out[i].l = backFFTOut[_bins / 2]; | ||||
|                 out[i].r = backFFTOut[_bins / 2]; | ||||
|             } | ||||
|  | ||||
|             // Correct amplitude | ||||
|             volk_32f_s32f_multiply_32f((float*)out, (float*)out, normFactor, count*2); | ||||
|  | ||||
|             // Move buffer buffer | ||||
|             memmove(buffer, &buffer[count], (_bins - 1) * sizeof(float)); | ||||
|  | ||||
|             return count; | ||||
|         } | ||||
|  | ||||
|         int run() { | ||||
|             int count = base_type::_in->read(); | ||||
|             if (count < 0) { return -1; } | ||||
|  | ||||
|             process(count, base_type::_in->readBuf, base_type::out.writeBuf); | ||||
|  | ||||
|             // Swap if some data was generated | ||||
|             base_type::_in->flush(); | ||||
|             if (!base_type::out.swap(count)) { return -1; } | ||||
|             return count; | ||||
|         } | ||||
|  | ||||
|     protected: | ||||
|         void initBuffers() { | ||||
|             // Allocate FFT buffers | ||||
|             forwFFTIn = (float*)fftwf_malloc(_bins * sizeof(float)); | ||||
|             forwFFTOut = (complex_t*)fftwf_malloc(_bins * sizeof(complex_t)); | ||||
|             backFFTIn = (complex_t*)fftwf_malloc(_bins * sizeof(complex_t)); | ||||
|             backFFTOut = (float*)fftwf_malloc(_bins * sizeof(float)); | ||||
|  | ||||
|             // Allocate and clear delay buffer | ||||
|             buffer = buffer::alloc<float>(STREAM_BUFFER_SIZE + 64000); | ||||
|             bufferStart = &buffer[_bins - 1]; | ||||
|             buffer::clear(buffer, _bins - 1); | ||||
|  | ||||
|             // Clear backward FFT input | ||||
|             buffer::clear(backFFTIn, _bins); | ||||
|  | ||||
|             // Allocate amplitude buffer | ||||
|             ampBuf = buffer::alloc<float>(_bins); | ||||
|             scaledAmps = buffer::alloc<float>(_bins); | ||||
|             noisePrint = buffer::alloc<float>(_bins); | ||||
|             buffer::clear(noisePrint, _bins); | ||||
|  | ||||
|             // Allocate and generate Window | ||||
|             fftWin = buffer::alloc<float>(_bins); | ||||
|             for (int i = 0; i < _bins; i++) { fftWin[i] = window::nuttall(i, _bins - 1); } | ||||
|  | ||||
|             // Plan FFTs | ||||
|             forwardPlan = fftwf_plan_dft_r2c_1d(_bins, forwFFTIn, (fftwf_complex*)forwFFTOut, FFTW_ESTIMATE); | ||||
|             backwardPlan = fftwf_plan_dft_c2r_1d(_bins, (fftwf_complex*)backFFTIn, backFFTOut, FFTW_ESTIMATE); | ||||
|         } | ||||
|  | ||||
|         void destroyBuffers() { | ||||
|             fftwf_destroy_plan(forwardPlan); | ||||
|             fftwf_destroy_plan(backwardPlan); | ||||
|             fftwf_free(forwFFTIn); | ||||
|             fftwf_free(forwFFTOut); | ||||
|             fftwf_free(backFFTIn); | ||||
|             fftwf_free(backFFTOut); | ||||
|             buffer::free(buffer); | ||||
|             buffer::free(ampBuf); | ||||
|             buffer::free(scaledAmps); | ||||
|             buffer::free(noisePrint); | ||||
|             buffer::free(fftWin); | ||||
|         } | ||||
|  | ||||
|         float _level = 0.0f; | ||||
|  | ||||
|         float* forwFFTIn; | ||||
|         complex_t* forwFFTOut; | ||||
|         complex_t* backFFTIn; | ||||
|         float* backFFTOut; | ||||
|  | ||||
|         fftwf_plan forwardPlan; | ||||
|         fftwf_plan backwardPlan; | ||||
|  | ||||
|         float* buffer; | ||||
|         float* bufferStart; | ||||
|  | ||||
|         float* fftWin; | ||||
|  | ||||
|         float* ampBuf; | ||||
|         float* scaledAmps; | ||||
|         float* noisePrint; | ||||
|  | ||||
|         int _bins; | ||||
|         int complexBins; | ||||
|         float normFactor = 1.0f; | ||||
|  | ||||
|         float alpha = 0.0001f; | ||||
|         float beta = 0.9999f; | ||||
|     };  | ||||
| } | ||||
| @@ -37,17 +37,21 @@ namespace dsp::noise_reduction { | ||||
|  | ||||
|         inline int process(int count, complex_t* in, complex_t* out) { | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 // Get signal amplitude and pass value if null | ||||
|                 // Get signal amplitude | ||||
|                 float inAmp = in[i].amplitude(); | ||||
|                 if (!inAmp) { | ||||
|                     out[i] = in[i]; | ||||
|  | ||||
|                 // Update average amplitude | ||||
|                 float gain = 1.0f; | ||||
|                 if (inAmp != 0.0f) { | ||||
|                     amp = (amp * _invRate) + (inAmp * _rate); | ||||
|                     float excess = inAmp / amp; | ||||
|                     if (excess > _level) { | ||||
|                         gain = 1.0f / excess; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // Update running average of amplitude | ||||
|                 amp = (_rate*inAmp) + (_invRate*amp); | ||||
|  | ||||
|                 // Null out if spike (Note: ideally, it should try to guess the real data) | ||||
|                 out[i] = (inAmp > _level*amp) ? complex_t{0.0f,0.0f} : in[i]; | ||||
|                  | ||||
|                 // Scale output by gain | ||||
|                 out[i] = in[i] * gain; | ||||
|             } | ||||
|             return count; | ||||
|         } | ||||
|   | ||||
| @@ -82,7 +82,7 @@ namespace dsp { | ||||
|  | ||||
|         inline float fastAmplitude() { | ||||
|             float re_abs = fabsf(re); | ||||
|             float im_abs = fabsf(re); | ||||
|             float im_abs = fabsf(im); | ||||
|             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,10 +574,22 @@ 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 + (vfo->snapInterval * wheel); | ||||
|                 nfreq = roundl(nfreq / vfo->snapInterval) * vfo->snapInterval; | ||||
|                 nfreq = gui::waterfall.getCenterFrequency() + vfo->generalOffset + (interval * wheel); | ||||
|                 nfreq = roundl(nfreq / interval) * interval; | ||||
|             } | ||||
|             else { | ||||
|                 nfreq = gui::waterfall.getCenterFrequency() - (gui::waterfall.getViewBandwidth() * wheel / 20.0); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <gui/style.h> | ||||
| #include <utils/optionlist.h> | ||||
| #include <algorithm> | ||||
|  | ||||
| namespace displaymenu { | ||||
|     bool showWaterfall; | ||||
| @@ -22,6 +23,10 @@ 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; | ||||
|  | ||||
| @@ -57,8 +62,10 @@ namespace displaymenu { | ||||
|         IQFrontEnd::FFTWindow::NUTTALL | ||||
|     }; | ||||
|  | ||||
|     void updateFFTHoldSpeed() { | ||||
|         gui::waterfall.setFFTHoldSpeed(fftHoldSpeed / (fftRate * 10.0f)); | ||||
|     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 init() { | ||||
| @@ -104,7 +111,13 @@ namespace displaymenu { | ||||
|         fftHold = core::configManager.conf["fftHold"]; | ||||
|         fftHoldSpeed = core::configManager.conf["fftHoldSpeed"]; | ||||
|         gui::waterfall.setFFTHold(fftHold); | ||||
|         updateFFTHoldSpeed(); | ||||
|         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(); | ||||
|  | ||||
|         // Define and load UI scales | ||||
|         uiScales.define(1.0f, "100%", 1.0f); | ||||
| @@ -144,16 +157,47 @@ namespace displaymenu { | ||||
|             core::configManager.conf["fftHold"] = fftHold; | ||||
|             core::configManager.release(true); | ||||
|         } | ||||
|  | ||||
|         ImGui::LeftLabel("FFT Hold Speed"); | ||||
|         ImGui::SameLine(); | ||||
|         ImGui::FillWidth(); | ||||
|         if (ImGui::InputInt("##sdrpp_fft_hold_speed", &fftHoldSpeed)) { | ||||
|             updateFFTHoldSpeed(); | ||||
|             updateFFTSpeeds(); | ||||
|             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)) { | ||||
| @@ -168,7 +212,7 @@ namespace displaymenu { | ||||
|         if (ImGui::InputInt("##sdrpp_fft_rate", &fftRate, 1, 10)) { | ||||
|             fftRate = std::max<int>(1, fftRate); | ||||
|             sigpath::iqFrontEnd.setFFTRate(fftRate); | ||||
|             updateFFTHoldSpeed(); | ||||
|             updateFFTSpeeds(); | ||||
|             core::configManager.acquire(); | ||||
|             core::configManager.conf["fftRate"] = fftRate; | ||||
|             core::configManager.release(true); | ||||
| @@ -210,4 +254,4 @@ namespace displaymenu { | ||||
|             ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Restart required."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <imgui.h> | ||||
| #include <string> | ||||
| #include <vector> | ||||
| @@ -144,4 +145,4 @@ namespace SmGui { | ||||
|     // Config configs | ||||
|     void ForceSyncForNext(); | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -689,6 +689,7 @@ 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; | ||||
| @@ -740,14 +741,23 @@ 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.0; // Hide everything | ||||
|             latestFFTHold[i] = -1000.0; | ||||
|             latestFFT[i] = -1000.0f; // Hide everything | ||||
|             latestFFTHold[i] = -1000.0f; | ||||
|         } | ||||
|  | ||||
|         fftAreaMin = ImVec2(widgetPos.x + (50.0f * style::uiScale), widgetPos.y + (9.0f * style::uiScale)); | ||||
| @@ -873,9 +883,25 @@ 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; | ||||
|             calculateVFOSignalInfo(waterfallVisible ? &rawFFTs[currentFFTLine * rawFFTSize] : rawFFTs, vfos[selectedVFO], dummy, selectedVFOSNR); | ||||
|             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); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // If FFT hold is enabled, update it | ||||
| @@ -1110,6 +1136,45 @@ 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,6 +169,12 @@ 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(); | ||||
|  | ||||
| @@ -182,7 +188,7 @@ namespace ImGui { | ||||
|         bool mouseInFFT = false; | ||||
|         bool mouseInWaterfall = false; | ||||
|  | ||||
|         float selectedVFOSNR = NAN; | ||||
|         float selectedVFOSNR = 0.0f; | ||||
|  | ||||
|         bool centerFrequencyLocked = false; | ||||
|  | ||||
| @@ -270,6 +276,7 @@ namespace ImGui { | ||||
|         std::recursive_mutex buf_mtx; | ||||
|         std::recursive_mutex latestFFTMtx; | ||||
|         std::mutex texMtx; | ||||
|         std::mutex smoothingBufMtx; | ||||
|  | ||||
|         float vRange; | ||||
|  | ||||
| @@ -304,8 +311,9 @@ namespace ImGui { | ||||
|         //std::vector<std::vector<float>> rawFFTs; | ||||
|         int rawFFTSize; | ||||
|         float* rawFFTs = NULL; | ||||
|         float* latestFFT; | ||||
|         float* latestFFTHold; | ||||
|         float* latestFFT = NULL; | ||||
|         float* latestFFTHold = NULL; | ||||
|         float* smoothingBuf = NULL; | ||||
|         int currentFFTLine = 0; | ||||
|         int fftLines = 0; | ||||
|  | ||||
| @@ -325,6 +333,14 @@ 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}.", path); | ||||
|         flog::error("Couldn't load {0}: {1}", path, dlerror()); | ||||
|         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::mutex> lck(mtx); | ||||
|     std::lock_guard<std::recursive_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::mutex> lck(mtx); | ||||
|     std::lock_guard<std::recursive_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::mutex> lck(mtx); | ||||
|     std::lock_guard<std::recursive_mutex> lck(mtx); | ||||
|     if (interfaces.find(name) == interfaces.end()) { return false; } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| std::string ModuleComManager::getModuleName(std::string name) { | ||||
|     std::lock_guard<std::mutex> lck(mtx); | ||||
|     std::lock_guard<std::recursive_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::mutex> lck(mtx); | ||||
|     std::lock_guard<std::recursive_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::mutex mtx; | ||||
|     std::recursive_mutex mtx; | ||||
|     std::map<std::string, ModuleComInterface> interfaces; | ||||
| }; | ||||
| @@ -1,6 +1,7 @@ | ||||
| #include "net.h" | ||||
| #include <string.h> | ||||
| #include <codecvt> | ||||
| #include <stdexcept> | ||||
|  | ||||
| #ifdef _WIN32 | ||||
| #define WOULD_BLOCK (WSAGetLastError() == WSAEWOULDBLOCK) | ||||
| @@ -403,4 +404,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,5 +1,6 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <string> | ||||
| #include <mutex> | ||||
| #include <memory> | ||||
| #include <map> | ||||
|   | ||||
							
								
								
									
										52
									
								
								core/src/utils/new_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								core/src/utils/new_event.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| #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; | ||||
| }; | ||||
| @@ -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 | ||||
|         // If parent chunk, increment its size by the size of the sub-chunk plus the size of its header) | ||||
|         if (!chunks.empty()) { | ||||
|             chunks.top().hdr.size += desc.hdr.size; | ||||
|             chunks.top().hdr.size += desc.hdr.size + sizeof(ChunkHeader); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -57,10 +57,13 @@ 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, 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); | ||||
|         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); | ||||
|         split.init(&demod.out); | ||||
|         split.bindStream(&symSinkStream); | ||||
|         split.bindStream(&sinkStream); | ||||
| @@ -99,6 +102,7 @@ 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(); | ||||
| @@ -151,6 +155,13 @@ 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) { | ||||
| @@ -245,7 +256,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, double omegaGain, double muGain, double omegaRelLimit = 0.01) { | ||||
|             init(in, symbolrate, samplerate, rrcTapCount, rrcBeta, agcRate, costasBandwidth, brokenModulation, omegaGain, muGain); | ||||
|         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() { | ||||
| @@ -21,11 +21,12 @@ 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, 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, bool oqpsk, 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); | ||||
| @@ -129,6 +130,12 @@ 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); | ||||
| @@ -144,6 +151,18 @@ 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); | ||||
|         } | ||||
|  | ||||
| @@ -166,6 +185,8 @@ 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; | ||||
|   | ||||
| @@ -45,7 +45,6 @@ namespace demod { | ||||
|         virtual int getDefaultDeemphasisMode() = 0; | ||||
|         virtual bool getFMIFNRAllowed() = 0; | ||||
|         virtual bool getNBAllowed() = 0; | ||||
|         virtual bool getAFNRAllowed() = 0; | ||||
|         virtual dsp::stream<dsp::stereo_t>* getOutput() = 0; | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -86,7 +86,6 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return false; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -92,7 +92,6 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return false; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -79,7 +79,6 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return true; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -79,7 +79,6 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return true; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -75,7 +75,6 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return true; } | ||||
|         bool getNBAllowed() { return false; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -59,7 +59,6 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return true; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -80,7 +80,6 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } | ||||
|         bool getFMIFNRAllowed() { return false; } | ||||
|         bool getNBAllowed() { return true; } | ||||
|         bool getAFNRAllowed() { return true; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -130,7 +130,6 @@ namespace demod { | ||||
|         int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; } | ||||
|         bool getFMIFNRAllowed() { return true; } | ||||
|         bool getNBAllowed() { return false; } | ||||
|         bool getAFNRAllowed() { return false; } | ||||
|         dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } | ||||
|  | ||||
|         // ============= DEDICATED FUNCTIONS ============= | ||||
|   | ||||
| @@ -9,7 +9,6 @@ | ||||
| #include <dsp/noise_reduction/noise_blanker.h> | ||||
| #include <dsp/noise_reduction/fm_if.h> | ||||
| #include <dsp/noise_reduction/squelch.h> | ||||
| #include <dsp/noise_reduction/audio.h> | ||||
| #include <dsp/multirate/rational_resampler.h> | ||||
| #include <dsp/filter/deephasis.h> | ||||
| #include <core.h> | ||||
| @@ -84,11 +83,9 @@ public: | ||||
|  | ||||
|         resamp.init(NULL, 250000.0, 48000.0); | ||||
|         deemp.init(NULL, 50e-6, 48000.0); | ||||
|         afNR.init(NULL, 1024); | ||||
|  | ||||
|         afChain.addBlock(&resamp, true); | ||||
|         afChain.addBlock(&deemp, false); | ||||
|         afChain.addBlock(&afNR, false); | ||||
|  | ||||
|         // Initialize the sink | ||||
|         srChangeHandler.ctx = this; | ||||
| @@ -250,12 +247,6 @@ private: | ||||
|             if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); } | ||||
|         } | ||||
|          | ||||
|         // Noise reduction | ||||
|         if (_this->afNRAllowed) { | ||||
|             if (ImGui::Checkbox(("Audio Noise Reduction##_radio_afnr_ena_" + _this->name).c_str(), &_this->afNREnabled)) { | ||||
|                 _this->setAFNREnabled(_this->afNREnabled); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Squelch | ||||
|         if (ImGui::Checkbox(("Squelch##_radio_sqelch_ena_" + _this->name).c_str(), &_this->squelchEnabled)) { | ||||
| @@ -379,8 +370,6 @@ private: | ||||
|         fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE); | ||||
|         nbAllowed = selectedDemod->getNBAllowed(); | ||||
|         nbEnabled = false; | ||||
|         afNRAllowed = selectedDemod->getAFNRAllowed(); | ||||
|         afNREnabled = false; | ||||
|         nbLevel = 0.0f; | ||||
|         double ifSamplerate = selectedDemod->getIFSampleRate(); | ||||
|         config.acquire(); | ||||
| @@ -422,9 +411,6 @@ private: | ||||
|         if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerLevel")) { | ||||
|             nbLevel = config.conf[name][selectedDemod->getName()]["noiseBlankerLevel"]; | ||||
|         } | ||||
|         if (config.conf[name][selectedDemod->getName()].contains("audioNoiseReductionEnabled")) { | ||||
|             nbEnabled = config.conf[name][selectedDemod->getName()]["audioNoiseReductionEnabled"]; | ||||
|         } | ||||
|         config.release(); | ||||
|  | ||||
|         // Configure VFO | ||||
| @@ -460,10 +446,7 @@ private: | ||||
|             afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); }); | ||||
|  | ||||
|             // Configure deemphasis | ||||
|             setDeemphasisMode(deempAllowed ? deempModes[deempId] : DEEMP_MODE_NONE); | ||||
|  | ||||
|             // Configure AF NR | ||||
|             setAFNREnabled(afNRAllowed && afNREnabled); | ||||
|             setDeemphasisMode(deempModes[deempId]); | ||||
|         } | ||||
|         else { | ||||
|             // Disable everything if post processing is disabled | ||||
| @@ -525,17 +508,6 @@ private: | ||||
|         config.release(true); | ||||
|     } | ||||
|  | ||||
|     void setAFNREnabled(bool enable) { | ||||
|         afNREnabled = enable; | ||||
|         if (!postProcEnabled || !selectedDemod) { return; } | ||||
|         afChain.setBlockEnabled(&afNR, afNREnabled, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); }); | ||||
|  | ||||
|         // Save config | ||||
|         config.acquire(); | ||||
|         config.conf[name][selectedDemod->getName()]["audioNoiseReductionEnabled"] = nbEnabled; | ||||
|         config.release(true); | ||||
|     } | ||||
|  | ||||
|     void setNBEnabled(bool enable) { | ||||
|         nbEnabled = enable; | ||||
|         if (!selectedDemod) { return; } | ||||
| @@ -688,7 +660,6 @@ private: | ||||
|     dsp::chain<dsp::stereo_t> afChain; | ||||
|     dsp::multirate::RationalResampler<dsp::stereo_t> resamp; | ||||
|     dsp::filter::Deemphasis<dsp::stereo_t> deemp; | ||||
|     dsp::noise_reduction::Audio afNR; | ||||
|  | ||||
|     SinkManager::Stream stream; | ||||
|  | ||||
| @@ -712,9 +683,6 @@ private: | ||||
|     int deempId = 0; | ||||
|     bool deempAllowed; | ||||
|  | ||||
|     bool afNREnabled = false; | ||||
|     bool afNRAllowed; | ||||
|  | ||||
|     bool FMIFNRAllowed; | ||||
|     bool FMIFNREnabled = false; | ||||
|     int fmIFPresetId; | ||||
|   | ||||
							
								
								
									
										4
									
								
								docker_builds/debian_bookworm/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docker_builds/debian_bookworm/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| FROM debian:bookworm | ||||
| ENV DEBIAN_FRONTEND=noninteractive  | ||||
| COPY do_build.sh /root | ||||
| RUN chmod +x /root/do_build.sh | ||||
							
								
								
									
										35
									
								
								docker_builds/debian_bookworm/do_build.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								docker_builds/debian_bookworm/do_build.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| #!/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 | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run | ||||
| @@ -15,10 +15,20 @@ 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 | ||||
| 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 .. | ||||
|   | ||||
| @@ -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 | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run | ||||
| @@ -15,10 +15,20 @@ 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 | ||||
| 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 | ||||
| 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 libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \ | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-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 | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run | ||||
| @@ -15,11 +15,21 @@ 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 | ||||
| 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' | ||||
| sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-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 | ||||
|             libcodec2-dev libudev-dev autoconf libtool xxd | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run | ||||
| @@ -41,6 +41,16 @@ 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 | ||||
| @@ -56,7 +66,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 | ||||
| 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 | ||||
| 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 | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run | ||||
| @@ -15,10 +15,20 @@ 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 | ||||
| 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 .. | ||||
|   | ||||
| @@ -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 | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run | ||||
| @@ -15,10 +15,20 @@ 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 | ||||
| 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 .. | ||||
|   | ||||
| @@ -81,7 +81,7 @@ bundle_find_full_path() { | ||||
|  | ||||
|         # Correct dep path | ||||
|         echo $RPATH/$RPATH_NEXT | ||||
|         return | ||||
|         return -1 | ||||
|     done | ||||
|  | ||||
|     # Search other common paths | ||||
|   | ||||
| @@ -38,6 +38,7 @@ 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 | ||||
| @@ -62,6 +63,7 @@ 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,6 +32,9 @@ 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,4 +5,5 @@ file(GLOB SRC "src/*.cpp") | ||||
|  | ||||
| include(${SDRPP_MODULE_CMAKE}) | ||||
|  | ||||
| target_include_directories(recorder PRIVATE "src/") | ||||
| target_include_directories(recorder PRIVATE "src/") | ||||
| target_include_directories(recorder PRIVATE "../../decoder_modules/radio/src") | ||||
| @@ -21,6 +21,7 @@ | ||||
| #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()) | ||||
|  | ||||
| @@ -168,7 +169,7 @@ public: | ||||
|  | ||||
|         // Open file | ||||
|         std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband"; | ||||
|         std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? gui::waterfall.selectedVFO : ""; | ||||
|         std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : ""; | ||||
|         std::string extension = ".wav"; | ||||
|         std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension); | ||||
|         if (!writer.open(expandedPath)) { | ||||
| @@ -437,6 +438,17 @@ 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); | ||||
| @@ -455,6 +467,7 @@ 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); | ||||
| @@ -462,6 +475,11 @@ 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); | ||||
| @@ -472,6 +490,7 @@ 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; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -333,6 +333,17 @@ 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; | ||||
| @@ -442,38 +453,18 @@ private: | ||||
|                 pos++; | ||||
|             } | ||||
|  | ||||
|             const std::string& newModeStr = parts[1]; | ||||
|             float newBandwidth = std::atoi(parts[2].c_str()); | ||||
|  | ||||
|             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 { | ||||
|              | ||||
|             auto it = std::find_if(radioModeToString.begin(), radioModeToString.end(), [&newModeStr](const auto& e) { | ||||
|                 return e.second == newModeStr; | ||||
|             }); | ||||
|             if (it == radioModeToString.end()) { | ||||
|                 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) { | ||||
| @@ -492,31 +483,9 @@ private: | ||||
|             if (!selectedVfo.empty() && core::modComManager.getModuleName(selectedVfo) == "radio") { | ||||
|                 int mode; | ||||
|                 core::modComManager.callInterface(selectedVfo, RADIO_IFACE_CMD_GET_MODE, NULL, &mode); | ||||
|  | ||||
|                 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"; | ||||
|                 } | ||||
|                 resp = std::string(radioModeToString[mode]) + "\n"; | ||||
|             } | ||||
|  | ||||
|             if (!selectedVfo.empty()) { | ||||
|             else if (!selectedVfo.empty()) { | ||||
|                 resp += std::to_string((int)sigpath::vfoManager.getBandwidth(selectedVfo)) + "\n"; | ||||
|             } | ||||
|             else { | ||||
| @@ -690,6 +659,11 @@ 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); | ||||
|   | ||||
							
								
								
									
										29
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								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 libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev | ||||
| sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev | ||||
| sudo dpkg -i sdrpp_debian_amd64.deb | ||||
| ``` | ||||
|  | ||||
| @@ -52,7 +52,9 @@ If `libvolk2-dev` is not available, use `libvolk1-dev`. | ||||
|  | ||||
| ### Arch-based | ||||
|  | ||||
| Install the latest release from the [sdrpp-git](https://aur.archlinux.org/packages/sdrpp-git/) AUR package | ||||
| Install from source following the instructions below. | ||||
|  | ||||
| **WARNING: The sdrpp-git AUR package is no longer official, it is not recommended to use it.** | ||||
|  | ||||
| ### Other | ||||
|  | ||||
| @@ -74,7 +76,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) | ||||
| * [PothosSDR](https://github.com/pothosware/PothosSDR) (This will install libraries for most SDRs. You have to install it in `C:/Program Files/PothosSDR`) | ||||
| * [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: | ||||
| @@ -113,16 +115,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 -s | ||||
| ./build/Release/sdrpp.exe -r root_dev -c | ||||
| ``` | ||||
|  | ||||
| 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 -s | ||||
| ./sdrpp.exe -r ../../root_dev -c | ||||
| ``` | ||||
|  | ||||
| The optional `-s` argument is for keeping the console active in order to see the error messages. | ||||
| The optional `-c` 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. | ||||
| @@ -302,7 +304,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_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_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 | ||||
| make -j<N> | ||||
| ``` | ||||
|  | ||||
| @@ -327,11 +329,12 @@ 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       | ✅              | ✅                     | ✅                         | | ||||
| @@ -348,7 +351,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_audio_sink | Working    | -            | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔              | ✅              | ✅ (Android only)          | | ||||
| | 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 | ⛔              | ✅              | ⛔                         | | ||||
| @@ -374,9 +377,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 | ||||
| @@ -470,6 +473,7 @@ 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) | ||||
| @@ -479,7 +483,6 @@ 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) | ||||
|  | ||||
|   | ||||
| @@ -149,6 +149,12 @@ | ||||
|       "start": 28000000, | ||||
|       "end": 29700000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "8m - Amateur", | ||||
|       "type": "amateur", | ||||
|       "start": 40660000, | ||||
|       "end": 40690000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "6m - Amateur", | ||||
|       "type": "amateur", | ||||
|   | ||||
| @@ -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": 27230000 | ||||
|             "end": 27410000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "10m - Radioamateur", | ||||
| @@ -493,4 +493,4 @@ | ||||
|             "end": 250000000000 | ||||
|         }		 | ||||
|     ] | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|     "name": "Germany", | ||||
|     "country_name": "Germany", | ||||
|     "country_code": "DE", | ||||
|     "author_name": "Tobias Mädel", | ||||
|     "author_name": "Manawyrm", | ||||
|     "author_url": "https://tbspace.de", | ||||
|     "bands": [ | ||||
|         { | ||||
|   | ||||
							
								
								
									
										3177
									
								
								root/res/bandplans/netherlands.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3177
									
								
								root/res/bandplans/netherlands.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -529,9 +529,9 @@ | ||||
|         }, | ||||
|         { | ||||
|             "name": "Train communications", | ||||
|             "type": "aviation", | ||||
|             "start": 151775000, | ||||
|             "end": 151875000 | ||||
|             "type": "railway", | ||||
|             "start": 151712500, | ||||
|             "end": 156012500 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Marine", | ||||
| @@ -557,12 +557,6 @@ | ||||
|             "start": 270000000, | ||||
|             "end": 380000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Train communications", | ||||
|             "type": "aviation", | ||||
|             "start": 299999000, | ||||
|             "end": 300001000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "70cm", | ||||
|             "type": "amateur", | ||||
|   | ||||
| @@ -5,6 +5,12 @@ | ||||
|     "author_name": "John Donkersley", | ||||
|     "author_url": "", | ||||
|     "bands": [ | ||||
|     { | ||||
|             "name": "2200m Ham Band", | ||||
|             "type": "amateur", | ||||
|             "start": 135700, | ||||
|             "end": 137800 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Long Wave", | ||||
|             "type": "broadcast", | ||||
| @@ -209,18 +215,18 @@ | ||||
|             "start": 15010000, | ||||
|             "end": 15100000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime", | ||||
|             "type": "marine", | ||||
|             "start": 16360000, | ||||
|             "end": 17410000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "19m Broadcast", | ||||
|             "type": "broadcast", | ||||
|             "start": 15100000, | ||||
|             "end": 15800000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Maritime", | ||||
|             "type": "marine", | ||||
|             "start": 16360000, | ||||
|             "end": 17410000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "16m Broadcast", | ||||
|             "type": "broadcast", | ||||
| @@ -345,7 +351,7 @@ | ||||
|             "name": "Air Band TACAN/ILS", | ||||
|             "type": "aviation", | ||||
|             "start": 108000000, | ||||
|             "end": 118000000 | ||||
|             "end": 117975000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Air Band Voice", | ||||
| @@ -371,6 +377,12 @@ | ||||
|             "start": 147343750, | ||||
|             "end": 147500000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Satellites", | ||||
|             "type": "satellite", | ||||
|             "start": 148000000, | ||||
|             "end": 150050000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Pagers - Flex/POCSAG", | ||||
|             "type": "PMR", | ||||
| @@ -425,6 +437,18 @@ | ||||
|             "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", | ||||
| @@ -462,22 +486,34 @@ | ||||
|             "end": 455000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Private Mobile Radio", | ||||
|             "name": "Private Mobile Radio inc OB", | ||||
|             "type": "PMR", | ||||
|             "start": 455000000, | ||||
|             "end": 467200000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Outside Broadcast Talkback", | ||||
|             "type": "PMR", | ||||
|             "start": 467200000, | ||||
|             "end": 468600000 | ||||
|             "end": 470000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Digital TV Broadcast", | ||||
|             "type": "broadcast", | ||||
|             "start": 470000000, | ||||
|             "end": 790000000 | ||||
|             "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 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Licence Exempt Short Range", | ||||
| @@ -485,12 +521,84 @@ | ||||
|             "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", | ||||
| @@ -498,10 +606,28 @@ | ||||
|             "end": 2302000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "13cm Ham Band", | ||||
|             "type": "amateur", | ||||
|             "start": 2310000000, | ||||
|             "end": 2450000000 | ||||
|             "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 | ||||
|         } | ||||
|     ] | ||||
| } | ||||
|             ] | ||||
| } | ||||
|   | ||||
| @@ -227,6 +227,18 @@ | ||||
|             "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", | ||||
|   | ||||
							
								
								
									
										14
									
								
								root/res/colormaps/smoke.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								root/res/colormaps/smoke.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| { | ||||
|     "name": "Smoke", | ||||
|     "author": "Yaroslav Andrianov", | ||||
|     "map": [ | ||||
|         "#FFFFFF", | ||||
|         "#EEEEEE", | ||||
|         "#CCCCCC", | ||||
|         "#777777", | ||||
|         "#555555", | ||||
|         "#333333", | ||||
|         "#111111", | ||||
|         "#000000" | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										28
									
								
								root/res/colormaps/temper_colors.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								root/res/colormaps/temper_colors.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| { | ||||
|     "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" | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										29
									
								
								root/res/colormaps/vivid.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								root/res/colormaps/vivid.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| { | ||||
|     "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"       | ||||
|     ] | ||||
| } | ||||
| @@ -1,22 +0,0 @@ | ||||
| #!/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,16 +45,20 @@ public: | ||||
|         int count = audio.getDeviceCount(); | ||||
|         RtAudio::DeviceInfo info; | ||||
|         for (int i = 0; i < count; i++) { | ||||
|             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'; | ||||
|             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()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         selectByName(device); | ||||
|     } | ||||
|  | ||||
| @@ -290,4 +294,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { | ||||
| MOD_EXPORT void _END_() { | ||||
|     config.disableAutoSave(); | ||||
|     config.save(); | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| #define NOMINMAX | ||||
| #include <imgui.h> | ||||
| #include <utils/flog.h> | ||||
| #include <module.h> | ||||
| @@ -9,6 +10,8 @@ | ||||
| #include <filesystem> | ||||
| #include <regex> | ||||
| #include <gui/tuner.h> | ||||
| #include <algorithm> | ||||
| #include <stdexcept> | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| @@ -121,6 +124,12 @@ 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(); | ||||
| @@ -130,7 +139,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(); | ||||
| @@ -144,8 +153,8 @@ private: | ||||
|  | ||||
|     static void worker(void* ctx) { | ||||
|         FileSourceModule* _this = (FileSourceModule*)ctx; | ||||
|         double sampleRate = _this->reader->getSampleRate(); | ||||
|         int blockSize = sampleRate / 200.0f; | ||||
|         double sampleRate = std::max(_this->reader->getSampleRate(), (uint32_t)1); | ||||
|         int blockSize = std::min((int)(sampleRate / 200.0f), (int)STREAM_BUFFER_SIZE); | ||||
|         int16_t* inBuf = new int16_t[blockSize * 2]; | ||||
|  | ||||
|         while (true) { | ||||
| @@ -159,8 +168,8 @@ private: | ||||
|  | ||||
|     static void floatWorker(void* ctx) { | ||||
|         FileSourceModule* _this = (FileSourceModule*)ctx; | ||||
|         double sampleRate = _this->reader->getSampleRate(); | ||||
|         int blockSize = sampleRate / 200.0f; | ||||
|         double sampleRate = std::max(_this->reader->getSampleRate(), (uint32_t)1); | ||||
|         int blockSize = std::min((int)(sampleRate / 200.0f), (int)STREAM_BUFFER_SIZE); | ||||
|         dsp::complex_t* inBuf = new dsp::complex_t[blockSize]; | ||||
|  | ||||
|         while (true) { | ||||
| @@ -214,4 +223,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { | ||||
| MOD_EXPORT void _END_() { | ||||
|     config.disableAutoSave(); | ||||
|     config.save(); | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -137,6 +137,10 @@ 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'; | ||||
|   | ||||
							
								
								
									
										29
									
								
								source_modules/perseus_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								source_modules/perseus_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| 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 () | ||||
							
								
								
									
										468
									
								
								source_modules/perseus_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										468
									
								
								source_modules/perseus_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,468 @@ | ||||
| #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(); | ||||
| } | ||||
| @@ -171,7 +171,7 @@ public: | ||||
| #ifndef __ANDROID__ | ||||
|         int oret = rtlsdr_open(&openDev, id); | ||||
| #else | ||||
|         int oret = rtlsdr_open_fd(&openDev, devFd); | ||||
|         int oret = rtlsdr_open_sys_dev(&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_fd(&_this->openDev, _this->devFd); | ||||
|         int oret = rtlsdr_open_sys_dev(&_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) / 128.0f; | ||||
|             _this->stream.writeBuf[i].im = (float)(buf[(i * 2) + 1] - 127) / 128.0f; | ||||
|             _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; | ||||
|         } | ||||
|         if (!_this->stream.swap(sampCount)) { return; } | ||||
|     } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ public: | ||||
|         this->name = name; | ||||
|  | ||||
|         strcpy(hostname, "localhost"); | ||||
|         sampleRate = 41000000.0; | ||||
|         sampleRate = 5750000.0; | ||||
|  | ||||
|         handler.ctx = this; | ||||
|         handler.selectHandler = menuSelected; | ||||
| @@ -103,8 +103,14 @@ private: | ||||
|  | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         SpectranHTTPSourceModule* _this = (SpectranHTTPSourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             // TODO | ||||
|         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); | ||||
|             } | ||||
|         } | ||||
|         _this->freq = freq; | ||||
|         flog::info("SpectranHTTPSourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
| @@ -138,6 +144,8 @@ 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(); } | ||||
| @@ -154,13 +162,28 @@ 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; | ||||
| @@ -168,11 +191,16 @@ 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 = 80; | ||||
|     int port = 54664; | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
|  | ||||
| }; | ||||
|   | ||||
| @@ -5,6 +5,8 @@ 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); | ||||
|  | ||||
| @@ -14,6 +16,13 @@ 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); | ||||
| } | ||||
| @@ -33,6 +42,27 @@ 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 | ||||
| @@ -52,6 +82,41 @@ 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); | ||||
| @@ -72,10 +137,11 @@ void SpectranHTTPClient::worker() { | ||||
|             i += read; | ||||
|             sampLen += read; | ||||
|         } | ||||
|         int sampCount = sampLen / 8; | ||||
|  | ||||
|         // Swap to stream | ||||
|         if (streamingEnabled) { | ||||
|             if (!stream->swap(sampLen / 8)) { return; } | ||||
|             if (!stream->swap(sampCount)) { return; } | ||||
|         } | ||||
|          | ||||
|         // Read trailing CRLF | ||||
|   | ||||
| @@ -4,22 +4,35 @@ | ||||
| #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; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user