mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 08:58:13 +01:00 
			
		
		
		
	Compare commits
	
		
			68 Commits
		
	
	
		
			new_sinks
			...
			fobos_test
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 17eccf5156 | ||
|  | acb1be121c | ||
|  | 0fa89614bb | ||
|  | 256affd918 | ||
|  | e80cdbf248 | ||
|  | 75e66226c3 | ||
|  | c2f0e756a5 | ||
|  | 79dd5bdcbb | ||
|  | 6dce28345c | ||
|  | fe9ac6c9a1 | ||
|  | bfdfa2b30b | ||
|  | 46e98b9b03 | ||
|  | e674a73771 | ||
|  | 118e1fbff0 | ||
|  | bcadb36232 | ||
|  | 554ba2f596 | ||
|  | 949fde022d | ||
|  | 123e34d250 | ||
|  | f1c7010437 | ||
|  | 33a7795de1 | ||
|  | fe7299c18a | ||
|  | 8a9e0abcc2 | ||
|  | 13abe4860b | ||
|  | 981592fa19 | ||
|  | 582750f79b | ||
|  | 36f2a083ce | ||
|  | d753135a61 | ||
|  | 07744e5bae | ||
|  | 9ec78da7ac | ||
|  | 0066994899 | ||
|  | 93ab51bf2f | ||
|  | f9d7d20073 | ||
|  | e81db5d85c | ||
|  | 5c3a66642b | ||
|  | 36492e799a | ||
|  | 0110dfbef6 | ||
|  | 0b5a2ff786 | ||
|  | ce0f1f05ae | ||
|  | 46a5ff8ac5 | ||
|  | 03559b928b | ||
|  | 206ce6e8c3 | ||
|  | d7a1f46af0 | ||
|  | 89e6e4f7ad | ||
|  | 6ced9b15c3 | ||
|  | 0de189a7b7 | ||
|  | bb9024fadd | ||
|  | d1dc20f4e2 | ||
|  | 309717b5f8 | ||
|  | 762444d340 | ||
|  | 18300e8916 | ||
|  | a93bb9d468 | ||
|  | ea0362b927 | ||
|  | ffc642f270 | ||
|  | 1b5975f563 | ||
|  | 733dc55723 | ||
|  | b841180f84 | ||
|  | e99e84e809 | ||
|  | 7a4281dd76 | ||
|  | c89763a989 | ||
|  | 27edc260c9 | ||
|  | 2ea7ac496f | ||
|  | 314d78d9d2 | ||
|  | 4e455e6661 | ||
|  | 58b86fcee5 | ||
|  | 27072e9fe7 | ||
|  | da1417b5ab | ||
|  | e60eca5d6d | ||
|  | ccb10bfb9a | 
							
								
								
									
										70
									
								
								.github/workflows/build_all.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								.github/workflows/build_all.yml
									
									
									
									
										vendored
									
									
								
							| @@ -36,6 +36,13 @@ jobs: | ||||
|           working-directory: ${{runner.workspace}} | ||||
|           run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/" | ||||
|   | ||||
|         - name: Download librtlsdr | ||||
|           run: Invoke-WebRequest -Uri "https://ftp.osmocom.org/binaries/windows/rtl-sdr/rtl-sdr-64bit-20240623.zip" -OutFile ${{runner.workspace}}/rtl-sdr.zip | ||||
|  | ||||
|         - name: Patch Pothos with newer librtlsdr version | ||||
|           working-directory: ${{runner.workspace}} | ||||
|           run: 7z x rtl-sdr.zip ; rm "C:/Program Files/PothosSDR/bin/rtlsdr.dll" ; cp "rtl-sdr-64bit-20240623/librtlsdr.dll" "C:/Program Files/PothosSDR/bin/rtlsdr.dll" | ||||
|  | ||||
|         - name: Download SDRPlay API | ||||
|           run: Invoke-WebRequest -Uri "https://www.sdrpp.org/SDRplay.zip" -OutFile ${{runner.workspace}}/SDRplay.zip | ||||
|  | ||||
| @@ -58,17 +65,23 @@ jobs: | ||||
|           run: mkdir "C:/Program Files/codec2" ; mkdir "C:/Program Files/codec2/include" ; mkdir "C:/Program Files/codec2/include/codec2" ; mkdir "C:/Program Files/codec2/lib" ; cd "codec2" ; xcopy "src" "C:/Program Files/codec2/include" ; cd "build" ; xcopy "src" "C:/Program Files/codec2/lib" ; xcopy "codec2" "C:/Program Files/codec2/include/codec2" | ||||
|  | ||||
|         - name: Install vcpkg dependencies | ||||
|           run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows | ||||
|           run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows spdlog: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" | ||||
|           run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake -DCMAKE_BUILD_TYPE=Release "-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: Install librfnm | ||||
|           run: git clone https://github.com/AlexandreRouma/librfnm ; cd librfnm ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install . | ||||
|  | ||||
|         - name: Install libfobos | ||||
|           run: git clone https://github.com/AlexandreRouma/libfobos ; cd libfobos ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install . | ||||
|  | ||||
|         - name: Prepare CMake | ||||
|           working-directory: ${{runner.workspace}}/build | ||||
|           run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON | ||||
|           run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$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 -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
|  | ||||
|         - name: Build | ||||
|           working-directory: ${{runner.workspace}}/build | ||||
| @@ -94,13 +107,13 @@ jobs: | ||||
|           run: cmake -E make_directory ${{runner.workspace}}/build | ||||
|  | ||||
|         - name: Install dependencies | ||||
|           run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako | ||||
|           run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako | ||||
|  | ||||
|         - name: Install volk | ||||
|           run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ | ||||
|  | ||||
|         - name: Install SDRplay API | ||||
|           run: wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.14.0.pkg && sudo installer -pkg SDRplayAPI-macos-installer-universal-3.14.0.pkg -target / | ||||
|           run: wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.15.0.pkg && sudo installer -pkg SDRplayAPI-macos-installer-universal-3.15.0.pkg -target / | ||||
|  | ||||
|         - name: Install libiio | ||||
|           run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ | ||||
| @@ -112,14 +125,20 @@ jobs: | ||||
|           run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -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 .. | ||||
|           run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && sudo make install && cd .. | ||||
|  | ||||
|         - name: Install librfnm | ||||
|           run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd .. | ||||
|  | ||||
|         - name: Install libfobos | ||||
|           run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo 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_OSX_DEPLOYMENT_TARGET=10.15 -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 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release | ||||
|           run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -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 -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release | ||||
|  | ||||
|         - name: Build | ||||
|           working-directory: ${{runner.workspace}}/build | ||||
| @@ -145,13 +164,13 @@ jobs: | ||||
|           run: cmake -E make_directory ${{runner.workspace}}/build | ||||
|  | ||||
|         - name: Install dependencies | ||||
|           run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako --break-system-packages | ||||
|           run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako --break-system-packages | ||||
|  | ||||
|         - name: Install volk | ||||
|           run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ | ||||
|  | ||||
|         - name: Install SDRplay API | ||||
|           run: wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.14.0.pkg && sudo installer -pkg SDRplayAPI-macos-installer-universal-3.14.0.pkg -target / | ||||
|           run: wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.15.0.pkg && sudo installer -pkg SDRplayAPI-macos-installer-universal-3.15.0.pkg -target / | ||||
|  | ||||
|         - name: Install libiio | ||||
|           run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ | ||||
| @@ -165,12 +184,18 @@ jobs: | ||||
|         # - 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 librfnm | ||||
|           run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd .. | ||||
|  | ||||
|         - name: Install libfobos | ||||
|           run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo 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_OSX_DEPLOYMENT_TARGET=10.15 -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 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -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=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release | ||||
|           run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -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=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release | ||||
|  | ||||
|         - name: Build | ||||
|           working-directory: ${{runner.workspace}}/build | ||||
| @@ -340,6 +365,28 @@ jobs: | ||||
|               name: sdrpp_ubuntu_mantic_amd64 | ||||
|               path: ${{runner.workspace}}/sdrpp_debian_amd64.deb | ||||
|  | ||||
|     build_ubuntu_noble: | ||||
|         runs-on: ubuntu-latest | ||||
|  | ||||
|         steps: | ||||
|         - uses: actions/checkout@v4 | ||||
|          | ||||
|         - name: Create Docker Image | ||||
|           run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_noble && 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@v4 | ||||
|           with: | ||||
|               name: sdrpp_ubuntu_noble_amd64 | ||||
|               path: ${{runner.workspace}}/sdrpp_debian_amd64.deb | ||||
|  | ||||
|     build_raspios_bullseye_armhf: | ||||
|         runs-on: ARM | ||||
|  | ||||
| @@ -395,7 +442,7 @@ jobs: | ||||
|               path: ${{runner.workspace}}/sdrpp.apk | ||||
|  | ||||
|     create_full_archive: | ||||
|         needs: ['build_windows', 'build_macos_intel', 'build_macos_arm', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_ubuntu_mantic', 'build_raspios_bullseye_armhf', 'build_android'] | ||||
|         needs: ['build_windows', 'build_macos_intel', 'build_macos_arm', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_ubuntu_mantic', 'build_ubuntu_noble', 'build_raspios_bullseye_armhf', 'build_android'] | ||||
|         runs-on: ubuntu-latest | ||||
|  | ||||
|         steps: | ||||
| @@ -415,6 +462,7 @@ jobs: | ||||
|             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 && | ||||
|             mv sdrpp_ubuntu_mantic_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_mantic_amd64.deb && | ||||
|             mv sdrpp_ubuntu_noble_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_noble_amd64.deb && | ||||
|             mv sdrpp_raspios_bullseye_armhf/sdrpp_debian_armhf.deb sdrpp_all/sdrpp_raspios_bullseye_armhf.deb && | ||||
|             mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk | ||||
|  | ||||
|   | ||||
| @@ -12,14 +12,18 @@ option(OPT_OVERRIDE_STD_FILESYSTEM "Use a local version of std::filesystem on sy | ||||
| option(OPT_BUILD_AIRSPY_SOURCE "Build Airspy Source Module (Dependencies: libairspy)" ON) | ||||
| option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Dependencies: libairspyhf)" ON) | ||||
| option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)" ON) | ||||
| option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF) | ||||
| option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF) | ||||
| option(OPT_BUILD_FILE_SOURCE "Wav file source" ON) | ||||
| option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF) | ||||
| option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON) | ||||
| option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF) | ||||
| 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_NETWORK_SOURCE "Build Network 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_RFNM_SOURCE "Build RFNM Source Module (Dependencies: librfnm)" OFF) | ||||
| 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) | ||||
| @@ -40,12 +44,14 @@ option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: port | ||||
|  | ||||
| # Decoders | ||||
| option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF) | ||||
| option(OPT_BUILD_DAB_DECODER "Build the DAB/DAB+ decoder (no dependencies required)" OFF) | ||||
| option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF) | ||||
| option(OPT_BUILD_KG_SSTV_DECODER "Build the KG SSTV (KG-STV) decoder module (no dependencies required)" OFF) | ||||
| option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (Dependencies: codec2)" OFF) | ||||
| option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON) | ||||
| option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON) | ||||
| option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON) | ||||
| option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF) | ||||
| option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF) | ||||
|  | ||||
| # Misc | ||||
| @@ -61,6 +67,7 @@ option(OPT_BUILD_SCHEDULER "Build the scheduler" OFF) | ||||
| # Other options | ||||
| option(USE_INTERNAL_LIBCORRECT "Use an internal version of libcorrect" ON) | ||||
| option(USE_BUNDLE_DEFAULTS "Set the default resource and module directories to the right ones for a MacOS .app" OFF) | ||||
| option(COPY_MSVC_REDISTRIBUTABLES "Copy over the Visual C++ Redistributable" OFF) | ||||
|  | ||||
| # Module cmake path | ||||
| set(SDRPP_MODULE_CMAKE "${CMAKE_SOURCE_DIR}/sdrpp_module.cmake") | ||||
| @@ -125,6 +132,10 @@ if (OPT_BUILD_AUDIO_SOURCE) | ||||
| add_subdirectory("source_modules/audio_source") | ||||
| endif (OPT_BUILD_AUDIO_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_BADGESDR_SOURCE) | ||||
| add_subdirectory("source_modules/badgesdr_source") | ||||
| endif (OPT_BUILD_BADGESDR_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_BLADERF_SOURCE) | ||||
| add_subdirectory("source_modules/bladerf_source") | ||||
| endif (OPT_BUILD_BLADERF_SOURCE) | ||||
| @@ -133,10 +144,18 @@ if (OPT_BUILD_FILE_SOURCE) | ||||
| add_subdirectory("source_modules/file_source") | ||||
| endif (OPT_BUILD_FILE_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_FOBOSSDR_SOURCE) | ||||
| add_subdirectory("source_modules/fobossdr_source") | ||||
| endif (OPT_BUILD_FOBOSSDR_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_HACKRF_SOURCE) | ||||
| add_subdirectory("source_modules/hackrf_source") | ||||
| endif (OPT_BUILD_HACKRF_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_HAROGIC_SOURCE) | ||||
| add_subdirectory("source_modules/harogic_source") | ||||
| endif (OPT_BUILD_HAROGIC_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_HERMES_SOURCE) | ||||
| add_subdirectory("source_modules/hermes_source") | ||||
| endif (OPT_BUILD_HERMES_SOURCE) | ||||
| @@ -157,6 +176,10 @@ if (OPT_BUILD_PLUTOSDR_SOURCE) | ||||
| add_subdirectory("source_modules/plutosdr_source") | ||||
| endif (OPT_BUILD_PLUTOSDR_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_RFNM_SOURCE) | ||||
| add_subdirectory("source_modules/rfnm_source") | ||||
| endif (OPT_BUILD_RFNM_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_RFSPACE_SOURCE) | ||||
| add_subdirectory("source_modules/rfspace_source") | ||||
| endif (OPT_BUILD_RFSPACE_SOURCE) | ||||
| @@ -225,6 +248,10 @@ if (OPT_BUILD_ATV_DECODER) | ||||
| add_subdirectory("decoder_modules/atv_decoder") | ||||
| endif (OPT_BUILD_ATV_DECODER) | ||||
|  | ||||
| if (OPT_BUILD_DAB_DECODER) | ||||
| add_subdirectory("decoder_modules/dab_decoder") | ||||
| endif (OPT_BUILD_DAB_DECODER) | ||||
|  | ||||
| if (OPT_BUILD_FALCON9_DECODER) | ||||
| add_subdirectory("decoder_modules/falcon9_decoder") | ||||
| endif (OPT_BUILD_FALCON9_DECODER) | ||||
| @@ -249,6 +276,10 @@ if (OPT_BUILD_RADIO) | ||||
| add_subdirectory("decoder_modules/radio") | ||||
| endif (OPT_BUILD_RADIO) | ||||
|  | ||||
| if (OPT_BUILD_RYFI_DECODER) | ||||
| add_subdirectory("decoder_modules/ryfi_decoder") | ||||
| endif (OPT_BUILD_RYFI_DECODER) | ||||
|  | ||||
| if (OPT_BUILD_WEATHER_SAT_DECODER) | ||||
| add_subdirectory("decoder_modules/weather_sat_decoder") | ||||
| endif (OPT_BUILD_WEATHER_SAT_DECODER) | ||||
| @@ -302,6 +333,21 @@ target_compile_options(sdrpp PRIVATE ${SDRPP_COMPILER_FLAGS}) | ||||
| if (MSVC) | ||||
|     add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y) | ||||
|     add_custom_target(do_always_volk ALL xcopy /s \"C:/Program Files/PothosSDR/bin\\volk.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y) | ||||
|  | ||||
|     if (COPY_MSVC_REDISTRIBUTABLES) | ||||
|         # Get the list of Visual C++ runtime DLLs | ||||
|         set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP True) | ||||
|         include(InstallRequiredSystemLibraries) | ||||
|  | ||||
|         # Create a space sperated list | ||||
|         set(REDIST_DLLS_STR "") | ||||
|         foreach(DLL IN LISTS CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS) | ||||
|             set(REDIST_DLLS_STR COMMAND xcopy /F \"${DLL}\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y ${REDIST_DLLS_STR}) | ||||
|         endforeach() | ||||
|          | ||||
|         # Create target | ||||
|         add_custom_target(do_always_msvc ALL ${REDIST_DLLS_STR}) | ||||
|     endif () | ||||
| endif () | ||||
|  | ||||
|  | ||||
| @@ -342,4 +388,6 @@ endif () | ||||
|  | ||||
| # Create uninstall target | ||||
| configure_file(${CMAKE_SOURCE_DIR}/cmake_uninstall.cmake ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY) | ||||
| add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) | ||||
| add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) | ||||
|  | ||||
| # Create headers target | ||||
| @@ -10,7 +10,7 @@ android { | ||||
|         minSdkVersion 28 | ||||
|         targetSdkVersion 28 | ||||
|         versionCode 1 | ||||
|         versionName "1.1.0" | ||||
|         versionName "1.2.0" | ||||
|  | ||||
|         externalNativeBuild { | ||||
|             cmake { | ||||
|   | ||||
| @@ -173,16 +173,22 @@ int sdrpp_main(int argc, char* argv[]) { | ||||
|     defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["File Source"]["module"] = "file_source"; | ||||
|     defConfig["moduleInstances"]["File Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["FobosSDR Source"]["module"] = "fobossdr_source"; | ||||
|     defConfig["moduleInstances"]["FobosSDR Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["HackRF Source"]["module"] = "hackrf_source"; | ||||
|     defConfig["moduleInstances"]["HackRF Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["Harogic Source"]["module"] = "harogic_source"; | ||||
|     defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["Hermes Source"]["module"] = "hermes_source"; | ||||
|     defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source"; | ||||
|     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"]["PlutoSDR Source"]["module"] = "plutosdr_source"; | ||||
|     defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["RFNM Source"]["module"] = "rfnm_source"; | ||||
|     defConfig["moduleInstances"]["RFNM 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"; | ||||
| @@ -193,8 +199,12 @@ int sdrpp_main(int argc, char* argv[]) { | ||||
|     defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["SDR++ Server Source"]["module"] = "sdrpp_server_source"; | ||||
|     defConfig["moduleInstances"]["SDR++ Server Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["Spectran HTTP Source"]["module"] = "spectran_http_source"; | ||||
|     defConfig["moduleInstances"]["Spectran HTTP Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["SpyServer Source"]["module"] = "spyserver_source"; | ||||
|     defConfig["moduleInstances"]["SpyServer Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["USRP Source"]["module"] = "usrp_source"; | ||||
|     defConfig["moduleInstances"]["USRP Source"]["enabled"] = true; | ||||
|  | ||||
|     defConfig["moduleInstances"]["Audio Sink"] = "audio_sink"; | ||||
|     defConfig["moduleInstances"]["Network Sink"] = "network_sink"; | ||||
|   | ||||
| @@ -37,14 +37,20 @@ namespace sdrpp_credits { | ||||
|     const char* hardwareDonators[] = { | ||||
|         "Aaronia AG", | ||||
|         "Airspy", | ||||
|         "Alex 4Z5LV", | ||||
|         "Analog Devices", | ||||
|         "CaribouLabs", | ||||
|         "Deepace", | ||||
|         "Ettus Research", | ||||
|         "Harogic", | ||||
|         "Howard Su", | ||||
|         "MicroPhase", | ||||
|         "Microtelecom", | ||||
|         "MyriadRF", | ||||
|         "Nuand", | ||||
|         "RFNM", | ||||
|         "RFspace", | ||||
|         "RigExpert", | ||||
|         "RTL-SDRblog", | ||||
|         "SDRplay" | ||||
|     }; | ||||
|   | ||||
| @@ -9,6 +9,7 @@ | ||||
| namespace dsp { | ||||
|     class generic_block { | ||||
|     public: | ||||
|         virtual ~generic_block() {} | ||||
|         virtual void start() {} | ||||
|         virtual void stop() {} | ||||
|         virtual int run() { return -1; } | ||||
| @@ -16,8 +17,6 @@ namespace dsp { | ||||
|  | ||||
|     class block : public generic_block { | ||||
|     public: | ||||
|         virtual void init() {} | ||||
|  | ||||
|         virtual ~block() { | ||||
|             if (!_block_init) { return; } | ||||
|             stop(); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #pragma once | ||||
| #include <volk/volk.h> | ||||
| #include <string.h> | ||||
|  | ||||
| namespace dsp::buffer { | ||||
|     template<class T> | ||||
|   | ||||
| @@ -10,6 +10,8 @@ namespace dsp { | ||||
|  | ||||
|         Operator(stream<A>* a, stream<B>* b) { init(a, b); } | ||||
|  | ||||
|         virtual ~Operator() {} | ||||
|  | ||||
|         virtual void init(stream<A>* a, stream<B>* b) { | ||||
|             _a = a; | ||||
|             _b = b; | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
| namespace dsp { | ||||
|     class untyped_stream { | ||||
|     public: | ||||
|         virtual ~untyped_stream() {} | ||||
|         virtual bool swap(int size) { return false; } | ||||
|         virtual int read() { return -1; } | ||||
|         virtual void flush() {} | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #pragma once | ||||
| #include <volk/volk.h> | ||||
| #include "../buffer/buffer.h" | ||||
|  | ||||
| namespace dsp { | ||||
|     template<class T> | ||||
|   | ||||
| @@ -19,6 +19,7 @@ namespace displaymenu { | ||||
|     std::string colorMapAuthor = ""; | ||||
|     int selectedWindow = 0; | ||||
|     int fftRate = 20; | ||||
|     int fftSizeId = 0; | ||||
|     int uiScaleId = 0; | ||||
|     bool restartRequired = false; | ||||
|     bool fftHold = false; | ||||
| @@ -28,34 +29,9 @@ namespace displaymenu { | ||||
|     bool snrSmoothing = false; | ||||
|     int snrSmoothingSpeed = 20; | ||||
|  | ||||
|     OptionList<int, int> fftSizes; | ||||
|     OptionList<float, float> uiScales; | ||||
|  | ||||
|     const int FFTSizes[] = { | ||||
|         524288, | ||||
|         262144, | ||||
|         131072, | ||||
|         65536, | ||||
|         32768, | ||||
|         16384, | ||||
|         8192, | ||||
|         4096, | ||||
|         2048, | ||||
|         1024 | ||||
|     }; | ||||
|  | ||||
|     const char* FFTSizesStr = "524288\0" | ||||
|                               "262144\0" | ||||
|                               "131072\0" | ||||
|                               "65536\0" | ||||
|                               "32768\0" | ||||
|                               "16384\0" | ||||
|                               "8192\0" | ||||
|                               "4096\0" | ||||
|                               "2048\0" | ||||
|                               "1024\0"; | ||||
|  | ||||
|     int fftSizeId = 0; | ||||
|  | ||||
|     const IQFrontEnd::FFTWindow fftWindowList[] = { | ||||
|         IQFrontEnd::FFTWindow::RECTANGULAR, | ||||
|         IQFrontEnd::FFTWindow::BLACKMAN, | ||||
| @@ -69,6 +45,18 @@ namespace displaymenu { | ||||
|     } | ||||
|  | ||||
|     void init() { | ||||
|         // Define FFT sizes | ||||
|         fftSizes.define(524288, "524288", 524288); | ||||
|         fftSizes.define(262144, "262144", 262144); | ||||
|         fftSizes.define(131072, "131072", 131072); | ||||
|         fftSizes.define(65536, "65536", 65536); | ||||
|         fftSizes.define(32768, "32768", 32768); | ||||
|         fftSizes.define(16384, "16384", 16384); | ||||
|         fftSizes.define(8192, "8192", 8192); | ||||
|         fftSizes.define(4096, "4096", 4096); | ||||
|         fftSizes.define(2048, "2048", 2048); | ||||
|         fftSizes.define(1024, "1024", 1024); | ||||
|  | ||||
|         showWaterfall = core::configManager.conf["showWaterfall"]; | ||||
|         showWaterfall ? gui::waterfall.showWaterfall() : gui::waterfall.hideWaterfall(); | ||||
|         std::string colormapName = core::configManager.conf["colorMap"]; | ||||
| @@ -90,15 +78,12 @@ namespace displaymenu { | ||||
|         fullWaterfallUpdate = core::configManager.conf["fullWaterfallUpdate"]; | ||||
|         gui::waterfall.setFullWaterfallUpdate(fullWaterfallUpdate); | ||||
|  | ||||
|         fftSizeId = 3; | ||||
|         int fftSize = core::configManager.conf["fftSize"]; | ||||
|         for (int i = 0; i < 7; i++) { | ||||
|             if (fftSize == FFTSizes[i]) { | ||||
|                 fftSizeId = i; | ||||
|                 break; | ||||
|             } | ||||
|         fftSizeId = fftSizes.valueId(65536); | ||||
|         int size = core::configManager.conf["fftSize"]; | ||||
|         if (fftSizes.keyExists(size)) { | ||||
|             fftSizeId = fftSizes.keyId(size); | ||||
|         } | ||||
|         sigpath::iqFrontEnd.setFFTSize(FFTSizes[fftSizeId]); | ||||
|         sigpath::iqFrontEnd.setFFTSize(fftSizes.value(fftSizeId)); | ||||
|  | ||||
|         fftRate = core::configManager.conf["fftRate"]; | ||||
|         sigpath::iqFrontEnd.setFFTRate(fftRate); | ||||
| @@ -229,10 +214,10 @@ namespace displaymenu { | ||||
|  | ||||
|         ImGui::LeftLabel("FFT Size"); | ||||
|         ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|         if (ImGui::Combo("##sdrpp_fft_size", &fftSizeId, FFTSizesStr)) { | ||||
|             sigpath::iqFrontEnd.setFFTSize(FFTSizes[fftSizeId]); | ||||
|         if (ImGui::Combo("##sdrpp_fft_size", &fftSizeId, fftSizes.txt)) { | ||||
|             sigpath::iqFrontEnd.setFFTSize(fftSizes.value(fftSizeId)); | ||||
|             core::configManager.acquire(); | ||||
|             core::configManager.conf["fftSize"] = FFTSizes[fftSizeId]; | ||||
|             core::configManager.conf["fftSize"] = fftSizes.key(fftSizeId); | ||||
|             core::configManager.release(true); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -42,6 +42,7 @@ public: | ||||
|  | ||||
|     class Instance { | ||||
|     public: | ||||
|         virtual ~Instance() {} | ||||
|         virtual void postInit() = 0; | ||||
|         virtual void enable() = 0; | ||||
|         virtual void disable() = 0; | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| #pragma once | ||||
|  | ||||
| #define VERSION_STR "1.1.0" | ||||
| #define VERSION_STR "1.2.0" | ||||
							
								
								
									
										37
									
								
								decoder_modules/dab_decoder/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								decoder_modules/dab_decoder/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(dab_decoder) | ||||
|  | ||||
| file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c") | ||||
|  | ||||
| include(${SDRPP_MODULE_CMAKE}) | ||||
|  | ||||
| target_include_directories(dab_decoder PRIVATE "src/") | ||||
|  | ||||
| if (MSVC) | ||||
|     # Lib path | ||||
|     target_include_directories(dab_decoder PRIVATE "C:/Program Files/codec2/include/") | ||||
|     target_link_directories(dab_decoder PRIVATE "C:/Program Files/codec2/lib") | ||||
|  | ||||
|     target_link_libraries(dab_decoder PRIVATE libcodec2) | ||||
| elseif (ANDROID) | ||||
|     target_include_directories(dab_decoder PUBLIC | ||||
|         /sdr-kit/${ANDROID_ABI}/include/codec2 | ||||
|     ) | ||||
|  | ||||
|     target_link_libraries(dab_decoder PUBLIC | ||||
|         /sdr-kit/${ANDROID_ABI}/lib/libcodec2.so | ||||
|     ) | ||||
| else () | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|     pkg_check_modules(LIBCODEC2 REQUIRED codec2) | ||||
|  | ||||
|     target_include_directories(dab_decoder PRIVATE ${LIBCODEC2_INCLUDE_DIRS}) | ||||
|     target_link_directories(dab_decoder PRIVATE ${LIBCODEC2_LIBRARY_DIRS}) | ||||
|     target_link_libraries(dab_decoder PRIVATE ${LIBCODEC2_LIBRARIES}) | ||||
|  | ||||
|     # Include it because for some reason pkgconfig doesn't look here? | ||||
|     if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") | ||||
|         target_include_directories(dab_decoder PRIVATE "/usr/local/include") | ||||
|     endif() | ||||
| endif () | ||||
							
								
								
									
										280
									
								
								decoder_modules/dab_decoder/src/dab_dsp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								decoder_modules/dab_decoder/src/dab_dsp.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | ||||
| #pragma once | ||||
| #include <dsp/processor.h> | ||||
| #include <utils/flog.h> | ||||
| #include <fftw3.h> | ||||
| #include "dab_phase_sym.h" | ||||
|  | ||||
| namespace dab { | ||||
|     class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> { | ||||
|         using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>; | ||||
|     public: | ||||
|         CyclicSync() {} | ||||
|  | ||||
|         // TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves | ||||
|         CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); } | ||||
|  | ||||
|         void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { | ||||
|             // Computer the number of samples for the symbol and its cyclic prefix | ||||
|             symbolSamps = round(samplerate * symbolLength); | ||||
|             prefixSamps = round(samplerate * cyclicPrefixLength); | ||||
|  | ||||
|             // Allocate and clear the delay buffer | ||||
|             delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000); | ||||
|             dsp::buffer::clear(delayBuf, symbolSamps); | ||||
|  | ||||
|             // Allocate and clear the history buffer | ||||
|             histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps); | ||||
|             dsp::buffer::clear(histBuf, prefixSamps); | ||||
|  | ||||
|             // Compute the delay input addresses | ||||
|             delayBufInput = &delayBuf[symbolSamps]; | ||||
|  | ||||
|             // Compute the correlation AGC configuration | ||||
|             this->agcRate = agcRate; | ||||
|             agcRateInv = 1.0f - agcRate; | ||||
|              | ||||
|             base_type::init(in); | ||||
|         } | ||||
|  | ||||
|         void reset() { | ||||
|             assert(base_type::_block_init); | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             base_type::tempStop(); | ||||
|              | ||||
|             base_type::tempStart(); | ||||
|         } | ||||
|  | ||||
|         int run() { | ||||
|             int count = base_type::_in->read(); | ||||
|             if (count < 0) { return -1; } | ||||
|  | ||||
|             // Copy the data into the normal delay buffer | ||||
|             memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t)); | ||||
|  | ||||
|             // Flush the input stream | ||||
|             base_type::_in->flush(); | ||||
|  | ||||
|             // Do cross-correlation | ||||
|             for (int i = 0; i < count; i++) { | ||||
|                 // Get the current history slot | ||||
|                 dsp::complex_t* slot = &histBuf[histId++]; | ||||
|  | ||||
|                 // Wrap around the history slot index (TODO: Check that the history buffer's length is correct) | ||||
|                 histId %= prefixSamps; | ||||
|  | ||||
|                 // Kick out last value from the correlation | ||||
|                 corr -= *slot; | ||||
|  | ||||
|                 // Save input value and compute the new prodct | ||||
|                 dsp::complex_t val = delayBuf[i]; | ||||
|                 dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps]; | ||||
|  | ||||
|                 // Add the new value to the correlation | ||||
|                 *slot = prod; | ||||
|  | ||||
|                 // Add the new value to the history buffer | ||||
|                 corr += prod; | ||||
|  | ||||
|                 // Compute sample amplitude | ||||
|                 float rcorr = corr.amplitude(); | ||||
|  | ||||
|                 // If a high enough peak is reached, reset the symbol counter | ||||
|                 if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed | ||||
|                     peakCorr = rcorr; | ||||
|                     peakLCorr = lastCorr; | ||||
|                     samplesSincePeak = 0; | ||||
|                 } | ||||
|  | ||||
|                 // If this is the sample right after the peak, save it | ||||
|                 if (samplesSincePeak == 1) { | ||||
|                     peakRCorr = rcorr; | ||||
|                 } | ||||
|  | ||||
|                 // Write the sample to the output | ||||
|                 out.writeBuf[samplesSincePeak++] = val; | ||||
|  | ||||
|                 // If the end of the symbol is reached, send it off | ||||
|                 if (samplesSincePeak >= symbolSamps) { | ||||
|                     if (!out.swap(symbolSamps)) { | ||||
|                         return -1; | ||||
|                     } | ||||
|                     samplesSincePeak = 0; | ||||
|                     peakCorr = 0; | ||||
|                 } | ||||
|  | ||||
|                 // Update the average correlation | ||||
|                 lastCorr = rcorr; | ||||
|  | ||||
|                 // Update the average correlation value | ||||
|                 avgCorr = agcRate*rcorr + agcRateInv*avgCorr; | ||||
|             } | ||||
|  | ||||
|             // Move unused data | ||||
|             memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t)); | ||||
|  | ||||
|             return count; | ||||
|         } | ||||
|  | ||||
|     protected: | ||||
|         int symbolSamps; | ||||
|         int prefixSamps; | ||||
|  | ||||
|         int histId = 0; | ||||
|         dsp::complex_t* histBuf; | ||||
|  | ||||
|         dsp::complex_t* delayBuf; | ||||
|         dsp::complex_t* delayBufInput; | ||||
|  | ||||
|         dsp::complex_t corr = { 0.0f, 0.0f }; | ||||
|  | ||||
|         int samplesSincePeak = 0; | ||||
|         float lastCorr = 0.0f; | ||||
|         float peakCorr = 0.0f; | ||||
|         float peakLCorr = 0.0f; | ||||
|         float peakRCorr = 0.0f; | ||||
|  | ||||
|         // Note only required for DAB | ||||
|         float avgCorr = 0.0f; | ||||
|         float agcRate; | ||||
|         float agcRateInv; | ||||
|     }; | ||||
|  | ||||
|     class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> { | ||||
|         using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>; | ||||
|     public: | ||||
|         FrameFreqSync() {} | ||||
|  | ||||
|         FrameFreqSync(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) { init(in, agcRate); } | ||||
|  | ||||
|         void init(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) { | ||||
|             // Allocate buffers | ||||
|             amps = dsp::buffer::alloc<float>(2048); | ||||
|             conjRef = dsp::buffer::alloc<dsp::complex_t>(2048); | ||||
|             corrIn = (dsp::complex_t*)fftwf_alloc_complex(2048); | ||||
|             corrOut = (dsp::complex_t*)fftwf_alloc_complex(2048); | ||||
|  | ||||
|             // Copy the phase reference | ||||
|             memcpy(conjRef, DAB_PHASE_SYM_CONJ, 2048 * sizeof(dsp::complex_t)); | ||||
|  | ||||
|             // Plan the FFT computation | ||||
|             plan = fftwf_plan_dft_1d(2048, (fftwf_complex*)corrIn, (fftwf_complex*)corrOut, FFTW_FORWARD, FFTW_ESTIMATE); | ||||
|  | ||||
|             // Compute the correlation AGC configuration | ||||
|             this->agcRate = agcRate; | ||||
|             agcRateInv = 1.0f - agcRate; | ||||
|              | ||||
|             base_type::init(in); | ||||
|         } | ||||
|  | ||||
|         void reset() { | ||||
|             assert(base_type::_block_init); | ||||
|             std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); | ||||
|             base_type::tempStop(); | ||||
|              | ||||
|             base_type::tempStart(); | ||||
|         } | ||||
|  | ||||
|         int run() { | ||||
|             int count = base_type::_in->read(); | ||||
|             if (count < 0) { return -1; } | ||||
|  | ||||
|             // Apply frequency shift | ||||
|             lv_32fc_t phase = lv_cmake(1.0f, 0.0f); | ||||
|             lv_32fc_t phaseDelta = lv_cmake(cos(offset), sin(offset)); | ||||
| #if VOLK_VERSION >= 030100 | ||||
|             volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count); | ||||
| #else | ||||
|             volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count); | ||||
| #endif | ||||
|  | ||||
|             // Compute the amplitude amplitude of all samples | ||||
|             volk_32fc_magnitude_32f(amps, (lv_32fc_t*)_in->readBuf, 2048); | ||||
|  | ||||
|             // Compute the average signal level by adding up all values | ||||
|             float level = 0.0f; | ||||
|             volk_32f_accumulator_s32f(&level, amps, 2048); | ||||
|  | ||||
|             // Detect a frame sync condition | ||||
|             if (level < avgLvl * 0.5f) { | ||||
|                 // Reset symbol counter | ||||
|                 sym = 1; | ||||
|  | ||||
|                 // Update the average level | ||||
|                 avgLvl = agcRate*level + agcRateInv*avgLvl; | ||||
|  | ||||
|                 // Flush the input stream and return | ||||
|                 base_type::_in->flush(); | ||||
|                 return count; | ||||
|             } | ||||
|  | ||||
|             // Update the average level | ||||
|             avgLvl = agcRate*level + agcRateInv*avgLvl; | ||||
|  | ||||
|             // Handle phase reference | ||||
|             if (sym == 1) { | ||||
|                 // Output the symbols (DEBUG ONLY) | ||||
|                 memcpy(corrIn, _in->readBuf, 2048 * sizeof(dsp::complex_t)); | ||||
|                 fftwf_execute(plan); | ||||
|                 volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048); | ||||
|                 int outCount = 0; | ||||
|                 dsp::complex_t pi4 = { cos(3.1415926535*0.25), sin(3.1415926535*0.25) }; | ||||
|                 for (int i = -767; i < 768; i++) { | ||||
|                     if (!i) { continue; } | ||||
|                     int cid0 = ((i-1) >= 0) ? (i-1) : 2048+(i-1); | ||||
|                     int cid1 = (i >= 0) ? i : 2048+i;; | ||||
|                     out.writeBuf[outCount++] = pi4 * (corrOut[cid1] * corrOut[cid0].conj()) * (1.0f/(amps[cid0]*amps[cid0])); | ||||
|                 } | ||||
|                 out.swap(outCount); | ||||
|  | ||||
|                 // Multiply the samples with the conjugated phase reference signal | ||||
|                 volk_32fc_x2_multiply_32fc((lv_32fc_t*)corrIn, (lv_32fc_t*)_in->readBuf, (lv_32fc_t*)conjRef, 2048); | ||||
|              | ||||
|                 // Compute the FFT of the product | ||||
|                 fftwf_execute(plan); | ||||
|  | ||||
|                 // Compute the amplitude of the bins | ||||
|                 volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048); | ||||
|  | ||||
|                 // Locate highest power bin | ||||
|                 uint32_t peakId; | ||||
|                 volk_32f_index_max_32u(&peakId, amps, 2048); | ||||
|  | ||||
|                 // Obtain the value of the bins next to the peak | ||||
|                 float peakL = amps[(peakId + 2047) % 2048]; | ||||
|                 float peakR = amps[(peakId + 1) % 2048]; | ||||
|  | ||||
|                 // Compute the integer frequency offset | ||||
|                 float offInt = (peakId < 1024) ? (float)peakId : ((float)peakId - 2048.0f); | ||||
|  | ||||
|                 // Compute the frequency offset in rad/samp | ||||
|                 float off = 3.1415926535f * (offInt + ((peakR - peakL) / (peakR + peakL))) * (1.0f / 1024.0f); | ||||
|  | ||||
|                 // Run control loop | ||||
|                 offset -= 0.1f*off; | ||||
|                 flog::debug("Offset: {} Hz, Error: {} Hz, Avg Level: {}", offset * (0.5f/3.1415926535f)*2.048e6, off * (0.5f/3.1415926535f)*2.048e6, avgLvl); | ||||
|             } | ||||
|  | ||||
|             // Increment the symbol counter | ||||
|             sym++; | ||||
|  | ||||
|             // Flush the input stream and return | ||||
|             base_type::_in->flush(); | ||||
|             return count; | ||||
|         } | ||||
|  | ||||
|     protected: | ||||
|         fftwf_plan plan; | ||||
|  | ||||
|         float* amps; | ||||
|         dsp::complex_t* conjRef; | ||||
|         dsp::complex_t* corrIn; | ||||
|         dsp::complex_t* corrOut; | ||||
|  | ||||
|         int sym; | ||||
|         float offset = 0.0f; | ||||
|  | ||||
|         float avgLvl = 0.0f; | ||||
|         float agcRate; | ||||
|         float agcRateInv; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										2053
									
								
								decoder_modules/dab_decoder/src/dab_phase_sym.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2053
									
								
								decoder_modules/dab_decoder/src/dab_phase_sym.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										163
									
								
								decoder_modules/dab_decoder/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								decoder_modules/dab_decoder/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,163 @@ | ||||
| #include <imgui.h> | ||||
| #include <config.h> | ||||
| #include <core.h> | ||||
| #include <gui/style.h> | ||||
| #include <gui/gui.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <module.h> | ||||
| #include <filesystem> | ||||
| #include <dsp/stream.h> | ||||
| #include <dsp/buffer/reshaper.h> | ||||
| #include <dsp/multirate/rational_resampler.h> | ||||
| #include <dsp/sink/handler_sink.h> | ||||
| #include <fstream> | ||||
| #include <chrono> | ||||
| #include "dab_dsp.h" | ||||
| #include <gui/widgets/constellation_diagram.h> | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
|     /* Name:            */ "dab_decoder", | ||||
|     /* Description:     */ "DAB/DAB+ Decoder for SDR++", | ||||
|     /* Author:          */ "Ryzerth", | ||||
|     /* Version:         */ 0, 1, 0, | ||||
|     /* Max instances    */ -1 | ||||
| }; | ||||
|  | ||||
| ConfigManager config; | ||||
|  | ||||
| #define INPUT_SAMPLE_RATE   2.048e6 | ||||
| #define VFO_BANDWIDTH       1.6e6 | ||||
|  | ||||
| class M17DecoderModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     M17DecoderModule(std::string name)  { | ||||
|         this->name = name; | ||||
|  | ||||
|         file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary); | ||||
|  | ||||
|         // Load config | ||||
|         config.acquire(); | ||||
|          | ||||
|         config.release(true); | ||||
|  | ||||
|         // Initialize VFO | ||||
|         vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true); | ||||
|         vfo->setSnapInterval(250); | ||||
|  | ||||
|         // Initialize DSP here | ||||
|         csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE); | ||||
|         ffsync.init(&csync.out); | ||||
|         ns.init(&ffsync.out, handler, this); | ||||
|  | ||||
|         // Start DSO Here | ||||
|         csync.start(); | ||||
|         ffsync.start(); | ||||
|         ns.start(); | ||||
|  | ||||
|         gui::menu.registerEntry(name, menuHandler, this, this); | ||||
|     } | ||||
|  | ||||
|     ~M17DecoderModule() { | ||||
|         gui::menu.removeEntry(name); | ||||
|         // Stop DSP Here | ||||
|         if (enabled) { | ||||
|             csync.stop(); | ||||
|             ffsync.stop(); | ||||
|             ns.stop(); | ||||
|             sigpath::vfoManager.deleteVFO(vfo); | ||||
|         } | ||||
|  | ||||
|         sigpath::sinkManager.unregisterStream(name); | ||||
|     } | ||||
|  | ||||
|     void postInit() {} | ||||
|  | ||||
|     void enable() { | ||||
|         double bw = gui::waterfall.getBandwidth(); | ||||
|         vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true); | ||||
|         vfo->setSnapInterval(250); | ||||
|  | ||||
|         // Set Input of demod here | ||||
|         csync.setInput(vfo->output); | ||||
|  | ||||
|         // Start DSP here | ||||
|         csync.start(); | ||||
|         ffsync.start(); | ||||
|         ns.start(); | ||||
|  | ||||
|         enabled = true; | ||||
|     } | ||||
|  | ||||
|     void disable() { | ||||
|         // Stop DSP here | ||||
|         csync.stop(); | ||||
|         ffsync.stop(); | ||||
|         ns.stop(); | ||||
|  | ||||
|         sigpath::vfoManager.deleteVFO(vfo); | ||||
|         enabled = false; | ||||
|     } | ||||
|  | ||||
|     bool isEnabled() { | ||||
|         return enabled; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     static void menuHandler(void* ctx) { | ||||
|         M17DecoderModule* _this = (M17DecoderModule*)ctx; | ||||
|  | ||||
|         float menuWidth = ImGui::GetContentRegionAvail().x; | ||||
|  | ||||
|         if (!_this->enabled) { style::beginDisabled(); } | ||||
|  | ||||
|         _this->constDiagram.draw(); | ||||
|  | ||||
|         if (!_this->enabled) { style::endDisabled(); } | ||||
|     } | ||||
|  | ||||
|     std::ofstream file; | ||||
|  | ||||
|     static void handler(dsp::complex_t* data, int count, void* ctx) { | ||||
|         M17DecoderModule* _this = (M17DecoderModule*)ctx; | ||||
|         //_this->file.write((char*)data, count * sizeof(dsp::complex_t)); | ||||
|  | ||||
|         dsp::complex_t* buf = _this->constDiagram.acquireBuffer(); | ||||
|         memcpy(buf, data, 1024 * sizeof(dsp::complex_t)); | ||||
|         _this->constDiagram.releaseBuffer(); | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|  | ||||
|     dab::CyclicSync csync; | ||||
|     dab::FrameFreqSync ffsync; | ||||
|     dsp::sink::Handler<dsp::complex_t> ns; | ||||
|  | ||||
|     ImGui::ConstellationDiagram constDiagram; | ||||
|  | ||||
|     // DSP Chain | ||||
|     VFOManager::VFO* vfo; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|     // Create default recording directory | ||||
|     json def = json({}); | ||||
|     config.setPath(core::args["root"].s() + "/dab_decoder_config.json"); | ||||
|     config.load(def); | ||||
|     config.enableAutoSave(); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new M17DecoderModule(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { | ||||
|     delete (M17DecoderModule*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|     config.disableAutoSave(); | ||||
|     config.save(); | ||||
| } | ||||
							
								
								
									
										34
									
								
								decoder_modules/dab_decoder/src/optimized_algo.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								decoder_modules/dab_decoder/src/optimized_algo.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| 0123456789 | ||||
| ---  --- | ||||
|  | ||||
| 0*4 | ||||
| 1*5 | ||||
| 2*6 | ||||
|  | ||||
| 1*5 | ||||
| 2*6 = L + 3*7 - 0*4 | ||||
| 3*7 | ||||
|  | ||||
| 2*6 | ||||
| 3*7 = L + 4*8 - 1*5 | ||||
| 4*8 | ||||
|  | ||||
| 3*7 | ||||
| 4*8 = L + 5*9 - 2*6 | ||||
| 5*9 | ||||
|  | ||||
|  | ||||
|  | ||||
| 0*5 | ||||
| 1*6 | ||||
| 2*7 | ||||
|  | ||||
| 1*6 | ||||
| 2*7 | ||||
| 3*8 | ||||
|  | ||||
| 2*7 | ||||
| 3*8 | ||||
| 4*9 | ||||
|  | ||||
| => Use same technique to cache the interpolation results | ||||
| @@ -597,14 +597,16 @@ private: | ||||
|  | ||||
|     static void moduleInterfaceHandler(int code, void* in, void* out, void* ctx) { | ||||
|         RadioModule* _this = (RadioModule*)ctx; | ||||
|         if (!_this->enabled || !_this->selectedDemod) { return; } | ||||
|  | ||||
|         // If no demod is selected, reject the command | ||||
|         if (!_this->selectedDemod) { return; } | ||||
|  | ||||
|         // Execute commands | ||||
|         if (code == RADIO_IFACE_CMD_GET_MODE && out) { | ||||
|             int* _out = (int*)out; | ||||
|             *_out = _this->selectedDemodID; | ||||
|         } | ||||
|         else if (code == RADIO_IFACE_CMD_SET_MODE && in) { | ||||
|         else if (code == RADIO_IFACE_CMD_SET_MODE && in && _this->enabled) { | ||||
|             int* _in = (int*)in; | ||||
|             _this->selectDemodByID((DemodID)*_in); | ||||
|         } | ||||
| @@ -612,7 +614,7 @@ private: | ||||
|             float* _out = (float*)out; | ||||
|             *_out = _this->bandwidth; | ||||
|         } | ||||
|         else if (code == RADIO_IFACE_CMD_SET_BANDWIDTH && in) { | ||||
|         else if (code == RADIO_IFACE_CMD_SET_BANDWIDTH && in && _this->enabled) { | ||||
|             float* _in = (float*)in; | ||||
|             if (_this->bandwidthLocked) { return; } | ||||
|             _this->setBandwidth(*_in); | ||||
| @@ -621,7 +623,7 @@ private: | ||||
|             bool* _out = (bool*)out; | ||||
|             *_out = _this->squelchEnabled; | ||||
|         } | ||||
|         else if (code == RADIO_IFACE_CMD_SET_SQUELCH_ENABLED && in) { | ||||
|         else if (code == RADIO_IFACE_CMD_SET_SQUELCH_ENABLED && in && _this->enabled) { | ||||
|             bool* _in = (bool*)in; | ||||
|             _this->setSquelchEnabled(*_in); | ||||
|         } | ||||
| @@ -629,7 +631,7 @@ private: | ||||
|             float* _out = (float*)out; | ||||
|             *_out = _this->squelchLevel; | ||||
|         } | ||||
|         else if (code == RADIO_IFACE_CMD_SET_SQUELCH_LEVEL && in) { | ||||
|         else if (code == RADIO_IFACE_CMD_SET_SQUELCH_LEVEL && in && _this->enabled) { | ||||
|             float* _in = (float*)in; | ||||
|             _this->setSquelchLevel(*_in); | ||||
|         } | ||||
|   | ||||
							
								
								
									
										8
									
								
								decoder_modules/ryfi_decoder/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								decoder_modules/ryfi_decoder/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(ryfi_decoder) | ||||
|  | ||||
| file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c") | ||||
|  | ||||
| include(${SDRPP_MODULE_CMAKE}) | ||||
|  | ||||
| target_include_directories(ryfi_decoder PRIVATE "src/") | ||||
							
								
								
									
										139
									
								
								decoder_modules/ryfi_decoder/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								decoder_modules/ryfi_decoder/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| #include <imgui.h> | ||||
| #include <config.h> | ||||
| #include <core.h> | ||||
| #include <gui/style.h> | ||||
| #include <gui/gui.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <module.h> | ||||
| #include <filesystem> | ||||
| #include <dsp/routing/splitter.h> | ||||
| #include <dsp/buffer/reshaper.h> | ||||
| #include <dsp/sink/handler_sink.h> | ||||
| #include <gui/widgets/folder_select.h> | ||||
| #include <gui/widgets/constellation_diagram.h> | ||||
| #include "ryfi/receiver.h" | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
|     /* Name:            */ "ryfi_decoder", | ||||
|     /* Description:     */ "RyFi decoder for SDR++", | ||||
|     /* Author:          */ "Ryzerth", | ||||
|     /* Version:         */ 0, 1, 0, | ||||
|     /* Max instances    */ -1 | ||||
| }; | ||||
|  | ||||
| #define INPUT_BANDWIDTH     600e3 | ||||
| #define INPUT_SAMPLE_RATE   1000e3 | ||||
| #define INPUT_BAUDRATE      500e3 | ||||
|  | ||||
| #define SYMBOL_DIAG_RATE    30 | ||||
| #define SYMBOL_DIAG_COUNT   1024 | ||||
|  | ||||
| class RyFiDecoderModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     RyFiDecoderModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true); | ||||
|         rx.init(vfo->output, INPUT_BAUDRATE, INPUT_SAMPLE_RATE); | ||||
|         reshape.init(rx.softOut, SYMBOL_DIAG_COUNT, (INPUT_BAUDRATE / SYMBOL_DIAG_RATE) - SYMBOL_DIAG_COUNT); | ||||
|         symSink.init(&reshape.out, symSinkHandler, this); | ||||
|         rx.onPacket.bind(&RyFiDecoderModule::packetHandler, this); | ||||
|  | ||||
|         rx.start(); | ||||
|         reshape.start(); | ||||
|         symSink.start(); | ||||
|  | ||||
|         gui::menu.registerEntry(name, menuHandler, this, this); | ||||
|     } | ||||
|  | ||||
|     ~RyFiDecoderModule() { | ||||
|         rx.stop(); | ||||
|         reshape.stop(); | ||||
|         symSink.stop(); | ||||
|         sigpath::vfoManager.deleteVFO(vfo); | ||||
|         gui::menu.removeEntry(name); | ||||
|     } | ||||
|  | ||||
|     void postInit() {} | ||||
|  | ||||
|     void enable() { | ||||
|         double bw = gui::waterfall.getBandwidth(); | ||||
|         vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true); | ||||
|  | ||||
|         rx.setInput(vfo->output); | ||||
|  | ||||
|         rx.start(); | ||||
|         reshape.start(); | ||||
|         symSink.start(); | ||||
|  | ||||
|         enabled = true; | ||||
|     } | ||||
|  | ||||
|     void disable() { | ||||
|         rx.stop(); | ||||
|         reshape.stop(); | ||||
|         symSink.stop(); | ||||
|  | ||||
|         sigpath::vfoManager.deleteVFO(vfo); | ||||
|         enabled = false; | ||||
|     } | ||||
|  | ||||
|     bool isEnabled() { | ||||
|         return enabled; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     void packetHandler(ryfi::Packet pkt) { | ||||
|         flog::debug("Got a {} byte packet!", pkt.size()); | ||||
|     } | ||||
|  | ||||
|     static void menuHandler(void* ctx) { | ||||
|         RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx; | ||||
|  | ||||
|         float menuWidth = ImGui::GetContentRegionAvail().x; | ||||
|  | ||||
|         if (!_this->enabled) { style::beginDisabled(); } | ||||
|  | ||||
|         ImGui::SetNextItemWidth(menuWidth); | ||||
|         _this->constDiagram.draw(); | ||||
|  | ||||
|         if (!_this->enabled) { style::endDisabled(); } | ||||
|     } | ||||
|  | ||||
|     static void symSinkHandler(dsp::complex_t* data, int count, void* ctx) { | ||||
|         RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx; | ||||
|  | ||||
|         dsp::complex_t* buf = _this->constDiagram.acquireBuffer(); | ||||
|         memcpy(buf, data, 1024 * sizeof(dsp::complex_t)); | ||||
|         _this->constDiagram.releaseBuffer(); | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|  | ||||
|     // DSP Chain | ||||
|     VFOManager::VFO* vfo; | ||||
|     ryfi::Receiver rx; | ||||
|     dsp::buffer::Reshaper<dsp::complex_t> reshape; | ||||
|     dsp::sink::Handler<dsp::complex_t> symSink; | ||||
|  | ||||
|     ImGui::ConstellationDiagram constDiagram; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|  | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new RyFiDecoderModule(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { | ||||
|     delete (RyFiDecoderModule*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|  | ||||
| } | ||||
							
								
								
									
										74
									
								
								decoder_modules/ryfi_decoder/src/ryfi/conv_codec.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								decoder_modules/ryfi_decoder/src/ryfi/conv_codec.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| #include "conv_codec.h" | ||||
|  | ||||
| namespace ryfi { | ||||
|     ConvEncoder::ConvEncoder(dsp::stream<uint8_t>* in) { | ||||
|         // Create the convolutional encoder instance | ||||
|         conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial); | ||||
|          | ||||
|         // Init the base class | ||||
|         base_type::init(in); | ||||
|     } | ||||
|  | ||||
|     ConvEncoder::~ConvEncoder() { | ||||
|         // Destroy the convolutional encoder instance | ||||
|         correct_convolutional_destroy(conv); | ||||
|     } | ||||
|  | ||||
|     int ConvEncoder::encode(const uint8_t* in, uint8_t* out, int count) { | ||||
|         // Run convolutional encoder on the data | ||||
|         return correct_convolutional_encode(conv, in, count, out); | ||||
|     } | ||||
|  | ||||
|     int ConvEncoder::run() { | ||||
|         int count = base_type::_in->read(); | ||||
|         if (count < 0) { return -1; } | ||||
|  | ||||
|         count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count); | ||||
|  | ||||
|         base_type::_in->flush(); | ||||
|         if (!out.swap(count)) { return -1; } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|     ConvDecoder::ConvDecoder(dsp::stream<dsp::complex_t>* in) { | ||||
|         // Create the convolutional encoder instance | ||||
|         conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial); | ||||
|  | ||||
|         // Allocate the soft symbol buffer | ||||
|         soft = dsp::buffer::alloc<uint8_t>(STREAM_BUFFER_SIZE); | ||||
|          | ||||
|         // Init the base class | ||||
|         base_type::init(in); | ||||
|     } | ||||
|  | ||||
|     ConvDecoder::~ConvDecoder() { | ||||
|         // Destroy the convolutional encoder instance | ||||
|         correct_convolutional_destroy(conv); | ||||
|  | ||||
|         // Free the soft symbol buffer | ||||
|         dsp::buffer::free(soft); | ||||
|     } | ||||
|  | ||||
|     int ConvDecoder::decode(const dsp::complex_t* in, uint8_t* out, int count) { | ||||
|         // Convert to uint8 | ||||
|         const float* _in = (const float*)in; | ||||
|         count *= 2; | ||||
|         for (int i = 0; i < count; i++) { | ||||
|             soft[i] = std::clamp<int>((_in[i] * 127.0f) + 128.0f, 0, 255); | ||||
|         } | ||||
|          | ||||
|         // Run convolutional decoder on the data | ||||
|         return correct_convolutional_decode_soft(conv, soft, count, out); | ||||
|     } | ||||
|  | ||||
|     int ConvDecoder::run() { | ||||
|         int count = base_type::_in->read(); | ||||
|         if (count < 0) { return -1; } | ||||
|  | ||||
|         count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count); | ||||
|  | ||||
|         base_type::_in->flush(); | ||||
|         if (!out.swap(count)) { return -1; } | ||||
|         return count; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										71
									
								
								decoder_modules/ryfi_decoder/src/ryfi/conv_codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								decoder_modules/ryfi_decoder/src/ryfi/conv_codec.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| #include "dsp/processor.h" | ||||
|  | ||||
| extern "C" { | ||||
|     #include "correct.h" | ||||
| } | ||||
|  | ||||
| namespace ryfi { | ||||
|     /** | ||||
|      * RyFi Convolutional Encoder. | ||||
|     */ | ||||
|     class ConvEncoder : public dsp::Processor<uint8_t, uint8_t> { | ||||
|         using base_type = dsp::Processor<uint8_t, uint8_t>; | ||||
|     public: | ||||
|         /** | ||||
|          * Create a convolutional encoder specifying an input stream. | ||||
|          * @param in Input stream. | ||||
|         */ | ||||
|         ConvEncoder(dsp::stream<uint8_t>* in = NULL); | ||||
|  | ||||
|         // Destructor | ||||
|         ~ConvEncoder(); | ||||
|  | ||||
|         /** | ||||
|          * Encode data. | ||||
|          * @param in Input bytes. | ||||
|          * @param out Output bits. | ||||
|          * @param count Number of input bytes. | ||||
|          * @return Number of output bits. | ||||
|         */ | ||||
|         int encode(const uint8_t* in, uint8_t* out, int count); | ||||
|  | ||||
|     private: | ||||
|         int run(); | ||||
|  | ||||
|         correct_convolutional* conv; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * RyFi Convolutional Decoder. | ||||
|     */ | ||||
|     class ConvDecoder : public dsp::Processor<dsp::complex_t, uint8_t> { | ||||
|         using base_type = dsp::Processor<dsp::complex_t, uint8_t>; | ||||
|     public: | ||||
|         /** | ||||
|          * Create a convolutional encoder specifying an input stream. | ||||
|          * @param in Input stream. | ||||
|         */ | ||||
|         ConvDecoder(dsp::stream<dsp::complex_t>* in = NULL); | ||||
|  | ||||
|         // Destructor | ||||
|         ~ConvDecoder(); | ||||
|  | ||||
|         /** | ||||
|          * Decode soft symbols. | ||||
|          * @param in Input soft symbols. | ||||
|          * @param out Output bytes. | ||||
|          * @param count Number of input bytes. | ||||
|          * @return Number of output bits. | ||||
|         */ | ||||
|         int decode(const dsp::complex_t* in, uint8_t* out, int count); | ||||
|  | ||||
|     private: | ||||
|         int run(); | ||||
|  | ||||
|         correct_convolutional* conv; | ||||
|         uint8_t* soft = NULL; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										37
									
								
								decoder_modules/ryfi_decoder/src/ryfi/frame.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								decoder_modules/ryfi_decoder/src/ryfi/frame.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| #include "frame.h" | ||||
|  | ||||
| namespace ryfi { | ||||
|     int Frame::serialize(uint8_t* bytes) const { | ||||
|         // Write the counter | ||||
|         bytes[0] = (counter >> 8) & 0xFF; | ||||
|         bytes[1] = counter & 0xFF; | ||||
|  | ||||
|         // Write the first packet pointer | ||||
|         bytes[2] = (firstPacket >> 8) & 0xFF; | ||||
|         bytes[3] = firstPacket & 0xFF; | ||||
|  | ||||
|         // Write the last packet pointer | ||||
|         bytes[4] = (lastPacket >> 8) & 0xFF; | ||||
|         bytes[5] = lastPacket & 0xFF; | ||||
|  | ||||
|         // Write the data | ||||
|         memcpy(&bytes[6], content, FRAME_DATA_SIZE); | ||||
|  | ||||
|         // Return the length of a serialized frame | ||||
|         return FRAME_SIZE; | ||||
|     } | ||||
|  | ||||
|     void Frame::deserialize(const uint8_t* bytes, Frame& frame) { | ||||
|         // Read the counter | ||||
|         frame.counter = (((uint16_t)bytes[0]) << 8) | ((uint16_t)bytes[1]); | ||||
|  | ||||
|         // Read the first packet pointer | ||||
|         frame.firstPacket = (((uint16_t)bytes[2]) << 8) | ((uint16_t)bytes[3]); | ||||
|  | ||||
|         // Read the last packet pointer | ||||
|         frame.lastPacket = (((uint16_t)bytes[4]) << 8) | ((uint16_t)bytes[5]); | ||||
|  | ||||
|         // Read the data | ||||
|         memcpy(frame.content, &bytes[6], FRAME_DATA_SIZE); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										42
									
								
								decoder_modules/ryfi_decoder/src/ryfi/frame.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								decoder_modules/ryfi_decoder/src/ryfi/frame.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include "rs_codec.h" | ||||
|  | ||||
| namespace ryfi { | ||||
|     enum PacketOffset { | ||||
|         PKT_OFFS_NONE   = 0xFFFF | ||||
|     }; | ||||
|      | ||||
|     struct Frame { | ||||
|         /** | ||||
|          * Serialize the frame to bytes. | ||||
|          * @param bytes Buffer to write the serialized frame to. | ||||
|         */ | ||||
|         int serialize(uint8_t* bytes) const; | ||||
|  | ||||
|         /** | ||||
|          * Deserialize a frame from bytes. | ||||
|          * @param bytes Buffer to deserialize the frame from. | ||||
|          * @param frame Object that will contain the deserialize frame. | ||||
|         */ | ||||
|         static void deserialize(const uint8_t* bytes, Frame& frame); | ||||
|  | ||||
|         // Size of a serialized frame | ||||
|         static inline const int FRAME_SIZE      = RS_BLOCK_DEC_SIZE*RS_BLOCK_COUNT; | ||||
|  | ||||
|         // Size of the data area of the frame | ||||
|         static inline const int FRAME_DATA_SIZE = FRAME_SIZE - 6; | ||||
|  | ||||
|         // Steadily increasing counter. | ||||
|         uint16_t counter = 0; | ||||
|  | ||||
|         // Byte offset of the first packet in the frame. | ||||
|         uint16_t firstPacket = 0; | ||||
|  | ||||
|         // Byte offset of the last packet in the frame. | ||||
|         uint16_t lastPacket = 0; | ||||
|  | ||||
|         // Data area of the frame. | ||||
|         uint8_t content[FRAME_DATA_SIZE]; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										137
									
								
								decoder_modules/ryfi_decoder/src/ryfi/framing.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								decoder_modules/ryfi_decoder/src/ryfi/framing.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| #include "framing.h" | ||||
|  | ||||
| namespace ryfi { | ||||
|     dsp::complex_t QPSK_SYMBOLS[4] = { | ||||
|         { -0.070710678118f, -0.070710678118f }, | ||||
|         { -0.070710678118f,  0.070710678118f }, | ||||
|         {  0.070710678118f, -0.070710678118f }, | ||||
|         {  0.070710678118f,  0.070710678118f }, | ||||
|     }; | ||||
|  | ||||
|     Framer::Framer(dsp::stream<uint8_t>* in) { | ||||
|         // Generate the sync symbols | ||||
|         int k = 0; | ||||
|         for (int i = 62; i >= 0; i -= 2) { | ||||
|             syncSyms[k++] = QPSK_SYMBOLS[(SYNC_WORD >> i) & 0b11]; | ||||
|         } | ||||
|          | ||||
|  | ||||
|         // Initialize base class | ||||
|         base_type::init(in); | ||||
|     } | ||||
|  | ||||
|     int Framer::encode(const uint8_t* in, dsp::complex_t* out, int count) { | ||||
|         // Copy sync symbols | ||||
|         memcpy(out, syncSyms, SYNC_SYMS*sizeof(dsp::complex_t)); | ||||
|  | ||||
|         // Modulate the rest of the bits | ||||
|         dsp::complex_t* dataOut = &out[SYNC_SYMS]; | ||||
|         int dataSyms = count / 2; | ||||
|         for (int i = 0; i < dataSyms; i++) { | ||||
|             uint8_t bits = (in[i >> 2] >> (6 - 2*(i & 0b11))) & 0b11; | ||||
|             dataOut[i] = QPSK_SYMBOLS[bits]; | ||||
|         } | ||||
|  | ||||
|         // Compute and return the total number of symbols | ||||
|         return SYNC_SYMS + dataSyms; | ||||
|     } | ||||
|  | ||||
|     int Framer::run() { | ||||
|         int count = base_type::_in->read(); | ||||
|         if (count < 0) { return -1; } | ||||
|  | ||||
|         count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count); | ||||
|  | ||||
|         base_type::_in->flush(); | ||||
|         if (!out.swap(count)) { return -1; } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|     Deframer::Deframer(dsp::stream<dsp::complex_t> *in) { | ||||
|         // Compute sync word rotations | ||||
|         //   0: 00 01 11 10 | ||||
|         //  90: 10 00 01 11 | ||||
|         // 180: 11 10 00 01 | ||||
|         // 270: 01 11 10 00 | ||||
|  | ||||
|         // For 0 and 180 it's the sync and its complement | ||||
|         syncRots[ROT_0_DEG] = SYNC_WORD; | ||||
|         syncRots[ROT_180_DEG] = ~SYNC_WORD; | ||||
|          | ||||
|         // For 90 and 270 its the quadrature and its complement | ||||
|         uint64_t quad; | ||||
|         for (int i = 62; i >= 0; i -= 2) { | ||||
|             // Get the symbol | ||||
|             uint8_t sym = (SYNC_WORD >> i) & 0b11; | ||||
|  | ||||
|             // Rotate it 90 degrees | ||||
|             uint8_t rsym; | ||||
|             switch (sym) { | ||||
|                 case 0b00:  rsym = 0b10; break; | ||||
|                 case 0b01:  rsym = 0b00; break; | ||||
|                 case 0b11:  rsym = 0b01; break; | ||||
|                 case 0b10:  rsym = 0b11; break; | ||||
|             } | ||||
|  | ||||
|             // Push it into the quadrature | ||||
|             quad = (quad << 2) | rsym; | ||||
|         } | ||||
|         syncRots[ROT_90_DEG] = quad; | ||||
|         syncRots[ROT_270_DEG] = ~quad; | ||||
|  | ||||
|         base_type::init(in); | ||||
|     } | ||||
|  | ||||
|     int Deframer::run() { | ||||
|         int count = base_type::_in->read(); | ||||
|         if (count < 0) { return -1; } | ||||
|  | ||||
|         dsp::complex_t* in = base_type::_in->readBuf; | ||||
|  | ||||
|         for (int i = 0; i < count; i++) { | ||||
|             if (recv) { | ||||
|                 // Copy the symbol to the output and rotate it approprieate | ||||
|                 base_type::out.writeBuf[outCount++] = in[i] * symRot; | ||||
|  | ||||
|                 // Check if we're done receiving the frame, send it out | ||||
|                 if (!(--recv)) { | ||||
|                     if (!base_type::out.swap(outCount)) { | ||||
|                         base_type::_in->flush(); | ||||
|                         return -1; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 // Get the raw symbol | ||||
|                 dsp::complex_t fsym = in[i]; | ||||
|  | ||||
|                 // Decode the symbol | ||||
|                 uint8_t sym = ((fsym.re > 0) ? 0b10 : 0b00) | ((fsym.im > 0) ? 0b01 : 0b00); | ||||
|  | ||||
|                 // Push it to the shift register | ||||
|                 shift = (shift << 2) | sym; | ||||
|  | ||||
|                 // Find the rotation starting with the last known one | ||||
|                 for (int i = 0; i < 4; i++) { | ||||
|                     // Get the test rotation | ||||
|                     int testRot = (knownRot+i) & 0b11; | ||||
|  | ||||
|                     // Check if the hamming distance is close enough | ||||
|                     int dist; | ||||
|                     if (distance(shift, syncRots[testRot]) < 6) { | ||||
|                         // Save the new rotation | ||||
|                         knownRot = testRot; | ||||
|  | ||||
|                         // Start reading in symbols for the frame | ||||
|                         symRot = symRots[knownRot]; | ||||
|                         recv = 8168; // TODO: Don't hardcode! | ||||
|                         outCount = 0; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         base_type::_in->flush(); | ||||
|         return count; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										87
									
								
								decoder_modules/ryfi_decoder/src/ryfi/framing.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								decoder_modules/ryfi_decoder/src/ryfi/framing.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| #pragma once | ||||
| #include "dsp/processor.h" | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
|  | ||||
| namespace ryfi { | ||||
|     // Synchronization word. | ||||
|     inline const uint64_t SYNC_WORD = 0x341CC540819D8963; | ||||
|  | ||||
|     // Number of synchronization bits. | ||||
|     inline const int SYNC_BITS      = 64; | ||||
|  | ||||
|     // Number of synchronization symbols. | ||||
|     inline const int SYNC_SYMS      = SYNC_BITS / 2; | ||||
|  | ||||
|     // Possible constellation rotations | ||||
|     enum { | ||||
|         ROT_0_DEG       = 0, | ||||
|         ROT_90_DEG      = 1, | ||||
|         ROT_180_DEG     = 2, | ||||
|         ROT_270_DEG     = 3 | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * RyFi Framer. | ||||
|     */ | ||||
|     class Framer : public dsp::Processor<uint8_t, dsp::complex_t> { | ||||
|         using base_type = dsp::Processor<uint8_t, dsp::complex_t>; | ||||
|     public: | ||||
|         /** | ||||
|          * Create a framer specifying an input stream. | ||||
|          * @param in Input stream. | ||||
|         */ | ||||
|         Framer(dsp::stream<uint8_t>* in = NULL); | ||||
|  | ||||
|         /** | ||||
|          * Encode a frame to symbols adding a sync word. | ||||
|         */ | ||||
|         int encode(const uint8_t* in, dsp::complex_t* out, int count); | ||||
|  | ||||
|     private: | ||||
|         int run(); | ||||
|  | ||||
|         dsp::complex_t syncSyms[SYNC_SYMS]; | ||||
|     }; | ||||
|  | ||||
|     class Deframer : public dsp::Processor<dsp::complex_t, dsp::complex_t> { | ||||
|         using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>; | ||||
|     public: | ||||
|         /** | ||||
|          * Create a deframer specifying an input stream. | ||||
|          * @param in Input stream. | ||||
|         */ | ||||
|         Deframer(dsp::stream<dsp::complex_t> *in = NULL); | ||||
|  | ||||
|     private: | ||||
|         int run(); | ||||
|  | ||||
|         inline static constexpr int distance(uint64_t a, uint64_t b) { | ||||
|             int dist = 0; | ||||
|             for (int i = 0; i < 64; i++) { | ||||
|                 dist += ((a & 1) != (b & 1)); | ||||
|                 a >>= 1; | ||||
|                 b >>= 1; | ||||
|             } | ||||
|             return dist; | ||||
|         } | ||||
|  | ||||
|         // Frame reading counters | ||||
|         int recv = 0; | ||||
|         int outCount = 0; | ||||
|  | ||||
|         // Rotation handling | ||||
|         int knownRot = 0; | ||||
|         uint64_t syncRots[4]; | ||||
|         dsp::complex_t symRot; | ||||
|         const dsp::complex_t symRots[4] = { | ||||
|             {  1.0f,  0.0f }, //   0 deg | ||||
|             {  0.0f, -1.0f }, //  90 deg | ||||
|             { -1.0f,  0.0f }, // 180 deg | ||||
|             {  0.0f,  1.0f }, // 270 deg | ||||
|         }; | ||||
|  | ||||
|         // Shift register | ||||
|         uint64_t shift; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										126
									
								
								decoder_modules/ryfi_decoder/src/ryfi/packet.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								decoder_modules/ryfi_decoder/src/ryfi/packet.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| #include "packet.h" | ||||
| #include "string.h" | ||||
| #include <stdexcept> | ||||
|  | ||||
| namespace ryfi { | ||||
|     Packet::Packet() {} | ||||
|  | ||||
|     Packet::Packet(uint8_t* content, int size) { | ||||
|         // Check that the size isn't too large | ||||
|         if (size > MAX_CONTENT_SIZE) { | ||||
|             throw std::runtime_error("Content size is too large to fit in a packet"); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // Allocate the buffer | ||||
|         allocate(size); | ||||
|  | ||||
|         // Copy over the content | ||||
|         memcpy(_content, content, size); | ||||
|     } | ||||
|  | ||||
|     Packet::Packet(const Packet& b) { | ||||
|         // Reallocate the buffer | ||||
|         allocate(b._size); | ||||
|  | ||||
|         // Copy over the content | ||||
|         memcpy(_content, b._content, b._size); | ||||
|     } | ||||
|  | ||||
|     Packet::Packet(Packet&& b) { | ||||
|         // Move members | ||||
|         _content = b._content; | ||||
|         _size = b._size; | ||||
|  | ||||
|         // Destroy old object | ||||
|         b._content = NULL; | ||||
|         b._size = 0; | ||||
|     } | ||||
|  | ||||
|     Packet::~Packet() { | ||||
|         // Delete the content | ||||
|         if (_content) { delete[] _content; } | ||||
|     } | ||||
|  | ||||
|     Packet& Packet::operator=(const Packet& b) { | ||||
|         // Reallocate the buffer | ||||
|         allocate(b._size); | ||||
|  | ||||
|         // Copy over the content | ||||
|         memcpy(_content, b._content, b._size); | ||||
|  | ||||
|         // Return self | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     Packet& Packet::operator=(Packet&& b) { | ||||
|         // Move members | ||||
|         _content = b._content; | ||||
|         _size = b._size; | ||||
|  | ||||
|         // Destroy old object | ||||
|         b._content = NULL; | ||||
|         b._size = 0; | ||||
|  | ||||
|         // Return self | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     Packet::operator bool() const { | ||||
|         return _size > 0; | ||||
|     } | ||||
|  | ||||
|     int Packet::size() const { | ||||
|         // Return the size | ||||
|         return _size; | ||||
|     } | ||||
|  | ||||
|     const uint8_t* Packet::data() const { | ||||
|         // Return the size | ||||
|         return _content; | ||||
|     } | ||||
|  | ||||
|     void Packet::setContent(uint8_t* content, int size) { | ||||
|         // Check that the size isn't too large | ||||
|         if (size > MAX_CONTENT_SIZE) { | ||||
|             throw std::runtime_error("Content size is too large to fit in a packet"); | ||||
|         } | ||||
|          | ||||
|         // Reallocate the buffer | ||||
|         allocate(size); | ||||
|  | ||||
|         // Copy over the content | ||||
|         memcpy(_content, content, size); | ||||
|     } | ||||
|  | ||||
|     int Packet::serializedSize() const { | ||||
|         // Two size bytes + Size of the content | ||||
|         return _size + 2; | ||||
|     } | ||||
|  | ||||
|     int Packet::serialize(uint8_t* bytes) const { | ||||
|         // Write the size in big-endian | ||||
|         bytes[0] = (_size >> 8) & 0xFF; | ||||
|         bytes[1] = _size & 0xFF; | ||||
|  | ||||
|         // Copy the content of the packet | ||||
|         memcpy(&bytes[2], _content, _size); | ||||
|  | ||||
|         // Return the serialized size | ||||
|         return serializedSize(); | ||||
|     } | ||||
|  | ||||
|     void Packet::allocate(int newSize) { | ||||
|         // If the size hasn't changed, do nothing | ||||
|         if (newSize == _size) { return; } | ||||
|  | ||||
|         // Free the old buffer | ||||
|         if (_content) { delete[] _content; }; | ||||
|  | ||||
|         // Update the size | ||||
|         _size = newSize; | ||||
|  | ||||
|         // Allocate the buffer | ||||
|         _content = new uint8_t[newSize]; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										89
									
								
								decoder_modules/ryfi_decoder/src/ryfi/packet.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								decoder_modules/ryfi_decoder/src/ryfi/packet.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
|  | ||||
| namespace ryfi { | ||||
|     /** | ||||
|      * RyFi Protocol Packet. | ||||
|     */ | ||||
|     class Packet { | ||||
|     public: | ||||
|         // Default constructor | ||||
|         Packet(); | ||||
|  | ||||
|         /** | ||||
|          * Create a packet from its content. | ||||
|          * @param content Content of the packet. | ||||
|          * @param size Number of bytes of content. | ||||
|         */ | ||||
|         Packet(uint8_t* content, int size); | ||||
|  | ||||
|         // Copy constructor | ||||
|         Packet(const Packet& b); | ||||
|  | ||||
|         // Move constructor | ||||
|         Packet(Packet&& b); | ||||
|  | ||||
|         // Destructor | ||||
|         ~Packet(); | ||||
|  | ||||
|         // Copy assignment operator | ||||
|         Packet& operator=(const Packet& b); | ||||
|  | ||||
|         // Move assignment operator | ||||
|         Packet& operator=(Packet&& b); | ||||
|  | ||||
|         // Cast to bool operator | ||||
|         operator bool() const; | ||||
|  | ||||
|         /** | ||||
|          * Get the size of the content of the packet. | ||||
|          * @return Size of the content of the packet. | ||||
|         */ | ||||
|         int size() const; | ||||
|  | ||||
|         /** | ||||
|          * Get the content of the packet. The pointer is only valid until reallocation or deletion. | ||||
|          * @return Content of the packet. | ||||
|         */ | ||||
|         const uint8_t* data() const; | ||||
|  | ||||
|         /** | ||||
|          * Set the content of the packet. | ||||
|          * @param content Content of the packet. | ||||
|          * @param size Number of bytes of content. | ||||
|         */ | ||||
|         void setContent(uint8_t* content, int size); | ||||
|  | ||||
|         /** | ||||
|          * Get the size of the serialized packet. | ||||
|          * @return Size of the serialized packet. | ||||
|         */ | ||||
|         int serializedSize() const; | ||||
|  | ||||
|         /** | ||||
|          * Serialize the packet to bytes. | ||||
|          * @param bytes Buffer to which to write the serialized packet. | ||||
|          * @return Size of the serialized packet. | ||||
|         */ | ||||
|         int serialize(uint8_t* bytes) const; | ||||
|  | ||||
|         /** | ||||
|          * Deserialize a packet from bytes. | ||||
|          * TODO | ||||
|         */ | ||||
|         static bool deserialize(uint8_t* bytes, int size, Packet& pkt); | ||||
|  | ||||
|         // Maximum size of the content of the packet. | ||||
|         static inline const int MAX_CONTENT_SIZE    = 0xFFFF; | ||||
|  | ||||
|         // Maximum size of the serialized packet. | ||||
|         static inline const int MAX_SERIALIZED_SIZE = MAX_CONTENT_SIZE + 2; | ||||
|  | ||||
|     private: | ||||
|         void allocate(int newSize); | ||||
|  | ||||
|         uint8_t* _content = NULL; | ||||
|         int _size = 0; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										194
									
								
								decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,194 @@ | ||||
| #include "receiver.h" | ||||
|  | ||||
| #include "utils/flog.h" | ||||
|  | ||||
| namespace ryfi { | ||||
|     Receiver::Receiver() {} | ||||
|  | ||||
|     Receiver::Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) { | ||||
|         init(in, baudrate, samplerate); | ||||
|     } | ||||
|  | ||||
|     Receiver::~Receiver() { | ||||
|         // Stop everything | ||||
|         stop(); | ||||
|     } | ||||
|  | ||||
|     void Receiver::init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) { | ||||
|         // Initialize the DSP | ||||
|         demod.init(in, baudrate, samplerate, 31, 0.6, 0.1f, 0.005f, 1e-6, 0.01); | ||||
|         doubler.init(&demod.out); | ||||
|         softOut = &doubler.outA; | ||||
|         deframer.setInput(&doubler.outB); | ||||
|         conv.setInput(&deframer.out); | ||||
|         rs.setInput(&conv.out); | ||||
|     } | ||||
|  | ||||
|     void Receiver::setInput(dsp::stream<dsp::complex_t>* in) { | ||||
|         demod.setInput(in); | ||||
|     } | ||||
|  | ||||
|     void Receiver::start() { | ||||
|         // Do nothing if already running | ||||
|         if (running) { return; } | ||||
|  | ||||
|         // Start the worker thread | ||||
|         workerThread = std::thread(&Receiver::worker, this); | ||||
|  | ||||
|         // Start the DSP | ||||
|         demod.start(); | ||||
|         doubler.start(); | ||||
|         deframer.start(); | ||||
|         conv.start(); | ||||
|         rs.start(); | ||||
|  | ||||
|         // Update the running state | ||||
|         running = true; | ||||
|     } | ||||
|  | ||||
|     void Receiver::stop() { | ||||
|         // Do nothing if not running | ||||
|         if (!running) { return; } | ||||
|  | ||||
|         // Stop the worker thread | ||||
|         rs.out.stopReader(); | ||||
|         if (workerThread.joinable()) { workerThread.join(); } | ||||
|         rs.out.clearReadStop(); | ||||
|  | ||||
|         // Stop the DSP | ||||
|         demod.stop(); | ||||
|         doubler.stop(); | ||||
|         deframer.stop(); | ||||
|         conv.stop(); | ||||
|         rs.stop(); | ||||
|  | ||||
|         // Update the running state | ||||
|         running = false; | ||||
|     } | ||||
|      | ||||
|     void Receiver::worker() { | ||||
|         Frame frame; | ||||
|         uint16_t lastCounter = 0; | ||||
|         uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE]; | ||||
|         int pktExpected = 0; | ||||
|         int pktRead = 0; | ||||
|         int valid = 0; | ||||
|  | ||||
|         while (true) { | ||||
|             // Read a frame | ||||
|             int count = rs.out.read(); | ||||
|             if (count <= 0) { break; } | ||||
|  | ||||
|             // Deserialize the frame | ||||
|             Frame::deserialize(rs.out.readBuf, frame); | ||||
|             valid++; | ||||
|  | ||||
|             // Flush the stream | ||||
|             rs.out.flush(); | ||||
|  | ||||
|             //flog::info("Frame[{}]: FirstPacket={}, LastPacket={}", frame.counter, frame.firstPacket, frame.lastPacket); | ||||
|  | ||||
|             // Compute the expected frame counter | ||||
|             uint16_t expectedCounter = lastCounter + 1; | ||||
|             lastCounter = frame.counter; | ||||
|  | ||||
|             // If the frames aren't consecutive | ||||
|             int frameRead = 0; | ||||
|             if (frame.counter != expectedCounter) { | ||||
|                 flog::warn("Lost at least {} frames after {} valid frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000, valid); | ||||
|  | ||||
|                 // Cancel the partial packet if there was one | ||||
|                 pktExpected = 0; | ||||
|                 pktRead = 0; | ||||
|                 valid = 1; | ||||
|  | ||||
|                 // If this frame is not an idle frame or continuation frame | ||||
|                 if (frame.firstPacket != PKT_OFFS_NONE) { | ||||
|                     // If the offset of the first packet is not plausible | ||||
|                     if (frame.firstPacket > Frame::FRAME_DATA_SIZE-2) { | ||||
|                         flog::warn("Packet had non-plausible offset: {}", frameRead); | ||||
|  | ||||
|                         // Skip the frame | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     // Skip to the end of the packet | ||||
|                     frameRead = frame.firstPacket; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // If there is no partial packet and the frame doesn't contain a packet start, skip it | ||||
|             if (!pktExpected && frame.firstPacket == PKT_OFFS_NONE) { continue; } | ||||
|  | ||||
|             // Extract packets from the frame | ||||
|             bool firstPacket = true; | ||||
|             bool lastPacket = false; | ||||
|             while (frameRead < Frame::FRAME_DATA_SIZE) { | ||||
|                 // If there is a partial packet read as much as possible from it | ||||
|                 if (pktExpected) { | ||||
|                     // Compute how many bytes of the packet are available in the frame | ||||
|                     int readable = std::min<int>(pktExpected - pktRead, Frame::FRAME_DATA_SIZE - frameRead); | ||||
|                     //flog::debug("Reading {} bytes", readable); | ||||
|  | ||||
|                     // Write them to the packet | ||||
|                     memcpy(&pktBuffer[pktRead], &frame.content[frameRead], readable); | ||||
|                     pktRead += readable; | ||||
|                     frameRead += readable; | ||||
|  | ||||
|                     // If the packet is read entirely | ||||
|                     if (pktRead >= pktExpected) { | ||||
|                         // Create the packet object | ||||
|                         Packet pkt(pktBuffer, pktExpected); | ||||
|  | ||||
|                         // Send off the packet | ||||
|                         onPacket(pkt); | ||||
|  | ||||
|                         // Prepare for the next packet | ||||
|                         pktRead = 0; | ||||
|                         pktExpected = 0; | ||||
|  | ||||
|                         // If this was the last packet of the frame | ||||
|                         if (lastPacket || frame.firstPacket == PKT_OFFS_NONE) { | ||||
|                             // Skip the rest of the frame | ||||
|                             frameRead = Frame::FRAME_DATA_SIZE; | ||||
|                             continue; | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     // Go to next packet | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 // If the packet offset is not plausible | ||||
|                 if (Frame::FRAME_DATA_SIZE - frameRead < 2) { | ||||
|                     flog::warn("Packet had non-plausible offset: {}", frameRead); | ||||
|  | ||||
|                     // Skip the rest of the frame and the packet | ||||
|                     frameRead = Frame::FRAME_DATA_SIZE; | ||||
|                     pktExpected = 0; | ||||
|                     pktRead = 0; | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 // If this is the first packet, use the frame info to skip possible left over data | ||||
|                 if (firstPacket) { | ||||
|                     frameRead = frame.firstPacket; | ||||
|                     firstPacket = false; | ||||
|                 } | ||||
|  | ||||
|                 // Check if this is the last packet | ||||
|                 lastPacket = (frameRead == frame.lastPacket); | ||||
|  | ||||
|                 // Parse the packet size | ||||
|                 pktExpected = ((uint16_t)frame.content[frameRead]) << 8; | ||||
|                 pktExpected |= (uint16_t)frame.content[frameRead+1]; | ||||
|                 //flog::debug("Starting to read a {} byte packet at offset {}", pktExpected, frameRead); | ||||
|  | ||||
|                 // Skip to the packet content | ||||
|                 frameRead += 2; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         delete[] pktBuffer; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										69
									
								
								decoder_modules/ryfi_decoder/src/ryfi/receiver.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								decoder_modules/ryfi_decoder/src/ryfi/receiver.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| #pragma once | ||||
| #include "utils/new_event.h" | ||||
| #include "dsp/demod/psk.h" | ||||
| #include "dsp/routing/doubler.h" | ||||
| #include "packet.h" | ||||
| #include "frame.h" | ||||
| #include "rs_codec.h" | ||||
| #include "conv_codec.h" | ||||
| #include "framing.h" | ||||
| #include <mutex> | ||||
|  | ||||
| namespace ryfi { | ||||
|     class Receiver { | ||||
|     public: | ||||
|         Receiver(); | ||||
|  | ||||
|         /** | ||||
|          * Create a transmitter. | ||||
|          * @param in Baseband input. | ||||
|          * @param baudrate Baudrate to use over the air. | ||||
|          * @param samplerate Samplerate of the baseband. | ||||
|         */ | ||||
|         Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate); | ||||
|  | ||||
|         /** | ||||
|          * Create a transmitter. | ||||
|          * @param in Baseband input. | ||||
|          * @param baudrate Baudrate to use over the air. | ||||
|          * @param samplerate Samplerate of the baseband. | ||||
|         */ | ||||
|         void init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate); | ||||
|  | ||||
|         /** | ||||
|          * Set the input stream. | ||||
|          * @param in Baseband input. | ||||
|         */ | ||||
|         void setInput(dsp::stream<dsp::complex_t>* in); | ||||
|  | ||||
|         // Destructor | ||||
|         ~Receiver(); | ||||
|  | ||||
|         /** | ||||
|          * Start the transmitter's DSP. | ||||
|         */ | ||||
|         void start(); | ||||
|  | ||||
|         /** | ||||
|          * Stop the transmitter's DSP. | ||||
|         */ | ||||
|         void stop(); | ||||
|          | ||||
|         dsp::stream<dsp::complex_t>* softOut; | ||||
|  | ||||
|         NewEvent<Packet> onPacket; | ||||
|  | ||||
|     private: | ||||
|         void worker(); | ||||
|  | ||||
|         // DSP | ||||
|         dsp::demod::PSK<4> demod; | ||||
|         dsp::routing::Doubler<dsp::complex_t> doubler; | ||||
|         Deframer deframer; | ||||
|         ConvDecoder conv; | ||||
|         RSDecoder rs; | ||||
|  | ||||
|         bool running = false; | ||||
|         std::thread workerThread; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										169
									
								
								decoder_modules/ryfi_decoder/src/ryfi/rs_codec.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								decoder_modules/ryfi_decoder/src/ryfi/rs_codec.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| #include "rs_codec.h" | ||||
|  | ||||
| namespace ryfi { | ||||
|     RSEncoder::RSEncoder(dsp::stream<uint8_t>* in) { | ||||
|         // Create the convolutional encoder instance | ||||
|         rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32); | ||||
|          | ||||
|         // Init the base class | ||||
|         base_type::init(in); | ||||
|     } | ||||
|  | ||||
|     RSEncoder::~RSEncoder() { | ||||
|         // Destroy the convolutional encoder instance | ||||
|         correct_reed_solomon_destroy(rs); | ||||
|     } | ||||
|  | ||||
|     int RSEncoder::encode(const uint8_t* in, uint8_t* out, int count) { | ||||
|         // Check the size | ||||
|         assert(count == RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE); | ||||
|  | ||||
|         // Go through each block | ||||
|         uint8_t block[RS_BLOCK_ENC_SIZE]; | ||||
|         for (int i = 0; i < RS_BLOCK_COUNT; i++) { | ||||
|             // Encode block | ||||
|             correct_reed_solomon_encode(rs, &in[i*RS_BLOCK_DEC_SIZE], RS_BLOCK_DEC_SIZE, block); | ||||
|  | ||||
|             // Interleave into the frame | ||||
|             int k = 0; | ||||
|             for (int j = i; j < RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT; j += RS_BLOCK_COUNT) { | ||||
|                 out[j] = block[k++]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Scramble | ||||
|         for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) { | ||||
|             out[i] ^= RS_SCRAMBLER_SEQ[i]; | ||||
|         } | ||||
|  | ||||
|         return RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; | ||||
|     } | ||||
|  | ||||
|     int RSEncoder::run() { | ||||
|         int count = base_type::_in->read(); | ||||
|         if (count < 0) { return -1; } | ||||
|  | ||||
|         count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count); | ||||
|  | ||||
|         base_type::_in->flush(); | ||||
|         if (!out.swap(count)) { return -1; } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|     RSDecoder::RSDecoder(dsp::stream<uint8_t>* in) { | ||||
|         // Create the convolutional encoder instance | ||||
|         rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32); | ||||
|          | ||||
|         // Init the base class | ||||
|         base_type::init(in); | ||||
|     } | ||||
|  | ||||
|     RSDecoder::~RSDecoder() { | ||||
|         // Destroy the convolutional encoder instance | ||||
|         correct_reed_solomon_destroy(rs); | ||||
|     } | ||||
|  | ||||
|     int RSDecoder::decode(uint8_t* in, uint8_t* out, int count) { | ||||
|         // Check the size | ||||
|         assert(count == RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE); | ||||
|  | ||||
|         // Descramble (TODO: Don't do it in-place) | ||||
|         for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) { | ||||
|             in[i] ^= RS_SCRAMBLER_SEQ[i]; | ||||
|         } | ||||
|  | ||||
|         // Go through each block | ||||
|         uint8_t block[RS_BLOCK_ENC_SIZE]; | ||||
|         for (int i = 0; i < RS_BLOCK_COUNT; i++) { | ||||
|             // Deinterleave out of the frame | ||||
|             int k = 0; | ||||
|             for (int j = i; j < count; j += RS_BLOCK_COUNT) { | ||||
|                 block[k++] = in[j]; | ||||
|             } | ||||
|  | ||||
|             // Decode block and return if decoding fails | ||||
|             int res = correct_reed_solomon_decode(rs, block, RS_BLOCK_ENC_SIZE, &out[i*RS_BLOCK_DEC_SIZE]); | ||||
|             if (res < 0) { return 0; } | ||||
|         } | ||||
|  | ||||
|         return RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE; | ||||
|     } | ||||
|  | ||||
|     int RSDecoder::run() { | ||||
|         int count = base_type::_in->read(); | ||||
|         if (count < 0) { return -1; } | ||||
|  | ||||
|         count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count); | ||||
|  | ||||
|         base_type::_in->flush(); | ||||
|         if (count && !out.swap(count)) { return -1; } | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|     const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT] = { | ||||
|         0x75, 0x05, 0x7C, 0xCE, 0xF1, 0xD0, 0x6C, 0xF6, 0xFA, 0x65, 0xF6, 0xFC, 0xE0, 0x0A, 0x82, 0x17, | ||||
|         0x6C, 0xBE, 0x76, 0xA0, 0xD6, 0x46, 0x12, 0x2E, 0xDE, 0xB5, 0xF7, 0xAD, 0xCB, 0x51, 0x63, 0x47, | ||||
|         0x27, 0x30, 0x7E, 0x43, 0xD1, 0xA1, 0xCB, 0x10, 0x08, 0x49, 0xDF, 0x86, 0xD4, 0xC4, 0xD7, 0x3C, | ||||
|         0x6D, 0x03, 0x07, 0x37, 0x5B, 0xB3, 0xCD, 0x79, 0x6F, 0x1E, 0xBA, 0xC5, 0x6E, 0xC3, 0x8C, 0x7A, | ||||
|         0x25, 0x99, 0x61, 0x54, 0x5A, 0x96, 0x57, 0x9B, 0xE0, 0x60, 0x5B, 0x09, 0x6D, 0x8B, 0x2D, 0x9D, | ||||
|         0x15, 0x9D, 0x0E, 0xBF, 0x57, 0xFB, 0x9C, 0x49, 0x82, 0x2C, 0x48, 0x59, 0x92, 0x47, 0x79, 0x17, | ||||
|         0x16, 0x74, 0xEA, 0xEA, 0xBB, 0xC5, 0x72, 0x32, 0x17, 0xD1, 0xB3, 0xDE, 0xEB, 0x15, 0xC7, 0x55, | ||||
|         0x8A, 0xF2, 0x88, 0xC2, 0x33, 0xA6, 0x17, 0x8B, 0xD4, 0x77, 0x22, 0x00, 0x63, 0x47, 0x45, 0x5F, | ||||
|         0x36, 0x35, 0x58, 0x8B, 0x88, 0xEC, 0xCA, 0xC4, 0x60, 0x53, 0x9E, 0xBD, 0xB2, 0xF5, 0x51, 0x46, | ||||
|         0x34, 0x9A, 0x07, 0x25, 0x3F, 0xF5, 0x65, 0x63, 0x77, 0x3C, 0x5A, 0xFA, 0x4E, 0x0C, 0xF7, 0x1B, | ||||
|         0x82, 0xAB, 0x73, 0x06, 0x7F, 0xB7, 0xC6, 0x6B, 0xBF, 0xB1, 0x46, 0xF3, 0x01, 0x91, 0xB1, 0xFF, | ||||
|         0x5C, 0x6F, 0xF9, 0x43, 0x0E, 0x6A, 0x70, 0x89, 0x0B, 0xEA, 0x8C, 0xD4, 0x1B, 0x51, 0x01, 0x31, | ||||
|         0x71, 0x2E, 0xDF, 0x24, 0xC1, 0xD5, 0xDB, 0x0E, 0xF5, 0xEB, 0x78, 0x79, 0x39, 0x5B, 0xAD, 0xC3, | ||||
|         0xA9, 0xA6, 0x60, 0x30, 0xA2, 0x9A, 0x7B, 0xA0, 0xF4, 0xAA, 0xC5, 0x57, 0xB3, 0x16, 0xF9, 0xB5, | ||||
|         0x79, 0x20, 0xC1, 0x88, 0x9A, 0x00, 0x43, 0xB2, 0xC6, 0x84, 0x8D, 0x03, 0xF2, 0xD8, 0x90, 0x7A, | ||||
|         0x21, 0x37, 0x7E, 0xF7, 0x75, 0xE5, 0xFB, 0xC9, 0xDC, 0xAB, 0x4B, 0xBC, 0x35, 0x38, 0xB9, 0x3A, | ||||
|         0x53, 0x89, 0x7E, 0xD5, 0x94, 0x12, 0x2D, 0x9B, 0x91, 0x90, 0x1D, 0x4D, 0x0E, 0xE0, 0x93, 0xF3, | ||||
|         0xC1, 0xA1, 0x9B, 0x73, 0x27, 0x22, 0x41, 0x27, 0xEE, 0x2A, 0xD7, 0x45, 0xBC, 0x8F, 0x9B, 0xA2, | ||||
|         0x36, 0x11, 0x16, 0x37, 0x1A, 0xF1, 0x2E, 0x71, 0xCF, 0x86, 0x89, 0x83, 0x5A, 0xF1, 0x24, 0x6C, | ||||
|         0x56, 0x71, 0x53, 0xE4, 0xD2, 0xCB, 0xCA, 0x86, 0x1E, 0xA0, 0xD5, 0x83, 0x3B, 0xEF, 0x09, 0x09, | ||||
|         0xC2, 0x07, 0x53, 0x86, 0xE6, 0x8A, 0xC6, 0x70, 0xFB, 0x91, 0x43, 0xCB, 0x91, 0x6E, 0xA9, 0xBC, | ||||
|         0x31, 0x42, 0x61, 0x0C, 0x88, 0xB8, 0x2C, 0xED, 0xD8, 0xE6, 0xA3, 0xEC, 0xAC, 0xB9, 0x45, 0x5E, | ||||
|         0x2C, 0x73, 0x3F, 0x2E, 0x06, 0xE0, 0xBF, 0x73, 0xDD, 0x2E, 0x45, 0x50, 0x6C, 0x53, 0x55, 0xF0, | ||||
|         0x7F, 0x6E, 0x61, 0xFA, 0xA0, 0x7A, 0x1C, 0xF0, 0xBD, 0xAC, 0x48, 0x61, 0x03, 0x6B, 0xED, 0x54, | ||||
|         0x2A, 0x27, 0x94, 0xF6, 0xF9, 0x6A, 0x04, 0x08, 0x0B, 0x3C, 0xC3, 0x30, 0x66, 0x01, 0xFB, 0xDC, | ||||
|         0xC9, 0x65, 0x03, 0x83, 0x7D, 0x0A, 0xDF, 0xA5, 0x04, 0x14, 0xE4, 0xF2, 0x4C, 0x01, 0xDF, 0x04, | ||||
|         0xD2, 0x80, 0xB9, 0x9B, 0xD9, 0x5E, 0xF8, 0x2A, 0x93, 0x8D, 0x8C, 0x09, 0x9B, 0x38, 0xEC, 0x3B, | ||||
|         0xC4, 0x29, 0x90, 0x7C, 0x65, 0x3A, 0xF2, 0x4B, 0x69, 0xD3, 0x63, 0x9B, 0x40, 0x95, 0xC3, 0xFB, | ||||
|         0x67, 0x54, 0x40, 0x9B, 0x26, 0x9F, 0x52, 0xFE, 0xD8, 0xD0, 0x24, 0x9C, 0x5C, 0xD4, 0xEF, 0xDE, | ||||
|         0x28, 0x66, 0x75, 0x04, 0xCB, 0xA4, 0xC0, 0xB9, 0x4B, 0xC9, 0x20, 0x4B, 0x56, 0xC7, 0x86, 0xC5, | ||||
|         0x39, 0x45, 0x18, 0xA7, 0x48, 0x14, 0x1A, 0x51, 0xCA, 0xD0, 0xC0, 0x15, 0xDD, 0xC1, 0x28, 0x4A, | ||||
|         0x7A, 0xD2, 0x10, 0xEA, 0x83, 0xD3, 0x3A, 0xEF, 0x48, 0x29, 0x41, 0xA4, 0xD4, 0x57, 0xA6, 0x1D, | ||||
|         0x76, 0x24, 0x93, 0x58, 0x7E, 0xB7, 0xDD, 0x0B, 0xF2, 0xCE, 0x71, 0x55, 0xF5, 0xAB, 0x8C, 0xC8, | ||||
|         0x70, 0x59, 0x73, 0x69, 0x9D, 0x29, 0x5E, 0x59, 0xF4, 0xB2, 0xC4, 0x97, 0x75, 0xF0, 0x65, 0x1B, | ||||
|         0x66, 0x5F, 0xA4, 0x33, 0x5C, 0xC7, 0xBF, 0x45, 0xE6, 0x20, 0xC0, 0xBD, 0xAD, 0xAE, 0x9F, 0x97, | ||||
|         0x05, 0xD8, 0x04, 0x2B, 0x0A, 0x46, 0xE8, 0xB8, 0xCB, 0x00, 0xE2, 0x7C, 0x70, 0x1B, 0x49, 0xDE, | ||||
|         0x81, 0xEB, 0x24, 0xAC, 0x1B, 0x3E, 0x09, 0xFB, 0xAC, 0xB7, 0xF2, 0xD1, 0xB2, 0x78, 0xF3, 0xAC, | ||||
|         0xC7, 0x6A, 0xA2, 0x07, 0x4C, 0xED, 0x61, 0xAD, 0x04, 0x7F, 0x45, 0x83, 0x59, 0x31, 0x27, 0xF0, | ||||
|         0x16, 0x6B, 0x0C, 0xAA, 0xD4, 0xD1, 0xCB, 0x1C, 0x51, 0x41, 0x0D, 0x2F, 0x8F, 0xF9, 0xF9, 0x7F, | ||||
|         0x22, 0x89, 0x46, 0xF4, 0xB8, 0x93, 0x98, 0x9E, 0x3E, 0x23, 0xF1, 0x6E, 0x64, 0x08, 0xB6, 0xC9, | ||||
|         0x6E, 0x53, 0x53, 0xED, 0xAD, 0x21, 0xCD, 0x1A, 0xF0, 0x45, 0xFC, 0x14, 0x00, 0xEA, 0xF7, 0x42, | ||||
|         0xEE, 0xDA, 0x58, 0x0D, 0x85, 0xBC, 0x74, 0xFB, 0x73, 0x78, 0xB5, 0x5E, 0x5E, 0x6F, 0x6F, 0x7E, | ||||
|         0x39, 0xC2, 0x05, 0x50, 0xDB, 0x3D, 0xB8, 0xF3, 0x8F, 0x80, 0xEC, 0x46, 0x29, 0x39, 0x89, 0xF3, | ||||
|         0x55, 0x9C, 0x6A, 0x5F, 0x7C, 0xD9, 0x7C, 0x13, 0xE4, 0x56, 0x5E, 0xE9, 0x60, 0x19, 0xE2, 0x7D, | ||||
|         0xC4, 0x41, 0x92, 0x8D, 0xDA, 0x21, 0x58, 0x20, 0xE9, 0xA8, 0x4C, 0x16, 0x34, 0x99, 0xAC, 0xB7, | ||||
|         0x30, 0xBD, 0x39, 0x19, 0xAC, 0x9B, 0x4B, 0x27, 0xFA, 0x32, 0xC1, 0x48, 0xA1, 0x80, 0x34, 0x36, | ||||
|         0x1E, 0xFB, 0x92, 0x43, 0x35, 0x72, 0x2D, 0xEF, 0xD2, 0xF2, 0xFC, 0xC2, 0x85, 0xAB, 0x59, 0x40, | ||||
|         0x8D, 0x9D, 0x1A, 0x1F, 0xE2, 0x92, 0x87, 0xA2, 0xF9, 0x2C, 0x78, 0xE4, 0xC3, 0x26, 0x56, 0x07, | ||||
|         0xB3, 0x78, 0xAF, 0x79, 0x3D, 0x88, 0xF4, 0xAD, 0x66, 0x7C, 0x07, 0x58, 0x98, 0x82, 0x1A, 0x26, | ||||
|         0xF7, 0xFD, 0xCE, 0xFF, 0x75, 0xED, 0xAB, 0xBD, 0xAE, 0x6D, 0x5C, 0x28, 0x91, 0xF3, 0xB7, 0x5C, | ||||
|         0x27, 0x05, 0xEC, 0x3B, 0xE3, 0xDD, 0x93, 0x24, 0x7F, 0xAD, 0x14, 0xAA, 0x49, 0x61, 0x8F, 0x96, | ||||
|         0x1F, 0xAA, 0xB2, 0xEE, 0xA8, 0x24, 0x41, 0x7C, 0xDC, 0xF1, 0x28, 0x26, 0xE6, 0x7F, 0x98, 0x20, | ||||
|         0x50, 0x5F, 0x90, 0x21, 0x8A, 0x09, 0x26, 0x59, 0xD0, 0x07, 0x2F, 0xE1, 0x35, 0x4D, 0x0B, 0x20, | ||||
|         0xB2, 0xD5, 0xDD, 0xB5, 0xAC, 0x1B, 0xFE, 0xD9, 0xE3, 0x35, 0xF1, 0xB8, 0x3F, 0x3D, 0xFC, 0x0B, | ||||
|         0x5A, 0x57, 0xA9, 0x92, 0x2B, 0xC8, 0x3E, 0xC2, 0xAA, 0xEF, 0xB9, 0x98, 0x2C, 0xA8, 0xAB, 0xF6, | ||||
|         0xA1, 0xBF, 0xBC, 0x8D, 0x97, 0xA2, 0x74, 0xD9, 0xE5, 0x99, 0x85, 0x81, 0x15, 0xB0, 0xE7, 0x8B, | ||||
|         0x48, 0x86, 0xF4, 0x94, 0x9C, 0x62, 0x82, 0xD1, 0x2C, 0x24, 0x4B, 0xAC, 0x7A, 0xB8, 0x4E, 0x4A, | ||||
|         0xD2, 0xF6, 0xAA, 0xED, 0xE0, 0x9C, 0x98, 0xD2, 0xDF, 0xC1, 0xBC, 0xBF, 0x55, 0x7D, 0x40, 0xB5, | ||||
|         0xDE, 0xD4, 0x25, 0xBB, 0x81, 0xF4, 0x07, 0x1D, 0xE7, 0x3C, 0xB4, 0x62, 0xC9, 0x55, 0x0A, 0x3A, | ||||
|         0xD5, 0xCE, 0x97, 0xED, 0x30, 0x76, 0x76, 0x51, 0xBC, 0x8C, 0xE4, 0x54, 0xBE, 0xB7, 0xB5, 0xCD, | ||||
|         0xF8, 0x76, 0x37, 0x53, 0x2C, 0x9F, 0xE4, 0xC7, 0xEB, 0xF5, 0x8D, 0x23, 0x8A, 0xDA, 0xD1, 0xA9, | ||||
|         0xD8, 0x4C, 0x53, 0xF3, 0x49, 0xA7, 0x1A, 0x5D, 0xE5, 0x03, 0x49, 0x52, 0xD3, 0xE2, 0x1F, 0xA5, | ||||
|         0x35, 0x9C, 0xBB, 0x0B, 0xC7, 0x0D, 0xA4, 0x65, 0x54, 0x8B, 0x39, 0xF1, 0x3B, 0x67, 0x21, 0x71, | ||||
|         0x10, 0xE7, 0x76, 0xC4, 0xA8, 0xC2, 0x9D, 0x93, 0xC6, 0x51, 0xBA, 0x23 | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										82
									
								
								decoder_modules/ryfi_decoder/src/ryfi/rs_codec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								decoder_modules/ryfi_decoder/src/ryfi/rs_codec.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
| #include <stddef.h> | ||||
| #include "dsp/processor.h" | ||||
|  | ||||
| extern "C" { | ||||
|     #include "correct.h" | ||||
| } | ||||
|  | ||||
| namespace ryfi { | ||||
|     // Size of an encoded reed-solomon block. | ||||
|     inline const int RS_BLOCK_ENC_SIZE  = 255; | ||||
|  | ||||
|     // Size of a decoded reed-solomon block. | ||||
|     inline const int RS_BLOCK_DEC_SIZE  = 223; | ||||
|  | ||||
|     // Number of reed-solomon blocks. | ||||
|     inline const int RS_BLOCK_COUNT     = 4; | ||||
|  | ||||
|     // Scrambler sequence | ||||
|     extern const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT]; | ||||
|  | ||||
|     /** | ||||
|      * RyFi Reed-Solomon Encoder. | ||||
|     */ | ||||
|     class RSEncoder : public dsp::Processor<uint8_t, uint8_t> { | ||||
|         using base_type = dsp::Processor<uint8_t, uint8_t>; | ||||
|     public: | ||||
|         /** | ||||
|          * Create a reed-solomon encoder specifying an input stream. | ||||
|          * @param in Input stream | ||||
|         */ | ||||
|         RSEncoder(dsp::stream<uint8_t>* in = NULL); | ||||
|  | ||||
|         // Destructor | ||||
|         ~RSEncoder(); | ||||
|  | ||||
|         /** | ||||
|          * Encode data. | ||||
|          * @param in Input bytes. | ||||
|          * @param out Output bytes. | ||||
|          * @param count Number of input bytes. | ||||
|          * @return Number of output bytes. | ||||
|         */ | ||||
|         int encode(const uint8_t* in, uint8_t* out, int count); | ||||
|  | ||||
|     private: | ||||
|         int run(); | ||||
|  | ||||
|         correct_reed_solomon* rs; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * RyFi Reed-Solomon Decoder. | ||||
|     */ | ||||
|     class RSDecoder : public dsp::Processor<uint8_t, uint8_t> { | ||||
|         using base_type = dsp::Processor<uint8_t, uint8_t>; | ||||
|     public: | ||||
|         /** | ||||
|          * Create a reed-solomon decoder specifying an input stream. | ||||
|          * @param in Input stream | ||||
|         */ | ||||
|         RSDecoder(dsp::stream<uint8_t>* in = NULL); | ||||
|  | ||||
|         // Destructor | ||||
|         ~RSDecoder(); | ||||
|  | ||||
|         /** | ||||
|          * Decode data. | ||||
|          * @param in Input bytes. | ||||
|          * @param out Output bytes. | ||||
|          * @param count Number of input bytes. | ||||
|          * @return Number of output bytes. | ||||
|         */ | ||||
|         int decode(uint8_t* in, uint8_t* out, int count); | ||||
|  | ||||
|     private: | ||||
|         int run(); | ||||
|  | ||||
|         correct_reed_solomon* rs; | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										177
									
								
								decoder_modules/ryfi_decoder/src/ryfi/transmitter.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								decoder_modules/ryfi_decoder/src/ryfi/transmitter.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | ||||
| #include "transmitter.h" | ||||
|  | ||||
| namespace ryfi { | ||||
|     Transmitter::Transmitter(double baudrate, double samplerate) { | ||||
|         // Initialize the DSP | ||||
|         rs.setInput(&in); | ||||
|         conv.setInput(&rs.out); | ||||
|         framer.setInput(&conv.out); | ||||
|         resamp.init(&framer.out, baudrate, samplerate); | ||||
|  | ||||
|         rrcTaps = dsp::taps::rootRaisedCosine<float>(511, 0.6, baudrate, samplerate); | ||||
|         // Normalize the taps | ||||
|         float tot = 0.0f; | ||||
|         for (int i = 0; i < rrcTaps.size; i++) { | ||||
|             tot += rrcTaps.taps[i]; | ||||
|         } | ||||
|         for (int i = 0; i < rrcTaps.size; i++) { | ||||
|             rrcTaps.taps[i] /= tot; | ||||
|         } | ||||
|  | ||||
|         rrc.init(&resamp.out, rrcTaps); | ||||
|         out = &rrc.out; | ||||
|     } | ||||
|  | ||||
|     Transmitter::~Transmitter() { | ||||
|         // Stop everything | ||||
|         stop(); | ||||
|     } | ||||
|  | ||||
|     void Transmitter::start() { | ||||
|         // Do nothing if already running | ||||
|         if (running) { return; } | ||||
|  | ||||
|         // Start the worker thread | ||||
|         workerThread = std::thread(&Transmitter::worker, this); | ||||
|  | ||||
|         // Start the DSP | ||||
|         rs.start(); | ||||
|         conv.start(); | ||||
|         framer.start(); | ||||
|         resamp.start(); | ||||
|         rrc.start(); | ||||
|  | ||||
|         // Update the running state | ||||
|         running = true; | ||||
|     } | ||||
|  | ||||
|     void Transmitter::stop() { | ||||
|         // Do nothing if not running | ||||
|         if (!running) { return; } | ||||
|  | ||||
|         // Stop the worker thread | ||||
|         in.stopWriter(); | ||||
|         if (workerThread.joinable()) { workerThread.join(); } | ||||
|         in.clearWriteStop(); | ||||
|  | ||||
|         // Stop the DSP | ||||
|         rs.stop(); | ||||
|         conv.stop(); | ||||
|         framer.stop(); | ||||
|         resamp.stop(); | ||||
|         rrc.stop(); | ||||
|  | ||||
|         // Update the running state | ||||
|         running = false; | ||||
|     } | ||||
|  | ||||
|     bool Transmitter::send(const Packet& pkt) { | ||||
|         // Acquire the packet queue | ||||
|         std::lock_guard<std::mutex> lck(packetsMtx); | ||||
|  | ||||
|         // If there are too many packets queued up, drop the packet | ||||
|         if (packets.size() >= MAX_QUEUE_SIZE) { return false; } | ||||
|  | ||||
|         // Push the packet onto the queue | ||||
|         packets.push(pkt); | ||||
|     } | ||||
|  | ||||
|     bool Transmitter::txFrame(const Frame& frame) { | ||||
|         // Serialize the frame | ||||
|         int count = frame.serialize(in.writeBuf); | ||||
|  | ||||
|         // Send it off | ||||
|         return in.swap(count); | ||||
|     } | ||||
|  | ||||
|     Packet Transmitter::popPacket() { | ||||
|         // Acquire the packet queue | ||||
|         std::unique_lock<std::mutex> lck(packetsMtx); | ||||
|  | ||||
|         // If no packets are available, return empty packet | ||||
|         if (!packets.size()) { return Packet(); } | ||||
|  | ||||
|         // Pop the front packet and return it | ||||
|         Packet pkt = packets.front(); | ||||
|         packets.pop(); | ||||
|         return pkt; | ||||
|     } | ||||
|  | ||||
|     void Transmitter::worker() { | ||||
|         Frame frame; | ||||
|         Packet pkt; | ||||
|         uint16_t counter = 0; | ||||
|         int pktToWrite = 0; | ||||
|         int pktWritten = 0; | ||||
|         uint8_t* pktBuffer = new uint8_t[Packet::MAX_SERIALIZED_SIZE]; | ||||
|  | ||||
|         while (true) { | ||||
|             // Initialize the frame | ||||
|             frame.counter = counter++; | ||||
|             frame.firstPacket = PKT_OFFS_NONE; | ||||
|             frame.lastPacket = PKT_OFFS_NONE; | ||||
|             int frameOffset = 0; | ||||
|  | ||||
|             // Fill the frame with as much packet data as possible | ||||
|             while (frameOffset < sizeof(Frame::content)) { | ||||
|                 // If there is no packet in the process of being sent | ||||
|                 if (!pktWritten) { | ||||
|                     // If there is not enough space for the size of the packet | ||||
|                     if ((sizeof(Frame::content) - frameOffset) < 2) { | ||||
|                         // Fill the rest of the frame with noise and send it | ||||
|                         for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); } | ||||
|                         break; | ||||
|                     } | ||||
|  | ||||
|                     // Get the next packet | ||||
|                     pkt = popPacket(); | ||||
|  | ||||
|                     // If there was an available packet | ||||
|                     if (pkt) { | ||||
|                         // Serialize the packet | ||||
|                         pktToWrite = pkt.serializedSize(); | ||||
|                         pkt.serialize(pktBuffer); | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // If none was available | ||||
|                 if (!pkt) { | ||||
|                     // Fill the rest of the frame with noise and send it | ||||
|                     for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); } | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
|                 // If this is the beginning of the packet | ||||
|                 if (!pktWritten) { | ||||
|                     //flog::debug("Starting to write a {} byte packet at offset {}", pktToWrite-2, frameOffset); | ||||
|  | ||||
|                     // If this is the first packet of the frame, update its offset | ||||
|                     if (frame.firstPacket == PKT_OFFS_NONE) { frame.firstPacket = frameOffset; } | ||||
|  | ||||
|                     // Update the last packet pointer | ||||
|                     frame.lastPacket = frameOffset; | ||||
|                 } | ||||
|  | ||||
|                 // Compute the amount of data writeable to the frame | ||||
|                 int writeable = std::min<int>(pktToWrite - pktWritten, sizeof(Frame::content) - frameOffset); | ||||
|  | ||||
|                 // Copy the data to the frame | ||||
|                 memcpy(&frame.content[frameOffset], &pktBuffer[pktWritten], writeable); | ||||
|                 pktWritten += writeable; | ||||
|                 frameOffset += writeable; | ||||
|  | ||||
|                 // If the packet is done being sent | ||||
|                 if (pktWritten >= pktToWrite) { | ||||
|                     // Prepare for a new packet | ||||
|                     pktToWrite = 0; | ||||
|                     pktWritten = 0; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Send the frame | ||||
|             if (!txFrame(frame)) { break; } | ||||
|         } | ||||
|  | ||||
|         delete[] pktBuffer; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										69
									
								
								decoder_modules/ryfi_decoder/src/ryfi/transmitter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								decoder_modules/ryfi_decoder/src/ryfi/transmitter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| #pragma once | ||||
| #include "dsp/multirate/rational_resampler.h" | ||||
| #include "dsp/taps/root_raised_cosine.h" | ||||
| #include "dsp/filter/fir.h" | ||||
| #include "packet.h" | ||||
| #include "frame.h" | ||||
| #include "rs_codec.h" | ||||
| #include "conv_codec.h" | ||||
| #include "framing.h" | ||||
| #include <queue> | ||||
| #include <mutex> | ||||
|  | ||||
| namespace ryfi { | ||||
|     class Transmitter { | ||||
|     public: | ||||
|         /** | ||||
|          * Create a transmitter. | ||||
|          * @param baudrate Baudrate to use over the air. | ||||
|          * @param samplerate Samplerate of the baseband. | ||||
|         */ | ||||
|         Transmitter(double baudrate, double samplerate); | ||||
|  | ||||
|         // Destructor | ||||
|         ~Transmitter(); | ||||
|  | ||||
|         /** | ||||
|          * Start the transmitter's DSP. | ||||
|         */ | ||||
|         void start(); | ||||
|  | ||||
|         /** | ||||
|          * Stop the transmitter's DSP. | ||||
|         */ | ||||
|         void stop(); | ||||
|  | ||||
|         /** | ||||
|          * Send a packet. | ||||
|          * @param pkg Packet to send. | ||||
|          * @return True if the packet was send, false if it was dropped. | ||||
|         */ | ||||
|         bool send(const Packet& pkt); | ||||
|  | ||||
|         // Baseband output | ||||
|         dsp::stream<dsp::complex_t>* out; | ||||
|  | ||||
|         static inline const int MAX_QUEUE_SIZE  = 32; | ||||
|  | ||||
|     private: | ||||
|         bool txFrame(const Frame& frame); | ||||
|         Packet popPacket(); | ||||
|         void worker(); | ||||
|  | ||||
|         // Packet queue | ||||
|         std::mutex packetsMtx; | ||||
|         std::queue<Packet> packets; | ||||
|  | ||||
|         // DSP | ||||
|         dsp::stream<uint8_t> in; | ||||
|         RSEncoder rs; | ||||
|         ConvEncoder conv; | ||||
|         Framer framer; | ||||
|         dsp::multirate::RationalResampler<dsp::complex_t> resamp; | ||||
|         dsp::tap<float> rrcTaps; | ||||
|         dsp::filter::FIR<dsp::complex_t, float> rrc; | ||||
|  | ||||
|         bool running = false; | ||||
|         std::thread workerThread; | ||||
|     }; | ||||
| } | ||||
| @@ -6,13 +6,13 @@ cd /root | ||||
| apt update | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \ | ||||
|             libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|             libcodec2-dev autoconf libtool xxd libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0 | ||||
| cp x86_64/libsdrplay_api.so.3.14 /usr/lib/libsdrplay_api.so | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so | ||||
| cp inc/* /usr/include/ | ||||
|  | ||||
| # Install libperseus | ||||
| @@ -25,10 +25,30 @@ make install | ||||
| ldconfig | ||||
| cd .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| cd SDRPlusPlus | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| cd .. | ||||
|   | ||||
| @@ -6,13 +6,13 @@ cd /root | ||||
| apt update | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \ | ||||
|             libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|             libcodec2-dev autoconf libtool xxd libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0 | ||||
| cp x86_64/libsdrplay_api.so.3.14 /usr/lib/libsdrplay_api.so | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so | ||||
| cp inc/* /usr/include/ | ||||
|  | ||||
| # Install libperseus | ||||
| @@ -25,10 +25,30 @@ make install | ||||
| ldconfig | ||||
| cd .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| cd SDRPlusPlus | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| cd .. | ||||
|   | ||||
| @@ -6,13 +6,13 @@ cd /root | ||||
| apt update | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspyhf-dev libairspy-dev \ | ||||
|             libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|             libcodec2-dev autoconf libtool xxd libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0 | ||||
| cp x86_64/libsdrplay_api.so.3.14 /usr/lib/libsdrplay_api.so | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so | ||||
| cp inc/* /usr/include/ | ||||
|  | ||||
| # Install libperseus | ||||
| @@ -25,10 +25,30 @@ make install | ||||
| ldconfig | ||||
| cd .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| cd SDRPlusPlus | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON | ||||
| cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| cd .. | ||||
|   | ||||
| @@ -6,13 +6,13 @@ cd /root | ||||
| apt update | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \ | ||||
|             libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|             libcodec2-dev autoconf libtool xxd libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0 | ||||
| cp x86_64/libsdrplay_api.so.3.14 /usr/lib/libsdrplay_api.so | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so | ||||
| cp inc/* /usr/include/ | ||||
|  | ||||
| # Install libperseus | ||||
| @@ -25,10 +25,30 @@ make install | ||||
| ldconfig | ||||
| cd .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| cd SDRPlusPlus | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| cd .. | ||||
|   | ||||
| @@ -12,13 +12,13 @@ apt update | ||||
| # Install dependencies and tools | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspy-dev \ | ||||
|             libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ | ||||
|             libcodec2-dev libudev-dev autoconf libtool xxd | ||||
|             libcodec2-dev libudev-dev autoconf libtool xxd libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0 | ||||
| cp x86_64/libsdrplay_api.so.3.14 /usr/lib/libsdrplay_api.so | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so | ||||
| cp inc/* /usr/include/ | ||||
|  | ||||
| # Install a more recent libusb version | ||||
| @@ -51,6 +51,26 @@ make install | ||||
| ldconfig | ||||
| cd .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Fix missing .pc file for codec2 | ||||
| echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc | ||||
| echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc | ||||
| @@ -66,7 +86,7 @@ echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc | ||||
| cd SDRPlusPlus | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON | ||||
| cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| # Generate package | ||||
|   | ||||
| @@ -6,13 +6,13 @@ cd /root | ||||
| apt update | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \ | ||||
|             libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|             libcodec2-dev autoconf libtool xxd libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0 | ||||
| cp x86_64/libsdrplay_api.so.3.14 /usr/lib/libsdrplay_api.so | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so | ||||
| cp inc/* /usr/include/ | ||||
|  | ||||
| # Install libperseus | ||||
| @@ -25,10 +25,30 @@ make install | ||||
| ldconfig | ||||
| cd .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| cd SDRPlusPlus | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| cd .. | ||||
|   | ||||
| @@ -6,13 +6,13 @@ cd /root | ||||
| apt update | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \ | ||||
|             libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|             libcodec2-dev autoconf libtool xxd libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0 | ||||
| cp x86_64/libsdrplay_api.so.3.14 /usr/lib/libsdrplay_api.so | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so | ||||
| cp inc/* /usr/include/ | ||||
|  | ||||
| # Install libperseus | ||||
| @@ -25,10 +25,30 @@ make install | ||||
| ldconfig | ||||
| cd .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| cd SDRPlusPlus | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| cd .. | ||||
|   | ||||
| @@ -6,13 +6,13 @@ cd /root | ||||
| apt update | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \ | ||||
|             libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \ | ||||
|             libcodec2-dev autoconf libtool xxd | ||||
|             libcodec2-dev autoconf libtool xxd libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.14.0 | ||||
| cp x86_64/libsdrplay_api.so.3.14 /usr/lib/libsdrplay_api.so | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so | ||||
| cp inc/* /usr/include/ | ||||
|  | ||||
| # Install libperseus | ||||
| @@ -25,10 +25,30 @@ make install | ||||
| ldconfig | ||||
| cd .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| cd SDRPlusPlus | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON | ||||
| cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| cd .. | ||||
|   | ||||
							
								
								
									
										4
									
								
								docker_builds/ubuntu_noble/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								docker_builds/ubuntu_noble/Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| FROM ubuntu:noble | ||||
| ENV DEBIAN_FRONTEND=noninteractive  | ||||
| COPY do_build.sh /root | ||||
| RUN chmod +x /root/do_build.sh | ||||
							
								
								
									
										55
									
								
								docker_builds/ubuntu_noble/do_build.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								docker_builds/ubuntu_noble/do_build.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| #!/bin/bash | ||||
| set -e | ||||
| cd /root | ||||
|  | ||||
| # Install dependencies and tools | ||||
| apt update | ||||
| apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-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 libspdlog-dev | ||||
|  | ||||
| # Install SDRPlay libraries | ||||
| wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1.run | ||||
| 7z x ./SDRplay_RSP_API-Linux-3.15.1 | ||||
| cp x86_64/libsdrplay_api.so.3.15 /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 .. | ||||
|  | ||||
| # Install librfnm | ||||
| git clone https://github.com/AlexandreRouma/librfnm | ||||
| cd librfnm | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| cd ../../ | ||||
|  | ||||
| # Install libfobos | ||||
| git clone https://github.com/AlexandreRouma/libfobos | ||||
| cd libfobos | ||||
| mkdir build | ||||
| cd build | ||||
| cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr | ||||
| make -j2 | ||||
| make install | ||||
| 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 -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON | ||||
| make VERBOSE=1 -j2 | ||||
|  | ||||
| cd .. | ||||
| sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev' | ||||
| @@ -8,7 +8,7 @@ mkdir sdrpp_debian_amd64/DEBIAN | ||||
| # Create package info | ||||
| echo Create package info | ||||
| echo Package: sdrpp >> sdrpp_debian_amd64/DEBIAN/control | ||||
| echo Version: 1.1.0$BUILD_NO >> sdrpp_debian_amd64/DEBIAN/control | ||||
| echo Version: 1.2.0$BUILD_NO >> sdrpp_debian_amd64/DEBIAN/control | ||||
| echo Maintainer: Ryzerth >> sdrpp_debian_amd64/DEBIAN/control | ||||
| echo Architecture: all >> sdrpp_debian_amd64/DEBIAN/control | ||||
| echo Description: Bloat-free SDR receiver software >> sdrpp_debian_amd64/DEBIAN/control | ||||
|   | ||||
| @@ -22,7 +22,7 @@ cp -R root/res/* $BUNDLE/Contents/Resources/ | ||||
| bundle_create_icns root/res/icons/sdrpp.macos.png $BUNDLE/Contents/Resources/sdrpp | ||||
|  | ||||
| # Create the property list | ||||
| bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.1.0 sdrp sdrpp sdrpp $BUNDLE/Contents/Info.plist | ||||
| bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.2.0 sdrp sdrpp sdrpp $BUNDLE/Contents/Info.plist | ||||
|  | ||||
| # ========================= Install binaries ========================= | ||||
|  | ||||
| @@ -35,11 +35,13 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules | ||||
| bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib | ||||
| bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib | ||||
| bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib | ||||
| bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/fobossdr_source.dylib | ||||
| 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/rfnm_source/rfnm_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 | ||||
| bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_tcp_source/rtl_tcp_source.dylib | ||||
|   | ||||
| @@ -24,6 +24,9 @@ cp 'C:/Program Files/PothosSDR/bin/bladeRF.dll' sdrpp_windows_x64/ | ||||
|  | ||||
| cp $build_dir/source_modules/file_source/Release/file_source.dll sdrpp_windows_x64/modules/ | ||||
|  | ||||
| cp $build_dir/source_modules/fobossdr_source/Release/fobossdr_source.dll sdrpp_windows_x64/modules/ | ||||
| cp 'C:/Program Files/RigExpert/Fobos/bin/fobos.dll' sdrpp_windows_x64/ | ||||
|  | ||||
| cp $build_dir/source_modules/hackrf_source/Release/hackrf_source.dll sdrpp_windows_x64/modules/ | ||||
| cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/ | ||||
|  | ||||
| @@ -39,6 +42,11 @@ cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_w | ||||
| cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/ | ||||
| cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/ | ||||
|  | ||||
| cp $build_dir/source_modules/rfnm_source/Release/rfnm_source.dll sdrpp_windows_x64/modules/ | ||||
| cp 'C:/Program Files/RFNM/bin/rfnm.dll' sdrpp_windows_x64/ | ||||
| cp 'C:/Program Files/RFNM/bin/spdlog.dll' sdrpp_windows_x64/ | ||||
| cp 'C:/Program Files/RFNM/bin/fmt.dll' sdrpp_windows_x64/ | ||||
|  | ||||
| cp $build_dir/source_modules/rfspace_source/Release/rfspace_source.dll sdrpp_windows_x64/modules/ | ||||
|  | ||||
| cp $build_dir/source_modules/rtl_sdr_source/Release/rtl_sdr_source.dll sdrpp_windows_x64/modules/ | ||||
|   | ||||
| @@ -249,7 +249,6 @@ private: | ||||
|         } | ||||
|         ImGui::Columns(1, CONCAT("EndRecorderModeColumns##_", _this->name), false); | ||||
|         ImGui::EndGroup(); | ||||
|         if (_this->recording) { style::endDisabled(); } | ||||
|  | ||||
|         // Recording path | ||||
|         if (_this->folderSelect.render("##_recorder_fold_" + _this->name)) { | ||||
| @@ -284,8 +283,11 @@ private: | ||||
|             config.release(true); | ||||
|         } | ||||
|  | ||||
|         if (_this->recording) { style::endDisabled(); } | ||||
|  | ||||
|         // Show additional audio options | ||||
|         if (_this->recMode == RECORDER_MODE_AUDIO) { | ||||
|             if (_this->recording) { style::beginDisabled(); } | ||||
|             ImGui::LeftLabel("Stream"); | ||||
|             ImGui::FillWidth(); | ||||
|             if (ImGui::Combo(CONCAT("##_recorder_stream_", _this->name), &_this->streamId, _this->audioStreams.txt)) { | ||||
| @@ -294,6 +296,7 @@ private: | ||||
|                 config.conf[_this->name]["audioStream"] = _this->audioStreams.key(_this->streamId); | ||||
|                 config.release(true); | ||||
|             } | ||||
|             if (_this->recording) { style::endDisabled(); } | ||||
|  | ||||
|             _this->updateAudioMeter(_this->audioLvl); | ||||
|             ImGui::FillWidth(); | ||||
| @@ -476,9 +479,9 @@ private: | ||||
|         sprintf(monStr, "%02d", ltm->tm_mon + 1); | ||||
|         sprintf(yearStr, "%02d", ltm->tm_year + 1900); | ||||
|         if (core::modComManager.getModuleName(name) == "radio") { | ||||
|             int mode; | ||||
|             int mode = -1; | ||||
|             core::modComManager.callInterface(name, RADIO_IFACE_CMD_GET_MODE, NULL, &mode); | ||||
|             modeStr = radioModeToString[mode]; | ||||
|             if (mode >= 0) { modeStr = radioModeToString[mode]; }; | ||||
|         } | ||||
|  | ||||
|         // Replace in template | ||||
|   | ||||
							
								
								
									
										15
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								readme.md
									
									
									
									
									
								
							| @@ -41,14 +41,13 @@ To create a desktop shortcut, rightclick the exe and select `Send to -> Desktop | ||||
|  | ||||
| Download the latest release from [the Releases page](https://github.com/AlexandreRouma/SDRPlusPlus/releases) and extract to the directory of your choice. | ||||
|  | ||||
| Then, run: | ||||
| Then, use apt to install it: | ||||
|  | ||||
| ```sh | ||||
| sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev | ||||
| sudo dpkg -i sdrpp_debian_amd64.deb | ||||
| sudo apt install path/to/the/sdrpp_debian_amd64.deb | ||||
| ``` | ||||
|  | ||||
| If `libvolk2-dev` is not available, use `libvolk1-dev`. | ||||
| **IMPORTANT: You must install the drivers for your SDR. Follow instructions from your manufacturer as to how to do this on your particular distro.** | ||||
|  | ||||
| ### Arch-based | ||||
|  | ||||
| @@ -325,12 +324,15 @@ Modules in beta are still included in releases for the most part but not enabled | ||||
| | audio_source         | Working    | rtaudio           | OPT_BUILD_AUDIO_SOURCE         | ✅              | ✅                     | ✅                         | | ||||
| | bladerf_source       | Working    | libbladeRF        | OPT_BUILD_BLADERF_SOURCE       | ⛔              | ✅ (not Debian Buster) | ✅                         | | ||||
| | file_source          | Working    | -                 | OPT_BUILD_FILE_SOURCE          | ✅              | ✅                     | ✅                         | | ||||
| | fobossdr_source      | Beta       | libfobos          | OPT_BUILD_FOBOSSDR_SOURCE      | ✅              | ✅                     | ✅                         | | ||||
| | hackrf_source        | Working    | libhackrf         | OPT_BUILD_HACKRF_SOURCE        | ✅              | ✅                     | ✅                         | | ||||
| | harogic_source       | Beta       | htra_api          | OPT_BUILD_HAROGIC_SOURCE       | ⛔              | ⛔                     | ✅                         | | ||||
| | hermes_source        | Beta       | -                 | OPT_BUILD_HERMES_SOURCE        | ✅              | ✅                     | ✅                         | | ||||
| | limesdr_source       | Working    | liblimesuite      | OPT_BUILD_LIMESDR_SOURCE       | ⛔              | ✅                     | ✅                         | | ||||
| | network_source       | Unfinished | -                 | OPT_BUILD_NETWORK_SOURCE       | ✅              | ✅                     | ⛔                         | | ||||
| | perseus_source       | Beta       | libperseus-sdr    | OPT_BUILD_PERSEUS_SOURCE       | ⛔              | ✅                     | ✅                         | | ||||
| | plutosdr_source      | Working    | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE      | ✅              | ✅                     | ✅                         | | ||||
| | rfnm_source          | Beta       | librfnm           | OPT_BUILD_RFNM_SOURCE          | ⛔              | ✅                     | ✅                         | | ||||
| | rfspace_source       | Working    | -                 | OPT_BUILD_RFSPACE_SOURCE       | ✅              | ✅                     | ✅                         | | ||||
| | rtl_sdr_source       | Working    | librtlsdr         | OPT_BUILD_RTL_SDR_SOURCE       | ✅              | ✅                     | ✅                         | | ||||
| | rtl_tcp_source       | Working    | -                 | OPT_BUILD_RTL_TCP_SOURCE       | ✅              | ✅                     | ✅                         | | ||||
| @@ -338,9 +340,9 @@ Modules in beta are still included in releases for the most part but not enabled | ||||
| | sdrpp_server_source  | Working    | -                 | OPT_BUILD_SDRPP_SERVER_SOURCE  | ✅              | ✅                     | ✅                         | | ||||
| | soapy_source         | Deprecated | soapysdr          | OPT_BUILD_SOAPY_SOURCE         | ⛔              | ⛔                     | ⛔                         | | ||||
| | spectran_source      | Unfinished | RTSA Suite        | OPT_BUILD_SPECTRAN_SOURCE      | ⛔              | ⛔                     | ⛔                         | | ||||
| | spectran_http_source | Beta       | -                 | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅              | ✅                     | ⛔                         | | ||||
| | spectran_http_source | Beta       | -                 | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅              | ✅                     | ✅                         | | ||||
| | spyserver_source     | Working    | -                 | OPT_BUILD_SPYSERVER_SOURCE     | ✅              | ✅                     | ✅                         | | ||||
| | usrp_source          | Beta       | libuhd            | OPT_BUILD_USRP_SOURCE          | ⛔              | ⛔                     | ⛔                         | | ||||
| | usrp_source          | Beta       | libuhd            | OPT_BUILD_USRP_SOURCE          | ⛔              | ⛔                     | ✅                         | | ||||
|  | ||||
| ## Sinks | ||||
|  | ||||
| @@ -357,6 +359,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 | | ||||
| |---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:| | ||||
| | atv_decoder         | Unfinished | -            | OPT_BUILD_ATV_DECODER         | ⛔              | ⛔              | ⛔                         | | ||||
| | dab_decoder         | Unfinished | -            | OPT_BUILD_DAB_DECODER         | ⛔              | ⛔              | ⛔                         | | ||||
| | falcon9_decoder     | Unfinished | ffplay       | OPT_BUILD_FALCON9_DECODER     | ⛔              | ⛔              | ⛔                         | | ||||
| | kgsstv_decoder      | Unfinished | -            | OPT_BUILD_KGSSTV_DECODER      | ⛔              | ⛔              | ⛔                         | | ||||
| | m17_decoder         | Working    | -            | OPT_BUILD_M17_DECODER         | ⛔              | ✅              | ⛔                         | | ||||
|   | ||||
							
								
								
									
										645
									
								
								root/res/bandplans/brazil.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										645
									
								
								root/res/bandplans/brazil.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,645 @@ | ||||
| { | ||||
|     "name": "Brazilian Ham Bands", | ||||
|     "country_name": "Brazil", | ||||
|     "country_code": "BR", | ||||
|     "author_name": "Rafael Beraldo", | ||||
|     "author_url": "https://github.com/rberaldo/", | ||||
|     "bands": [ | ||||
|         { | ||||
|             "start": 135700, | ||||
|             "end": 137800, | ||||
|             "type": "amateur", | ||||
|             "name": "2200m Ham Band CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 472000, | ||||
|             "end": 479000, | ||||
|             "type": "amateur", | ||||
|             "name": "635m Ham Band CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 1800000, | ||||
|             "end": 1810000, | ||||
|             "type": "amateur", | ||||
|             "name": "|160m Ham Band CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 1810000, | ||||
|             "end": 1839000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW" | ||||
|         }, | ||||
|         { | ||||
|             "start": 1839000, | ||||
|             "end": 1840000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 1840000, | ||||
|             "end": 1843000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 1843000, | ||||
|             "end": 1850000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB" | ||||
|         }, | ||||
|         { | ||||
|             "start": 1850000, | ||||
|             "end": 2000000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, AM, DV, Digital 160 Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 3500000, | ||||
|             "end": 3570000, | ||||
|             "type": "amateur", | ||||
|             "name": "|80m Ham Band CW" | ||||
|         }, | ||||
|         { | ||||
|             "start": 3570000, | ||||
|             "end": 3590000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 3590000, | ||||
|             "end": 3600000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSD, AM, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 3600000, | ||||
|             "end": 3775000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSD, AM, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 3775000, | ||||
|             "end": 3875000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSD, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 3775000, | ||||
|             "end": 3875000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSD, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 3875000, | ||||
|             "end": 4000000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSD, AM, DV, Digital, 80m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 5351500, | ||||
|             "end": 5354000, | ||||
|             "type": "amateur", | ||||
|             "name": "|60m Ham Band CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 5354000, | ||||
|             "end": 5366000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 5366000, | ||||
|             "end": 5366500, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, Digital 60m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 7000000, | ||||
|             "end": 7040000, | ||||
|             "type": "amateur", | ||||
|             "name": "|40m Ham Band CW" | ||||
|         }, | ||||
|         { | ||||
|             "start": 7040000, | ||||
|             "end": 7047000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 7047000, | ||||
|             "end": 7050000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 7050000, | ||||
|             "end": 7100000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 7100000, | ||||
|             "end": 7300000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB, AM, DV, Digital 40m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 10100000, | ||||
|             "end": 10130000, | ||||
|             "type": "amateur", | ||||
|             "name": "|30m Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 10130000, | ||||
|             "end": 10150000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital 30m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 14000000, | ||||
|             "end": 14070000, | ||||
|             "type": "amateur", | ||||
|             "name": "|20m Ham Band CW" | ||||
|         }, | ||||
|         { | ||||
|             "start": 14070000, | ||||
|             "end": 14099000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 14099000, | ||||
|             "end": 14101000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW IBP" | ||||
|         }, | ||||
|         { | ||||
|             "start": 14101000, | ||||
|             "end": 14282000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 14285000, | ||||
|             "end": 14350000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB, AM, DV, Digital 20m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 18068000, | ||||
|             "end": 18095000, | ||||
|             "type": "amateur", | ||||
|             "name": "|17m Ham Band CW" | ||||
|         }, | ||||
|         { | ||||
|             "start": 18095000, | ||||
|             "end": 18109000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 18109000, | ||||
|             "end": 18111000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW IBP" | ||||
|         }, | ||||
|         { | ||||
|             "start": 18111000, | ||||
|             "end": 18168000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, DV, Digital 17m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 21000000, | ||||
|             "end": 21070000, | ||||
|             "type": "amateur", | ||||
|             "name": "|15m Ham Band CW" | ||||
|         }, | ||||
|         { | ||||
|             "start": 21070000, | ||||
|             "end": 21149000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 21149000, | ||||
|             "end": 21151000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, IBP" | ||||
|         }, | ||||
|         { | ||||
|             "start": 21151000, | ||||
|             "end": 21380000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 21380000, | ||||
|             "end": 21450000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB, AM, DV, Digital 15m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 24890000, | ||||
|             "end": 24915000, | ||||
|             "type": "amateur", | ||||
|             "name": "|12m Ham Band CW" | ||||
|         }, | ||||
|         { | ||||
|             "start": 24915000, | ||||
|             "end": 24929000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 24929000, | ||||
|             "end": 24931000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW IBP" | ||||
|         }, | ||||
|         { | ||||
|             "start": 24931000, | ||||
|             "end": 24990000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, DV, Digital 12m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 28000000, | ||||
|             "end": 28070000, | ||||
|             "type": "amateur", | ||||
|             "name": "|10m Ham Band CW" | ||||
|         }, | ||||
|         { | ||||
|             "start": 28070000, | ||||
|             "end": 28190000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 28190000, | ||||
|             "end": 28199000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW - Pilot Emissions" | ||||
|         }, | ||||
|         { | ||||
|             "start": 28199000, | ||||
|             "end": 28201000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW IBP" | ||||
|         }, | ||||
|         { | ||||
|             "start": 28201000, | ||||
|             "end": 28225000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW - Pilot Emissions" | ||||
|         }, | ||||
|         { | ||||
|             "start": 28225000, | ||||
|             "end": 28300000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital - Pilot Emissions" | ||||
|         }, | ||||
|         { | ||||
|             "start": 28300000, | ||||
|             "end": 29000000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 29000000, | ||||
|             "end": 29300000, | ||||
|             "type": "amateur1", | ||||
|             "name": "All Modes" | ||||
|         }, | ||||
|         { | ||||
|             "start": 29300000, | ||||
|             "end": 29510000, | ||||
|             "type": "amateur", | ||||
|             "name": "All Modes - Satellites" | ||||
|         }, | ||||
|         { | ||||
|             "start": 29510000, | ||||
|             "end": 29520000, | ||||
|             "type": "amateur1", | ||||
|             "name": "All Modes" | ||||
|         }, | ||||
|         { | ||||
|             "start": 29520000, | ||||
|             "end": 29590000, | ||||
|             "type": "amateur", | ||||
|             "name": "FM, DV - Repeater input" | ||||
|         }, | ||||
|         { | ||||
|             "start": 29590000, | ||||
|             "end": 29620000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, FM, DV - FM calling freq: 29.600 kHz" | ||||
|         }, | ||||
|         { | ||||
|             "start": 29620000, | ||||
|             "end": 29700000, | ||||
|             "type": "amateur", | ||||
|             "name": "FM, DV - Repeater output 10m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 50000000, | ||||
|             "end": 54000000, | ||||
|             "type": "amateur", | ||||
|             "name": "6m Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144000000, | ||||
|             "end": 144025000, | ||||
|             "type": "amateur", | ||||
|             "name": "|2m Ham Band All Modes - Satellites" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144025000, | ||||
|             "end": 144110000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW - EME" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144110000, | ||||
|             "end": 144150000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, Digital - EME" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144150000, | ||||
|             "end": 144180000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144180000, | ||||
|             "end": 144275000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB - Calling freq: 144.2 MHz" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144275000, | ||||
|             "end": 144300000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW - Pilot Emissions" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144300000, | ||||
|             "end": 144360000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB - Calling freq: 144.2 MHz" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144360000, | ||||
|             "end": 144400000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144400000, | ||||
|             "end": 144600000, | ||||
|             "type": "amateur", | ||||
|             "name": "All Modes" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144600000, | ||||
|             "end": 144900000, | ||||
|             "type": "amateur1", | ||||
|             "name": "FM, DV - Repeater input" | ||||
|         }, | ||||
|         { | ||||
|             "start": 144900000, | ||||
|             "end": 145000000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, FM, DV, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 145000000, | ||||
|             "end": 145200000, | ||||
|             "type": "amateur1", | ||||
|             "name": "All Modes, IVG" | ||||
|         }, | ||||
|         { | ||||
|             "start": 145200000, | ||||
|             "end": 145500000, | ||||
|             "type": "amateur", | ||||
|             "name": "FM, DV - Repeater output" | ||||
|         }, | ||||
|         { | ||||
|             "start": 145500000, | ||||
|             "end": 145565000, | ||||
|             "type": "amateur1", | ||||
|             "name": "All Modes" | ||||
|         }, | ||||
|         { | ||||
|             "start": 145565000, | ||||
|             "end": 145575000, | ||||
|             "type": "amateur", | ||||
|             "name": "APRS" | ||||
|         }, | ||||
|         { | ||||
|             "start": 145575000, | ||||
|             "end": 145790000, | ||||
|             "type": "amateur1", | ||||
|             "name": "All Modes" | ||||
|         }, | ||||
|         { | ||||
|             "start": 145790000, | ||||
|             "end": 145800000, | ||||
|             "type": "amateur", | ||||
|             "name": "Guard Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 145800000, | ||||
|             "end": 146000000, | ||||
|             "type": "amateur1", | ||||
|             "name": "All Modes - Satellites" | ||||
|         }, | ||||
|         { | ||||
|             "start": 146000000, | ||||
|             "end": 146390000, | ||||
|             "type": "amateur", | ||||
|             "name": "FM, DV - Repeater input" | ||||
|         }, | ||||
|         { | ||||
|             "start": 146390000, | ||||
|             "end": 146600000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, FM, DV - Calling freq: 146.52 MHz" | ||||
|         }, | ||||
|         { | ||||
|             "start": 146600000, | ||||
|             "end": 146990000, | ||||
|             "type": "amateur", | ||||
|             "name": "FM, DV - Repeater output" | ||||
|         }, | ||||
|         { | ||||
|             "start": 146990000, | ||||
|             "end": 147400000, | ||||
|             "type": "amateur1", | ||||
|             "name": "FM, DV - Repeater input" | ||||
|         }, | ||||
|         { | ||||
|             "start": 147400000, | ||||
|             "end": 147590000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, FM, DV" | ||||
|         }, | ||||
|         { | ||||
|             "start": 147590000, | ||||
|             "end": 148000000, | ||||
|             "type": "amateur1", | ||||
|             "name": "FM, DV - Repeater output 2m Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 220000000, | ||||
|             "end": 225000000, | ||||
|             "type": "amateur", | ||||
|             "name": "1.3m Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 430000000, | ||||
|             "end": 432000000, | ||||
|             "type": "amateur", | ||||
|             "name": "|70cm Ham Band All Modes" | ||||
|         }, | ||||
|         { | ||||
|             "start": 432000000, | ||||
|             "end": 432025000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW - EME" | ||||
|         }, | ||||
|         { | ||||
|             "start": 432025000, | ||||
|             "end": 432100000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, Digital - EME" | ||||
|         }, | ||||
|         { | ||||
|             "start": 432100000, | ||||
|             "end": 432300000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, SSB - Calling freq: 432.1 MHz" | ||||
|         }, | ||||
|         { | ||||
|             "start": 432300000, | ||||
|             "end": 432400000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW - Pilot Emissions" | ||||
|         }, | ||||
|         { | ||||
|             "start": 432400000, | ||||
|             "end": 432420000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital - Pilot Emissions" | ||||
|         }, | ||||
|         { | ||||
|             "start": 432420000, | ||||
|             "end": 433000000, | ||||
|             "type": "amateur", | ||||
|             "name": "CW, SSB, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 433000000, | ||||
|             "end": 433050000, | ||||
|             "type": "amateur1", | ||||
|             "name": "CW, Digital" | ||||
|         }, | ||||
|         { | ||||
|             "start": 433050000, | ||||
|             "end": 434000000, | ||||
|             "type": "amateur", | ||||
|             "name": "All Modes" | ||||
|         }, | ||||
|         { | ||||
|             "start": 434000000, | ||||
|             "end": 435000000, | ||||
|             "type": "amateur1", | ||||
|             "name": "Fm, DV - Repeater input" | ||||
|         }, | ||||
|         { | ||||
|             "start": 435000000, | ||||
|             "end": 438000000, | ||||
|             "type": "amateur", | ||||
|             "name": "All Modes - Satellites" | ||||
|         }, | ||||
|         { | ||||
|             "start": 438000000, | ||||
|             "end": 439000000, | ||||
|             "type": "amateur1", | ||||
|             "name": "All Modes" | ||||
|         }, | ||||
|         { | ||||
|             "start": 439000000, | ||||
|             "end": 440000000, | ||||
|             "type": "amateur", | ||||
|             "name": "FM, DV - Repeater output 70cm Ham Band|" | ||||
|         }, | ||||
|         { | ||||
|             "start": 902000000, | ||||
|             "end": 928000000, | ||||
|             "type": "amateur", | ||||
|             "name": "33cm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 1240000000, | ||||
|             "end": 1300000000, | ||||
|             "type": "amateur", | ||||
|             "name": "23cm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 2330000000, | ||||
|             "end": 2450000000, | ||||
|             "type": "amateur", | ||||
|             "name": "13cm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 3400000000, | ||||
|             "end": 3500000000, | ||||
|             "type": "amateur", | ||||
|             "name": "9cm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 5650000000, | ||||
|             "end": 5925000000, | ||||
|             "type": "amateur", | ||||
|             "name": "5cm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 10000000000, | ||||
|             "end": 10500000000, | ||||
|             "type": "amateur", | ||||
|             "name": "3cm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 24000000000, | ||||
|             "end": 24250000000, | ||||
|             "type": "amateur", | ||||
|             "name": "1.2cm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 47000000000, | ||||
|             "end": 47200000000, | ||||
|             "type": "amateur", | ||||
|             "name": "6mm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 122250000000, | ||||
|             "end": 123000000000, | ||||
|             "type": "amateur", | ||||
|             "name": "2.5mm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 134000000000, | ||||
|             "end": 141000000000, | ||||
|             "type": "amateur", | ||||
|             "name": "2mm Ham Band" | ||||
|         }, | ||||
|         { | ||||
|             "start": 241000000000, | ||||
|             "end": 250000000000, | ||||
|             "type": "amateur", | ||||
|             "name": "1mm Ham Band" | ||||
|         } | ||||
|     ] | ||||
| } | ||||
| @@ -2,8 +2,8 @@ | ||||
|     "name": "France", | ||||
|     "country_name": "France", | ||||
|     "country_code": "FR", | ||||
|     "author_name": "Fred F4EED", | ||||
|     "author_url": "http://f4eed.wordpress.com", | ||||
|     "author_name": "Fred F4EED, Armand31", | ||||
|     "author_url": "http://f4eed.wordpress.com, https://github.com/Armand31", | ||||
|     "bands": [ | ||||
|         { | ||||
|             "name": "137KHz - Radioamateur", | ||||
| @@ -355,7 +355,7 @@ | ||||
|             "end": 54000000 | ||||
|         }, | ||||
|         { | ||||
|             "name": "Bande FM - Radiodif.", | ||||
|             "name": "Radiodiffusion - Bande FM", | ||||
|             "type": "broadcast", | ||||
|             "start": 80000000, | ||||
|             "end": 108000000 | ||||
| @@ -396,6 +396,12 @@ | ||||
|             "start": 162362500, | ||||
|             "end": 162587500 | ||||
|         }, | ||||
| 	{ | ||||
| 	    "name": "Radiodiffusion - Bande DAB", | ||||
| 	    "type": "broadcast", | ||||
| 	    "start": 174000000, | ||||
| 	    "end": 223000000 | ||||
| 	}, | ||||
|         { | ||||
|             "name": "Military Aviation", | ||||
|             "type": "military", | ||||
| @@ -408,6 +414,12 @@ | ||||
|             "start": 240000000, | ||||
|             "end": 270000000 | ||||
|         }, | ||||
| 	{ | ||||
| 	    "name": "Police (TETRAPOL)", | ||||
| 	    "type": "military", | ||||
|  	    "start": 380000000, | ||||
| 	    "end": 400000000 | ||||
| 	}, | ||||
|         { | ||||
|             "name": "70cm - Radioamateur", | ||||
|             "type": "amateur", | ||||
| @@ -420,12 +432,24 @@ | ||||
|             "start": 446000000, | ||||
|             "end": 446200000 | ||||
|         }, | ||||
| 	{ | ||||
| 	    "name": "TNT (DVB-T)", | ||||
| 	    "type": "broadcast", | ||||
| 	    "start": 470000000, | ||||
| 	    "end": 694000000 | ||||
| 	}, | ||||
|         { | ||||
|             "name": "23cm - Radioamateur", | ||||
|             "type": "amateur", | ||||
|             "start": 1240000000, | ||||
|             "end": 1300000000 | ||||
|         }, | ||||
| 	{ | ||||
| 	    "name": "Radiodiffusion - Bande DAB", | ||||
| 	    "type": "broadcast", | ||||
| 	    "start": 1452000000, | ||||
| 	    "end": 1492000000 | ||||
| 	}, | ||||
|         { | ||||
|             "name": "13cm - Radioamateur", | ||||
|             "type": "amateur", | ||||
|   | ||||
							
								
								
									
										285
									
								
								root/res/bandplans/turkey.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								root/res/bandplans/turkey.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | ||||
| { | ||||
|   "name": "Turkey", | ||||
|   "country_name": "Turkey", | ||||
|   "country_code": "TR", | ||||
|   "author_name": "Yunus TA2PEA", | ||||
|   "author_url": "https://github.com/ycanerol", | ||||
|   "bands": [ | ||||
|     { | ||||
|       "name": "LW", | ||||
|       "type": "amateur", | ||||
|       "start": 135700, | ||||
|       "end": 137800 | ||||
|     }, | ||||
|     { | ||||
|       "name": "630m", | ||||
|       "type": "amateur", | ||||
|       "start": 472000, | ||||
|       "end": 479000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "160m", | ||||
|       "type": "amateur", | ||||
|       "start": 1810000, | ||||
|       "end": 1850000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "80m", | ||||
|       "type": "amateur", | ||||
|       "start": 3500000, | ||||
|       "end": 3800000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "60m", | ||||
|       "type": "amateur", | ||||
|       "start": 5351500, | ||||
|       "end": 5366500 | ||||
|     }, | ||||
|     { | ||||
|       "name": "40m", | ||||
|       "type": "amateur", | ||||
|       "start": 7000000, | ||||
|       "end": 7200000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "30m", | ||||
|       "type": "amateur", | ||||
|       "start": 10100000, | ||||
|       "end": 10150000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "20m", | ||||
|       "type": "amateur", | ||||
|       "start": 14000000, | ||||
|       "end": 14350000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "17m", | ||||
|       "type": "amateur", | ||||
|       "start": 18068000, | ||||
|       "end": 18168000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "15m", | ||||
|       "type": "amateur", | ||||
|       "start": 21000000, | ||||
|       "end": 21450000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "12m", | ||||
|       "type": "amateur", | ||||
|       "start": 24890000, | ||||
|       "end": 24990000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "CB", | ||||
|       "type": "other", | ||||
|       "start": 26565000, | ||||
|       "end": 27405000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Pagers", | ||||
|       "type": "amateur", | ||||
|       "start": 27750000, | ||||
|       "end": 28000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "10m", | ||||
|       "type": "amateur", | ||||
|       "start": 28000000, | ||||
|       "end": 29700000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "6m", | ||||
|       "type": "amateur", | ||||
|       "start": 50030000, | ||||
|       "end": 51000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "FM", | ||||
|       "type": "broadcast", | ||||
|       "start": 87500000, | ||||
|       "end": 108000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Airband VOR/ILS", | ||||
|       "type": "aviation", | ||||
|       "start": 108000000, | ||||
|       "end": 117975000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Airband Voice", | ||||
|       "type": "aviation", | ||||
|       "start": 117975000, | ||||
|       "end": 137000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "2m", | ||||
|       "type": "amateur", | ||||
|       "start": 144000000, | ||||
|       "end": 146000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Sayac Okuma", | ||||
|       "type": "other", | ||||
|       "start": 169400000, | ||||
|       "end": 169475000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Pagers", | ||||
|       "type": "other", | ||||
|       "start": 167000000, | ||||
|       "end": 167100000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Public announcement systems", | ||||
|       "type": "other", | ||||
|       "start": 173882500, | ||||
|       "end": 174000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "DVB-T", | ||||
|       "type": "broadcast", | ||||
|       "start": 174000000, | ||||
|       "end": 216000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "T-DAB", | ||||
|       "type": "broadcast", | ||||
|       "start": 216000000, | ||||
|       "end": 233000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "ILS-Glide Path", | ||||
|       "type": "aviation", | ||||
|       "start": 328600000, | ||||
|       "end": 335400000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Public Safety/Emergency", | ||||
|       "type": "other", | ||||
|       "start": 380000000, | ||||
|       "end": 385000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Public Safety/Emergency", | ||||
|       "type": "other", | ||||
|       "start": 390000000, | ||||
|       "end": 395000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "70cm", | ||||
|       "type": "amateur", | ||||
|       "start": 430200000, | ||||
|       "end": 430700000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "70cm-RepeaterRX", | ||||
|       "type": "amateur", | ||||
|       "start": 431550000, | ||||
|       "end": 431825000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "70cm", | ||||
|       "type": "amateur", | ||||
|       "start": 432000000, | ||||
|       "end": 432975000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "70cm", | ||||
|       "type": "amateur", | ||||
|       "start": 433400000, | ||||
|       "end": 434000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "70cm", | ||||
|       "type": "amateur", | ||||
|       "start": 435000000, | ||||
|       "end": 438000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "70cm-RepeaterTX", | ||||
|       "type": "amateur", | ||||
|       "start": 439150000, | ||||
|       "end": 439425000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "Public announcement systems", | ||||
|       "type": "other", | ||||
|       "start": 445250000, | ||||
|       "end": 445462500 | ||||
|     }, | ||||
|     { | ||||
|       "name": "PMR446", | ||||
|       "type": "other", | ||||
|       "start": 446006250, | ||||
|       "end": 446196875 | ||||
|     }, | ||||
|     { | ||||
|       "name": "RFID", | ||||
|       "type": "other", | ||||
|       "start": 865000000, | ||||
|       "end": 868000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "RFID", | ||||
|       "type": "other", | ||||
|       "start": 916100000, | ||||
|       "end": 918900000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "23cm", | ||||
|       "type": "amateur", | ||||
|       "start": 1240000000, | ||||
|       "end": 1300000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "DECT", | ||||
|       "type": "other", | ||||
|       "start": 1880000000, | ||||
|       "end": 1900000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "5GHz", | ||||
|       "type": "amateur", | ||||
|       "start": 5650000000, | ||||
|       "end": 5670000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "5GHz", | ||||
|       "type": "amateur", | ||||
|       "start": 5820000000, | ||||
|       "end": 5850000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "3cm", | ||||
|       "type": "amateur", | ||||
|       "start": 104500000000, | ||||
|       "end": 104520000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "24GHz", | ||||
|       "type": "amateur", | ||||
|       "start": 24000000000, | ||||
|       "end": 24050000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "47GHz", | ||||
|       "type": "amateur", | ||||
|       "start": 47000000000, | ||||
|       "end": 47200000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "75GHz", | ||||
|       "type": "amateur", | ||||
|       "start": 75500000000, | ||||
|       "end": 7600000000 | ||||
|     }, | ||||
|     { | ||||
|       "name": "134GHz", | ||||
|       "type": "amateur", | ||||
|       "start": 134000000000, | ||||
|       "end": 142000000000 | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -217,14 +217,19 @@ private: | ||||
|     } | ||||
|  | ||||
|     void startServer() { | ||||
|         if (modeId == SINK_MODE_TCP) { | ||||
|             listener = net::listen(hostname, port); | ||||
|             if (listener) { | ||||
|                 listener->acceptAsync(clientHandler, this); | ||||
|         try { | ||||
|             if (modeId == SINK_MODE_TCP) { | ||||
|                 listener = net::listen(hostname, port); | ||||
|                 if (listener) { | ||||
|                     listener->acceptAsync(clientHandler, this); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 conn = net::openUDP("0.0.0.0", port, hostname, port, false); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             conn = net::openUDP("0.0.0.0", port, hostname, port, false); | ||||
|         catch (const std::exception& e) { | ||||
|             flog::error("Failed to open socket: {}", e.what()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -152,7 +152,6 @@ public: | ||||
|  | ||||
|         // Update samplerate from ID | ||||
|         sampleRate = sampleRates[srId]; | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|     } | ||||
|  | ||||
| private: | ||||
| @@ -232,6 +231,7 @@ private: | ||||
|         if (SmGui::Combo(CONCAT("##_audio_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { | ||||
|             std::string dev = _this->devices.key(_this->devId); | ||||
|             _this->select(dev); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             config.acquire(); | ||||
|             config.conf["device"] = dev; | ||||
|             config.release(true); | ||||
| @@ -253,6 +253,7 @@ private: | ||||
|         if (SmGui::Button(CONCAT("Refresh##_audio_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             _this->select(_this->selectedDevice); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|         } | ||||
|  | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|   | ||||
							
								
								
									
										25
									
								
								source_modules/badgesdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								source_modules/badgesdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(badgesdr_source) | ||||
|  | ||||
| file(GLOB SRC "src/*.cpp") | ||||
|  | ||||
| include(${SDRPP_MODULE_CMAKE}) | ||||
|  | ||||
| if (MSVC) | ||||
|     find_package(libusb CONFIG REQUIRED) | ||||
|     target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS}) | ||||
|     target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES}) | ||||
| elseif (ANDROID) | ||||
|     target_link_libraries(badgesdr_source PUBLIC | ||||
|         /sdr-kit/${ANDROID_ABI}/lib/libusb1.0.so | ||||
|         /sdr-kit/${ANDROID_ABI}/lib/librtlsdr.so | ||||
|     ) | ||||
| else (MSVC) | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|     pkg_check_modules(LIBUSB REQUIRED libusb-1.0) | ||||
|  | ||||
|     target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS}) | ||||
|     target_link_directories(badgesdr_source PRIVATE ${LIBUSB_LIBRARY_DIRS}) | ||||
|     target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES}) | ||||
| endif () | ||||
							
								
								
									
										309
									
								
								source_modules/badgesdr_source/src/badgesdr.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								source_modules/badgesdr_source/src/badgesdr.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,309 @@ | ||||
| #include "badgesdr.h" | ||||
| #include <stdexcept> | ||||
| #include <utils/flog.h> | ||||
|  | ||||
| #define R820T_I2C_ADDR  0x1A | ||||
|  | ||||
| namespace BadgeSDR { | ||||
|     enum Commands { | ||||
|         CMD_I2C_RW, | ||||
|         CMD_I2C_STATUS, | ||||
|         CMD_ADC_START, | ||||
|         CMD_ADC_STOP, | ||||
|         CMD_ADC_GET_SAMP_COUNT | ||||
|     }; | ||||
|  | ||||
|     libusb_context* ctx = NULL; | ||||
|  | ||||
|     bool DeviceInfo::operator==(const DeviceInfo& b) const { | ||||
|         return serialNumber == b.serialNumber; | ||||
|     } | ||||
|  | ||||
|     void Device::write_reg(uint8_t reg, uint8_t value, void* ctx) { | ||||
|         Device* dev = (Device*)ctx; | ||||
|         dev->writeR820TReg(reg, value); | ||||
|         dev->writeR820TReg(0x1F, 0); // TODO: Figure out why this is needed | ||||
|     } | ||||
|  | ||||
|     void Device::read_reg(uint8_t* data, int len, void* ctx) { | ||||
|         Device* dev = (Device*)ctx; | ||||
|         dev->readI2C(R820T_I2C_ADDR, data, len); | ||||
|     } | ||||
|  | ||||
|     Device::Device(libusb_device_handle* dev) { | ||||
|         // Save device handle | ||||
|         this->dev = dev; | ||||
|  | ||||
|         // Init tuner | ||||
|         r820t = { | ||||
|             16000000, // xtal_freq => 16MHz | ||||
|             3000000, // Set at boot to airspy_m0_m4_conf_t conf0 -> r820t_if_freq | ||||
|             100000000, /* Default Freq 100Mhz */ | ||||
|             { | ||||
|                 /* 05 */ 0x9F, // LNA manual gain mode, init to 0 | ||||
|                 /* 06 */ 0x80, | ||||
|                 /* 07 */ 0x60, | ||||
|                 /* 08 */ 0x80, // Image Gain Adjustment | ||||
|                 /* 09 */ 0x40, // Image Phase Adjustment | ||||
|                 /* 0A */ 0xA8, // Channel filter [0..3]: 0 = widest, f = narrowest - Optimal. Don't touch! | ||||
|                 /* 0B */ 0x0F, // High pass filter - Optimal. Don't touch! | ||||
|                 /* 0C */ 0x4F, // VGA control by code, init at 0 | ||||
|                 /* 0D */ 0x63, // LNA AGC settings: [0..3]: Lower threshold; [4..7]: High threshold | ||||
|                 /* 0E */ 0x75, | ||||
|                 /* 0F */ 0xE8, // Filter Widest, LDO_5V OFF, clk out ON, | ||||
|                 /* 10 */ 0x7C, | ||||
|                 /* 11 */ 0x42, | ||||
|                 /* 12 */ 0x06, | ||||
|                 /* 13 */ 0x00, | ||||
|                 /* 14 */ 0x0F, | ||||
|                 /* 15 */ 0x00, | ||||
|                 /* 16 */ 0xC0, | ||||
|                 /* 17 */ 0xA0, | ||||
|                 /* 18 */ 0x48, | ||||
|                 /* 19 */ 0xCC, | ||||
|                 /* 1A */ 0x60, | ||||
|                 /* 1B */ 0x00, | ||||
|                 /* 1C */ 0x54, | ||||
|                 /* 1D */ 0xAE, | ||||
|                 /* 1E */ 0x0A, | ||||
|                 /* 1F */ 0xC0 | ||||
|             }, | ||||
|             0 /* uint16_t padding */ | ||||
|         }; | ||||
|         r820t_init(&r820t, 3000000, write_reg, read_reg, this); | ||||
|         r820t_set_mixer_gain(&r820t, 15); | ||||
|         r820t_set_vga_gain(&r820t, 15); | ||||
|     } | ||||
|  | ||||
|     Device::~Device() { | ||||
|         // Release the bulk interface | ||||
|         libusb_release_interface(dev, 0); | ||||
|  | ||||
|         // Close device | ||||
|         libusb_close(dev); | ||||
|     } | ||||
|  | ||||
|     void Device::setFrequency(double freq) { | ||||
|         r820t_set_freq(&r820t, freq - 2125000); | ||||
|     } | ||||
|  | ||||
|     void Device::setLNAGain(int gain) { | ||||
|         r820t_set_lna_gain(&r820t, gain); | ||||
|     } | ||||
|  | ||||
|     void Device::setMixerGain(int gain) { | ||||
|         r820t_set_mixer_gain(&r820t, gain); | ||||
|     } | ||||
|  | ||||
|     void Device::setVGAGain(int gain) { | ||||
|         r820t_set_vga_gain(&r820t, gain); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     void Device::start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx, int minBufferSize) { | ||||
|         // Do nothing if already running | ||||
|         if (run) { return; } | ||||
|  | ||||
|         // Save handler | ||||
|         this->callback = callback; | ||||
|         this->ctx = ctx; | ||||
|  | ||||
|         // Compute buffer size | ||||
|         int bufCount = minBufferSize / 64; | ||||
|         if (minBufferSize % 64) { bufCount++; } | ||||
|         bufferSize = bufCount * 64; | ||||
|  | ||||
|         // Mark as running | ||||
|         run = true; | ||||
|  | ||||
|         // Start the ADC | ||||
|         startADC(); | ||||
|  | ||||
|         // Start worker thread | ||||
|         workerThread = std::thread(&Device::worker, this); | ||||
|     } | ||||
|  | ||||
|     void Device::stop() { | ||||
|         // Do nothing if already stopped | ||||
|         if (!run) { return; } | ||||
|  | ||||
|         // Mark as stopped | ||||
|         run = false; | ||||
|  | ||||
|         // Wait for the worker to exit | ||||
|         if (workerThread.joinable()) { workerThread.join(); } | ||||
|  | ||||
|         // Stop the ADC | ||||
|         stopADC(); | ||||
|     } | ||||
|  | ||||
|     int Device::getI2CStatus() { | ||||
|         int status; | ||||
|         int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000); | ||||
|         if (ret <= 0 || status < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|         return status; | ||||
|     } | ||||
|  | ||||
|     int Device::readI2C(uint8_t addr, uint8_t* data, int len) { | ||||
|         // Do read | ||||
|         int bytes = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_RW, 0, addr, data, len, 1000); | ||||
|         if (bytes < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         // Get status (TODO: Use function) | ||||
|         int status; | ||||
|         int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000); | ||||
|         if (ret <= 0 || status < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         return bytes; | ||||
|     } | ||||
|  | ||||
|     int Device::writeI2C(uint8_t addr, const uint8_t* data, int len) { | ||||
|         // Do write | ||||
|         int bytes = libusb_control_transfer(dev, (2 << 5), CMD_I2C_RW, 0, addr, (uint8_t*)data, len, 1000); | ||||
|         if (bytes < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         // Get status (TODO: Use function) | ||||
|         int status; | ||||
|         int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000); | ||||
|         if (ret <= 0 || status < 0) { | ||||
|             return -1; | ||||
|         } | ||||
|  | ||||
|         return bytes; | ||||
|     } | ||||
|  | ||||
|     uint8_t bitrev(uint8_t val) { | ||||
|         return ((val & 0x01) << 7) | ((val & 0x02) << 5) | ((val & 0x04) << 3) | ((val & 0x08) << 1) | ((val & 0x10) >> 1) | ((val & 0x20) >> 3) | ((val & 0x40) >> 5) | ((val & 0x80) >> 7); | ||||
|     } | ||||
|  | ||||
|     uint8_t Device::readR820TReg(uint8_t reg) { | ||||
|         // Read registers up to it (can't read single) | ||||
|         uint8_t regs[0x20]; | ||||
|         readI2C(R820T_I2C_ADDR, regs, reg+1); | ||||
|  | ||||
|         // Invert bit order | ||||
|         return bitrev(regs[reg]); | ||||
|     } | ||||
|  | ||||
|     void Device::writeR820TReg(uint8_t reg, uint8_t val) { | ||||
|         // Write register id then value | ||||
|         uint8_t cmd[2] = { reg, val }; | ||||
|         writeI2C(R820T_I2C_ADDR, cmd, 2); | ||||
|     } | ||||
|  | ||||
|     int Device::startADC() { | ||||
|         return libusb_control_transfer(dev, (2 << 5), CMD_ADC_START, 0, 0, NULL, 0, 1000); | ||||
|     } | ||||
|  | ||||
|     int Device::stopADC() { | ||||
|         return libusb_control_transfer(dev, (2 << 5), CMD_ADC_STOP, 0, 0, NULL, 0, 1000); | ||||
|     } | ||||
|  | ||||
|     void Device::worker() { | ||||
|         // Allocate sample buffer | ||||
|         uint8_t* buffer = new uint8_t[bufferSize]; | ||||
|         int sampleCount = 0; | ||||
|  | ||||
|         while (run) { | ||||
|             // Receive data with bulk transfer | ||||
|             int recvLen = 0; | ||||
|             int val = libusb_bulk_transfer(dev, LIBUSB_ENDPOINT_IN | 1, &buffer[sampleCount], bufferSize - sampleCount, &recvLen, 1000); | ||||
|              | ||||
|             // If timed out, try again. Otherwise, if an error occur, stop the thread | ||||
|             if (val == LIBUSB_ERROR_TIMEOUT) { | ||||
|                 continue; | ||||
|             } | ||||
|             else if (val) { | ||||
|                 flog::error("USB Error: {}", val); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             // Increment sample count | ||||
|             if (recvLen) { sampleCount += recvLen; } | ||||
|  | ||||
|             // If the buffer is full, call handler and reset sample count | ||||
|             if (sampleCount >= bufferSize) { | ||||
|                 callback(buffer, sampleCount, ctx); | ||||
|                 sampleCount = 0; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Free buffer | ||||
|         delete[] buffer; | ||||
|     } | ||||
|  | ||||
|     std::vector<DeviceInfo> list() { | ||||
|         // Init libusb if done yet | ||||
|         if (!ctx) { | ||||
|             libusb_init(&ctx); | ||||
|             libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_WARNING); | ||||
|         } | ||||
|  | ||||
|         // List devices | ||||
|         std::vector<DeviceInfo> devList; | ||||
|         libusb_device** devices; | ||||
|         int devCount = libusb_get_device_list(ctx, &devices); | ||||
|         for (int i = 0; i < devCount; i++) { | ||||
|             // Get device info | ||||
|             DeviceInfo devInfo; | ||||
|             devInfo.dev = devices[i]; | ||||
|             libusb_device_descriptor desc; | ||||
|             libusb_get_device_descriptor(devInfo.dev, &desc); | ||||
|  | ||||
|             // Check the VID/PID and give up if not the right ones | ||||
|             if (desc.idVendor != 0xCAFE || desc.idProduct != 0x4010) { | ||||
|                 libusb_unref_device(devInfo.dev); | ||||
|                 continue; | ||||
|             } | ||||
|              | ||||
|             // Open devices | ||||
|             libusb_device_handle* openDev; | ||||
|             int err = libusb_open(devInfo.dev, &openDev); | ||||
|             if (err) { | ||||
|                 libusb_unref_device(devInfo.dev); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             // Get serial number | ||||
|             char serial[128]; | ||||
|             libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, (uint8_t*)serial, sizeof(serial)); | ||||
|             devInfo.serialNumber = serial; | ||||
|  | ||||
|             // TODO: The libusb device should be unreffed but would need to know when the list disappears | ||||
|  | ||||
|             // Close device | ||||
|             libusb_close(openDev); | ||||
|  | ||||
|             // Add to list | ||||
|             devList.push_back(devInfo); | ||||
|         } | ||||
|  | ||||
|         // Return devices | ||||
|         return devList; | ||||
|     } | ||||
|  | ||||
|     std::shared_ptr<Device> open(const DeviceInfo& dev) { | ||||
|         // Open device | ||||
|         libusb_device_handle* openDev; | ||||
|         int err = libusb_open(dev.dev, &openDev); | ||||
|         if (err) { | ||||
|             throw std::runtime_error("Failed to open device"); | ||||
|         } | ||||
|  | ||||
|         // Claim the bulk transfer interface | ||||
|         if (libusb_claim_interface(openDev, 0)) { | ||||
|             throw std::runtime_error("Failed to claim bulk interface"); | ||||
|         } | ||||
|  | ||||
|         // Create device | ||||
|         return std::make_shared<Device>(openDev); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										53
									
								
								source_modules/badgesdr_source/src/badgesdr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								source_modules/badgesdr_source/src/badgesdr.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| #pragma once | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <memory> | ||||
| #include <thread> | ||||
| #include <libusb.h> | ||||
| #include "r820t.h" | ||||
|  | ||||
| namespace BadgeSDR { | ||||
|     struct DeviceInfo { | ||||
|         std::string serialNumber; | ||||
|         libusb_device* dev; | ||||
|         bool operator==(const DeviceInfo& b) const; | ||||
|     }; | ||||
|  | ||||
|     class Device { | ||||
|     public: | ||||
|         Device(libusb_device_handle* dev); | ||||
|         ~Device(); | ||||
|  | ||||
|         void setFrequency(double freq); | ||||
|         void setLNAGain(int gain); | ||||
|         void setMixerGain(int gain); | ||||
|         void setVGAGain(int gain); | ||||
|          | ||||
|         void start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx = NULL, int minBufferSize = 2500); | ||||
|         void stop(); | ||||
|  | ||||
|     private: | ||||
|         int getI2CStatus(); | ||||
|         int readI2C(uint8_t addr, uint8_t* data, int len); | ||||
|         int writeI2C(uint8_t addr, const uint8_t* data, int len); | ||||
|         uint8_t readR820TReg(uint8_t reg); | ||||
|         void writeR820TReg(uint8_t reg, uint8_t val); | ||||
|         int startADC(); | ||||
|         int stopADC(); | ||||
|         void worker(); | ||||
|  | ||||
|         libusb_device_handle* dev; | ||||
|         std::thread workerThread; | ||||
|         bool run = false; | ||||
|         int bufferSize = 0; // Must be multiple of 64 for best performance | ||||
|         void* ctx = NULL; | ||||
|         void (*callback)(const uint8_t* samples, int count, void* ctx); | ||||
|  | ||||
|         static void write_reg(uint8_t reg, uint8_t value, void* ctx); | ||||
|         static void read_reg(uint8_t* data, int len, void* ctx); | ||||
|         r820t_priv_t r820t; | ||||
|     }; | ||||
|  | ||||
|     std::vector<DeviceInfo> list(); | ||||
|     std::shared_ptr<Device> open(const DeviceInfo& dev); | ||||
| } | ||||
							
								
								
									
										272
									
								
								source_modules/badgesdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								source_modules/badgesdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,272 @@ | ||||
| #include <imgui.h> | ||||
| #include <module.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/smgui.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <core.h> | ||||
| #include <utils/optionlist.h> | ||||
| #include <dsp/channel/rx_vfo.h> | ||||
| #include <dsp/correction/dc_blocker.h> | ||||
| #include "badgesdr.h" | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
|     /* Name:            */ "badgesdr_source", | ||||
|     /* Description:     */ "BadgeSDR Source Module", | ||||
|     /* Author:          */ "Ryzerth", | ||||
|     /* Version:         */ 0, 1, 0, | ||||
|     /* Max instances    */ -1 | ||||
| }; | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| class BadgeSDRSourceModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     BadgeSDRSourceModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         sampleRate = 250000.0; | ||||
|  | ||||
|         // Initialize DSP | ||||
|         dcBlock.init(&input, 0.001); | ||||
|         ddc.init(&dcBlock.out, 500000, 250000, 250000, 125000); | ||||
|  | ||||
|         handler.ctx = this; | ||||
|         handler.selectHandler = menuSelected; | ||||
|         handler.deselectHandler = menuDeselected; | ||||
|         handler.menuHandler = menuHandler; | ||||
|         handler.startHandler = start; | ||||
|         handler.stopHandler = stop; | ||||
|         handler.tuneHandler = tune; | ||||
|         handler.stream = &ddc.out; | ||||
|  | ||||
|         // Refresh devices | ||||
|         refresh(); | ||||
|  | ||||
|         // Select first (TODO: Select from config) | ||||
|         select(""); | ||||
|  | ||||
|         sigpath::sourceManager.registerSource("BadgeSDR", &handler); | ||||
|     } | ||||
|  | ||||
|     ~BadgeSDRSourceModule() { | ||||
|          | ||||
|     } | ||||
|  | ||||
|     void postInit() {} | ||||
|  | ||||
|     void enable() { | ||||
|         enabled = true; | ||||
|     } | ||||
|  | ||||
|     void disable() { | ||||
|         enabled = false; | ||||
|     } | ||||
|  | ||||
|     bool isEnabled() { | ||||
|         return enabled; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     void refresh() { | ||||
|         devices.clear(); | ||||
|         auto list = BadgeSDR::list(); | ||||
|         for (const auto& info : list) { | ||||
|             // Format device name | ||||
|             std::string devName = "BadgeSDR "; | ||||
|             devName += " ["; | ||||
|             devName += info.serialNumber; | ||||
|             devName += ']'; | ||||
|  | ||||
|             // Save device | ||||
|             devices.define(info.serialNumber, devName, info); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void select(const std::string& serial) { | ||||
|         // If there are no devices, give up | ||||
|         if (devices.empty()) { | ||||
|             selectedSerial.clear(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // If the serial was not found, select the first available serial | ||||
|         if (!devices.keyExists(serial)) { | ||||
|             select(devices.key(0)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Save serial number | ||||
|         selectedSerial = serial; | ||||
|         selectedDev = devices.value(devices.keyId(serial)); | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
|         BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; | ||||
|         core::setInputSampleRate(_this->sampleRate); | ||||
|         flog::info("BadgeSDRSourceModule '{0}': Menu Select!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void menuDeselected(void* ctx) { | ||||
|         BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; | ||||
|         flog::info("BadgeSDRSourceModule '{0}': Menu Deselect!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void start(void* ctx) { | ||||
|         BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; | ||||
|         if (_this->running) { return; } | ||||
|  | ||||
|         // Open the device | ||||
|         _this->openDev = BadgeSDR::open(_this->selectedDev); | ||||
|  | ||||
|         // Configure the device | ||||
|         _this->openDev->setFrequency(_this->freq); | ||||
|         _this->openDev->setLNAGain(_this->lnaGain); | ||||
|         _this->openDev->setMixerGain(_this->mixerGain); | ||||
|         _this->openDev->setVGAGain(_this->vgaGain); | ||||
|  | ||||
|         // Start DSP | ||||
|         _this->dcBlock.start(); | ||||
|         _this->ddc.start(); | ||||
|  | ||||
|         // Start device | ||||
|         _this->openDev->start(callback, _this, 500000/200); | ||||
|          | ||||
|         _this->running = true; | ||||
|         flog::info("BadgeSDRSourceModule '{0}': Start!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void stop(void* ctx) { | ||||
|         BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; | ||||
|         if (!_this->running) { return; } | ||||
|         _this->running = false; | ||||
|          | ||||
|         // Stop worker | ||||
|         _this->openDev->stop(); | ||||
|  | ||||
|         // Stop DSP | ||||
|         _this->dcBlock.stop(); | ||||
|         _this->ddc.stop(); | ||||
|  | ||||
|         // Close device | ||||
|         _this->openDev.reset(); | ||||
|  | ||||
|         flog::info("BadgeSDRSourceModule '{0}': Stop!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             _this->openDev->setFrequency(freq); | ||||
|         } | ||||
|         _this->freq = freq; | ||||
|         flog::info("BadgeSDRSourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
|     } | ||||
|  | ||||
|     static void menuHandler(void* ctx) { | ||||
|         BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; | ||||
|          | ||||
|         if (_this->running) { SmGui::BeginDisabled(); } | ||||
|  | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_badgesdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { | ||||
|             _this->select(_this->devices.key(_this->devId)); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Button(CONCAT("Refresh##_badgesdr_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             _this->select(_this->selectedSerial); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|         } | ||||
|  | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|  | ||||
|         SmGui::LeftLabel("LNA Gain"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::SliderInt(CONCAT("##_badgesdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) { | ||||
|             if (_this->running) { | ||||
|                 _this->openDev->setLNAGain(_this->lnaGain); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::LeftLabel("Mixer Gain"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::SliderInt(CONCAT("##_badgesdr_mixer_gain_", _this->name), &_this->mixerGain, 0, 15)) { | ||||
|             if (_this->running) { | ||||
|                 _this->openDev->setMixerGain(_this->mixerGain); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::LeftLabel("VGA Gain"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::SliderInt(CONCAT("##_badgesdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { | ||||
|             if (_this->running) { | ||||
|                 _this->openDev->setVGAGain(_this->vgaGain); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static void callback(const uint8_t* samples, int count, void* ctx) { | ||||
|         BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx; | ||||
|  | ||||
|         // Convert samples to float | ||||
|         dsp::complex_t* out = _this->input.writeBuf; | ||||
|         int min = 255, max = 0; | ||||
|         for (int i = 0; i < count; i++) { | ||||
|             if (samples[i] < min) { min = samples[i]; } | ||||
|             if (samples[i] > max) { max = samples[i]; } | ||||
|  | ||||
|             out[i].re = ((float)samples[i] - 127.5f) * (1.0f/127.0f); | ||||
|             out[i].im = 1.0f; | ||||
|         } | ||||
|  | ||||
|         // Send out samples | ||||
|         _this->input.swap(count); | ||||
|  | ||||
|         flog::debug("Amplitudes: {} -> {}", min, max); | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|     double sampleRate; | ||||
|     SourceManager::SourceHandler handler; | ||||
|     bool running = false; | ||||
|     double freq; | ||||
|  | ||||
|     OptionList<std::string, BadgeSDR::DeviceInfo> devices; | ||||
|  | ||||
|     int devId = 0; | ||||
|     int lnaGain = 0; | ||||
|     int mixerGain = 0; | ||||
|     int vgaGain = 0; | ||||
|     std::string selectedSerial; | ||||
|     BadgeSDR::DeviceInfo selectedDev; | ||||
|     std::shared_ptr<BadgeSDR::Device> openDev; | ||||
|  | ||||
|     dsp::stream<dsp::complex_t> input; | ||||
|     dsp::correction::DCBlocker<dsp::complex_t> dcBlock; | ||||
|     dsp::channel::RxVFO ddc; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|     // Nothing here | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new BadgeSDRSourceModule(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { | ||||
|     delete (BadgeSDRSourceModule*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|     // Nothing here | ||||
| } | ||||
							
								
								
									
										622
									
								
								source_modules/badgesdr_source/src/r820t.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										622
									
								
								source_modules/badgesdr_source/src/r820t.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,622 @@ | ||||
| /* | ||||
|  * Rafael Micro R820T driver for AIRSPY | ||||
|  * | ||||
|  * Copyright 2013 Youssef Touil <youssef@airspy.com> | ||||
|  * Copyright 2014-2016 Benjamin Vernoux <bvernoux@airspy.com> | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 2 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include "r820t.h" | ||||
| #include <thread> | ||||
|  | ||||
| static int r820t_read_cache_reg(r820t_priv_t *priv, int reg); | ||||
|  | ||||
| #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | ||||
|  | ||||
| /* Tuner frequency ranges */ | ||||
| struct r820t_freq_range | ||||
| { | ||||
|   uint8_t open_d; | ||||
|   uint8_t rf_mux_ploy; | ||||
|   uint8_t tf_c; | ||||
| }; | ||||
|  | ||||
| #define R820T_READ_MAX_DATA 32 | ||||
| #define R820T_INIT_NB_REGS (32-5) | ||||
| uint8_t r820t_read_data[R820T_READ_MAX_DATA]; /* Buffer for data read from I2C */ | ||||
| uint8_t r820t_state_standby = 1; /* 1=standby/power off 0=r820t initialized/power on */ | ||||
|  | ||||
| /* Tuner frequency ranges | ||||
| "Copyright (C) 2013 Mauro Carvalho Chehab" | ||||
| https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c | ||||
| part of freq_ranges() | ||||
| */ | ||||
| const struct r820t_freq_range freq_ranges[] = | ||||
| { | ||||
|   { | ||||
|   /* 0 MHz */ | ||||
|   /* .open_d = */     0x08, /* low */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0xdf, /* R27[7:0]  band2,band0 */ | ||||
|   }, { | ||||
|   /* 50 MHz */ | ||||
|   /* .open_d = */     0x08, /* low */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0xbe, /* R27[7:0]  band4,band1  */ | ||||
|   }, { | ||||
|   /* 55 MHz */ | ||||
|   /* .open_d = */     0x08, /* low */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x8b, /* R27[7:0]  band7,band4 */ | ||||
|   }, { | ||||
|   /* 60 MHz */ | ||||
|   /* .open_d = */     0x08, /* low */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x7b, /* R27[7:0]  band8,band4 */ | ||||
|   }, { | ||||
|   /* 65 MHz */ | ||||
|   /* .open_d = */     0x08, /* low */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x69, /* R27[7:0]  band9,band6 */ | ||||
|   }, { | ||||
|   /* 70 MHz */ | ||||
|   /* .open_d = */     0x08, /* low */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x58, /* R27[7:0]  band10,band7 */ | ||||
|   }, { | ||||
|   /* 75 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x44, /* R27[7:0]  band11,band11 */ | ||||
|   }, { | ||||
|   /* 80 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x44, /* R27[7:0]  band11,band11 */ | ||||
|   }, { | ||||
|   /* 90 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x34, /* R27[7:0]  band12,band11 */ | ||||
|   }, { | ||||
|   /* 100 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x34, /* R27[7:0]  band12,band11 */ | ||||
|   }, { | ||||
|   /* 110 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x24, /* R27[7:0]  band13,band11 */ | ||||
|   }, { | ||||
|   /* 120 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x24, /* R27[7:0]  band13,band11 */ | ||||
|   }, { | ||||
|   /* 140 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x14, /* R27[7:0]  band14,band11 */ | ||||
|   }, { | ||||
|   /* 180 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x13, /* R27[7:0]  band14,band12 */ | ||||
|   }, { | ||||
|   /* 220 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x13, /* R27[7:0]  band14,band12 */ | ||||
|   }, { | ||||
|   /* 250 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x11, /* R27[7:0]  highest,highest */ | ||||
|   }, { | ||||
|   /* 280 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */ | ||||
|   /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */ | ||||
|   }, { | ||||
|   /* 310 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x41, /* R26[7:6]=1 (bypass)  R26[1:0]=1 (middle) */ | ||||
|   /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */ | ||||
|   }, { | ||||
|   /* 450 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x41, /* R26[7:6]=1 (bypass)  R26[1:0]=1 (middle) */ | ||||
|   /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */ | ||||
|   }, { | ||||
|   /* 588 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x40, /* R26[7:6]=1 (bypass)  R26[1:0]=0 (highest) */ | ||||
|   /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */ | ||||
|   }, { | ||||
|   /* 650 MHz */ | ||||
|   /* .open_d = */     0x00, /* high */ | ||||
|   /* .rf_mux_ploy = */  0x40, /* R26[7:6]=1 (bypass)  R26[1:0]=0 (highest) */ | ||||
|   /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */ | ||||
|   } | ||||
| }; | ||||
|  | ||||
| #define FREQ_TO_IDX_SIZE (600) | ||||
| const uint8_t freq_to_idx[FREQ_TO_IDX_SIZE]= | ||||
| { | ||||
|   /* 50 */ 1,/* 51 */ 1,/* 52 */ 1,/* 53 */ 1,/* 54 */ 1, | ||||
|   /* 55 */ 2,/* 56 */ 2,/* 57 */ 2,/* 58 */ 2,/* 59 */ 2, | ||||
|   /* 60 */ 3,/* 61 */ 3,/* 62 */ 3,/* 63 */ 3,/* 64 */ 3, | ||||
|   /* 65 */ 4,/* 66 */ 4,/* 67 */ 4,/* 68 */ 4,/* 69 */ 4, | ||||
|   /* 70 */ 5,/* 71 */ 5,/* 72 */ 5,/* 73 */ 5,/* 74 */ 5, | ||||
|   /* 75 */ 6,/* 76 */ 6,/* 77 */ 6,/* 78 */ 6,/* 79 */ 6, | ||||
|   /* 80 */ 7,/* 81 */ 7,/* 82 */ 7,/* 83 */ 7,/* 84 */ 7,/* 85 */ 7,/* 86 */ 7,/* 87 */ 7,/* 88 */ 7,/* 89 */ 7, | ||||
|   /* 90 */ 8,/* 91 */ 8,/* 92 */ 8,/* 93 */ 8,/* 94 */ 8,/* 95 */ 8,/* 96 */ 8,/* 97 */ 8,/* 98 */ 8,/* 99 */ 8, | ||||
|   /* 100 */ 9,/* 101 */ 9,/* 102 */ 9,/* 103 */ 9,/* 104 */ 9,/* 105 */ 9,/* 106 */ 9,/* 107 */ 9,/* 108 */ 9,/* 109 */ 9, | ||||
|   /* 110 */ 10,/* 111 */ 10,/* 112 */ 10,/* 113 */ 10,/* 114 */ 10,/* 115 */ 10,/* 116 */ 10,/* 117 */ 10,/* 118 */ 10,/* 119 */ 10, | ||||
|   /* 120 */ 11,/* 121 */ 11,/* 122 */ 11,/* 123 */ 11,/* 124 */ 11,/* 125 */ 11,/* 126 */ 11,/* 127 */ 11,/* 128 */ 11,/* 129 */ 11, | ||||
|   /* 130 */ 11,/* 131 */ 11,/* 132 */ 11,/* 133 */ 11,/* 134 */ 11,/* 135 */ 11,/* 136 */ 11,/* 137 */ 11,/* 138 */ 11,/* 139 */ 11, | ||||
|   /* 140 */ 12,/* 141 */ 12,/* 142 */ 12,/* 143 */ 12,/* 144 */ 12,/* 145 */ 12,/* 146 */ 12,/* 147 */ 12,/* 148 */ 12,/* 149 */ 12, | ||||
|   /* 150 */ 12,/* 151 */ 12,/* 152 */ 12,/* 153 */ 12,/* 154 */ 12,/* 155 */ 12,/* 156 */ 12,/* 157 */ 12,/* 158 */ 12,/* 159 */ 12, | ||||
|   /* 160 */ 12,/* 161 */ 12,/* 162 */ 12,/* 163 */ 12,/* 164 */ 12,/* 165 */ 12,/* 166 */ 12,/* 167 */ 12,/* 168 */ 12,/* 169 */ 12, | ||||
|   /* 170 */ 12,/* 171 */ 12,/* 172 */ 12,/* 173 */ 12,/* 174 */ 12,/* 175 */ 12,/* 176 */ 12,/* 177 */ 12,/* 178 */ 12,/* 179 */ 12, | ||||
|   /* 180 */ 13,/* 181 */ 13,/* 182 */ 13,/* 183 */ 13,/* 184 */ 13,/* 185 */ 13,/* 186 */ 13,/* 187 */ 13,/* 188 */ 13,/* 189 */ 13, | ||||
|   /* 190 */ 13,/* 191 */ 13,/* 192 */ 13,/* 193 */ 13,/* 194 */ 13,/* 195 */ 13,/* 196 */ 13,/* 197 */ 13,/* 198 */ 13,/* 199 */ 13, | ||||
|   /* 200 */ 13,/* 201 */ 13,/* 202 */ 13,/* 203 */ 13,/* 204 */ 13,/* 205 */ 13,/* 206 */ 13,/* 207 */ 13,/* 208 */ 13,/* 209 */ 13, | ||||
|   /* 210 */ 13,/* 211 */ 13,/* 212 */ 13,/* 213 */ 13,/* 214 */ 13,/* 215 */ 13,/* 216 */ 13,/* 217 */ 13,/* 218 */ 13,/* 219 */ 13, | ||||
|   /* 220 */ 14,/* 221 */ 14,/* 222 */ 14,/* 223 */ 14,/* 224 */ 14,/* 225 */ 14,/* 226 */ 14,/* 227 */ 14,/* 228 */ 14,/* 229 */ 14, | ||||
|   /* 230 */ 14,/* 231 */ 14,/* 232 */ 14,/* 233 */ 14,/* 234 */ 14,/* 235 */ 14,/* 236 */ 14,/* 237 */ 14,/* 238 */ 14,/* 239 */ 14, | ||||
|   /* 240 */ 14,/* 241 */ 14,/* 242 */ 14,/* 243 */ 14,/* 244 */ 14,/* 245 */ 14,/* 246 */ 14,/* 247 */ 14,/* 248 */ 14,/* 249 */ 14, | ||||
|   /* 250 */ 15,/* 251 */ 15,/* 252 */ 15,/* 253 */ 15,/* 254 */ 15,/* 255 */ 15,/* 256 */ 15,/* 257 */ 15,/* 258 */ 15,/* 259 */ 15, | ||||
|   /* 260 */ 15,/* 261 */ 15,/* 262 */ 15,/* 263 */ 15,/* 264 */ 15,/* 265 */ 15,/* 266 */ 15,/* 267 */ 15,/* 268 */ 15,/* 269 */ 15, | ||||
|   /* 270 */ 15,/* 271 */ 15,/* 272 */ 15,/* 273 */ 15,/* 274 */ 15,/* 275 */ 15,/* 276 */ 15,/* 277 */ 15,/* 278 */ 15,/* 279 */ 15, | ||||
|   /* 280 */ 16,/* 281 */ 16,/* 282 */ 16,/* 283 */ 16,/* 284 */ 16,/* 285 */ 16,/* 286 */ 16,/* 287 */ 16,/* 288 */ 16,/* 289 */ 16, | ||||
|   /* 290 */ 16,/* 291 */ 16,/* 292 */ 16,/* 293 */ 16,/* 294 */ 16,/* 295 */ 16,/* 296 */ 16,/* 297 */ 16,/* 298 */ 16,/* 299 */ 16, | ||||
|   /* 300 */ 16,/* 301 */ 16,/* 302 */ 16,/* 303 */ 16,/* 304 */ 16,/* 305 */ 16,/* 306 */ 16,/* 307 */ 16,/* 308 */ 16,/* 309 */ 16, | ||||
|   /* 310 */ 17,/* 311 */ 17,/* 312 */ 17,/* 313 */ 17,/* 314 */ 17,/* 315 */ 17,/* 316 */ 17,/* 317 */ 17,/* 318 */ 17,/* 319 */ 17, | ||||
|   /* 320 */ 17,/* 321 */ 17,/* 322 */ 17,/* 323 */ 17,/* 324 */ 17,/* 325 */ 17,/* 326 */ 17,/* 327 */ 17,/* 328 */ 17,/* 329 */ 17, | ||||
|   /* 330 */ 17,/* 331 */ 17,/* 332 */ 17,/* 333 */ 17,/* 334 */ 17,/* 335 */ 17,/* 336 */ 17,/* 337 */ 17,/* 338 */ 17,/* 339 */ 17, | ||||
|   /* 340 */ 17,/* 341 */ 17,/* 342 */ 17,/* 343 */ 17,/* 344 */ 17,/* 345 */ 17,/* 346 */ 17,/* 347 */ 17,/* 348 */ 17,/* 349 */ 17, | ||||
|   /* 350 */ 17,/* 351 */ 17,/* 352 */ 17,/* 353 */ 17,/* 354 */ 17,/* 355 */ 17,/* 356 */ 17,/* 357 */ 17,/* 358 */ 17,/* 359 */ 17, | ||||
|   /* 360 */ 17,/* 361 */ 17,/* 362 */ 17,/* 363 */ 17,/* 364 */ 17,/* 365 */ 17,/* 366 */ 17,/* 367 */ 17,/* 368 */ 17,/* 369 */ 17, | ||||
|   /* 370 */ 17,/* 371 */ 17,/* 372 */ 17,/* 373 */ 17,/* 374 */ 17,/* 375 */ 17,/* 376 */ 17,/* 377 */ 17,/* 378 */ 17,/* 379 */ 17, | ||||
|   /* 380 */ 17,/* 381 */ 17,/* 382 */ 17,/* 383 */ 17,/* 384 */ 17,/* 385 */ 17,/* 386 */ 17,/* 387 */ 17,/* 388 */ 17,/* 389 */ 17, | ||||
|   /* 390 */ 17,/* 391 */ 17,/* 392 */ 17,/* 393 */ 17,/* 394 */ 17,/* 395 */ 17,/* 396 */ 17,/* 397 */ 17,/* 398 */ 17,/* 399 */ 17, | ||||
|   /* 400 */ 17,/* 401 */ 17,/* 402 */ 17,/* 403 */ 17,/* 404 */ 17,/* 405 */ 17,/* 406 */ 17,/* 407 */ 17,/* 408 */ 17,/* 409 */ 17, | ||||
|   /* 410 */ 17,/* 411 */ 17,/* 412 */ 17,/* 413 */ 17,/* 414 */ 17,/* 415 */ 17,/* 416 */ 17,/* 417 */ 17,/* 418 */ 17,/* 419 */ 17, | ||||
|   /* 420 */ 17,/* 421 */ 17,/* 422 */ 17,/* 423 */ 17,/* 424 */ 17,/* 425 */ 17,/* 426 */ 17,/* 427 */ 17,/* 428 */ 17,/* 429 */ 17, | ||||
|   /* 430 */ 17,/* 431 */ 17,/* 432 */ 17,/* 433 */ 17,/* 434 */ 17,/* 435 */ 17,/* 436 */ 17,/* 437 */ 17,/* 438 */ 17,/* 439 */ 17, | ||||
|   /* 440 */ 17,/* 441 */ 17,/* 442 */ 17,/* 443 */ 17,/* 444 */ 17,/* 445 */ 17,/* 446 */ 17,/* 447 */ 17,/* 448 */ 17,/* 449 */ 17, | ||||
|   /* 450 */ 18,/* 451 */ 18,/* 452 */ 18,/* 453 */ 18,/* 454 */ 18,/* 455 */ 18,/* 456 */ 18,/* 457 */ 18,/* 458 */ 18,/* 459 */ 18, | ||||
|   /* 460 */ 18,/* 461 */ 18,/* 462 */ 18,/* 463 */ 18,/* 464 */ 18,/* 465 */ 18,/* 466 */ 18,/* 467 */ 18,/* 468 */ 18,/* 469 */ 18, | ||||
|   /* 470 */ 18,/* 471 */ 18,/* 472 */ 18,/* 473 */ 18,/* 474 */ 18,/* 475 */ 18,/* 476 */ 18,/* 477 */ 18,/* 478 */ 18,/* 479 */ 18, | ||||
|   /* 480 */ 18,/* 481 */ 18,/* 482 */ 18,/* 483 */ 18,/* 484 */ 18,/* 485 */ 18,/* 486 */ 18,/* 487 */ 18,/* 488 */ 18,/* 489 */ 18, | ||||
|   /* 490 */ 18,/* 491 */ 18,/* 492 */ 18,/* 493 */ 18,/* 494 */ 18,/* 495 */ 18,/* 496 */ 18,/* 497 */ 18,/* 498 */ 18,/* 499 */ 18, | ||||
|   /* 500 */ 18,/* 501 */ 18,/* 502 */ 18,/* 503 */ 18,/* 504 */ 18,/* 505 */ 18,/* 506 */ 18,/* 507 */ 18,/* 508 */ 18,/* 509 */ 18, | ||||
|   /* 510 */ 18,/* 511 */ 18,/* 512 */ 18,/* 513 */ 18,/* 514 */ 18,/* 515 */ 18,/* 516 */ 18,/* 517 */ 18,/* 518 */ 18,/* 519 */ 18, | ||||
|   /* 520 */ 18,/* 521 */ 18,/* 522 */ 18,/* 523 */ 18,/* 524 */ 18,/* 525 */ 18,/* 526 */ 18,/* 527 */ 18,/* 528 */ 18,/* 529 */ 18, | ||||
|   /* 530 */ 18,/* 531 */ 18,/* 532 */ 18,/* 533 */ 18,/* 534 */ 18,/* 535 */ 18,/* 536 */ 18,/* 537 */ 18,/* 538 */ 18,/* 539 */ 18, | ||||
|   /* 540 */ 18,/* 541 */ 18,/* 542 */ 18,/* 543 */ 18,/* 544 */ 18,/* 545 */ 18,/* 546 */ 18,/* 547 */ 18,/* 548 */ 18,/* 549 */ 18, | ||||
|   /* 550 */ 18,/* 551 */ 18,/* 552 */ 18,/* 553 */ 18,/* 554 */ 18,/* 555 */ 18,/* 556 */ 18,/* 557 */ 18,/* 558 */ 18,/* 559 */ 18, | ||||
|   /* 560 */ 18,/* 561 */ 18,/* 562 */ 18,/* 563 */ 18,/* 564 */ 18,/* 565 */ 18,/* 566 */ 18,/* 567 */ 18,/* 568 */ 18,/* 569 */ 18, | ||||
|   /* 570 */ 18,/* 571 */ 18,/* 572 */ 18,/* 573 */ 18,/* 574 */ 18,/* 575 */ 18,/* 576 */ 18,/* 577 */ 18,/* 578 */ 18,/* 579 */ 18, | ||||
|   /* 580 */ 18,/* 581 */ 18,/* 582 */ 18,/* 583 */ 18,/* 584 */ 18,/* 585 */ 18,/* 586 */ 18,/* 587 */ 18, | ||||
|   /* 588 */ 19,/* 589 */ 19,/* 590 */ 19,/* 591 */ 19,/* 592 */ 19,/* 593 */ 19,/* 594 */ 19,/* 595 */ 19,/* 596 */ 19,/* 597 */ 19, | ||||
|   /* 598 */ 19,/* 599 */ 19,/* 600 */ 19,/* 601 */ 19,/* 602 */ 19,/* 603 */ 19,/* 604 */ 19,/* 605 */ 19,/* 606 */ 19,/* 607 */ 19, | ||||
|   /* 608 */ 19,/* 609 */ 19,/* 610 */ 19,/* 611 */ 19,/* 612 */ 19,/* 613 */ 19,/* 614 */ 19,/* 615 */ 19,/* 616 */ 19,/* 617 */ 19, | ||||
|   /* 618 */ 19,/* 619 */ 19,/* 620 */ 19,/* 621 */ 19,/* 622 */ 19,/* 623 */ 19,/* 624 */ 19,/* 625 */ 19,/* 626 */ 19,/* 627 */ 19, | ||||
|   /* 628 */ 19,/* 629 */ 19,/* 630 */ 19,/* 631 */ 19,/* 632 */ 19,/* 633 */ 19,/* 634 */ 19,/* 635 */ 19,/* 636 */ 19,/* 637 */ 19, | ||||
|   /* 638 */ 19,/* 639 */ 19,/* 640 */ 19,/* 641 */ 19,/* 642 */ 19,/* 643 */ 19,/* 644 */ 19,/* 645 */ 19,/* 646 */ 19,/* 647 */ 19, | ||||
|   /* 648 */ 19,/* 649 */ 19 | ||||
| }; | ||||
|  | ||||
| #define FREQ_50MHZ (50) | ||||
| #define FREQ_TO_IDX_0_TO_49MHZ (0) | ||||
| #define FREQ_TO_IDX_650_TO_1800MHZ (20) | ||||
|  | ||||
| int r820t_freq_get_idx(uint32_t freq_mhz) | ||||
| { | ||||
|   uint32_t freq_mhz_fix; | ||||
|  | ||||
|   if(freq_mhz < FREQ_50MHZ) | ||||
|   { | ||||
|     /* Frequency Less than 50MHz */ | ||||
|     return FREQ_TO_IDX_0_TO_49MHZ; | ||||
|   }else | ||||
|   { | ||||
|     /* Frequency Between 50 to 649MHz use table */ | ||||
|     /* Fix the frequency for the table */ | ||||
|     freq_mhz_fix = freq_mhz - FREQ_50MHZ; | ||||
|     if(freq_mhz_fix < FREQ_TO_IDX_SIZE) | ||||
|     { | ||||
|  | ||||
|       return freq_to_idx[freq_mhz_fix]; | ||||
|     }else | ||||
|     { | ||||
|       /* Frequency Between 650 to 1800MHz */ | ||||
|       return FREQ_TO_IDX_650_TO_1800MHZ; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| static inline bool r820t_is_power_enabled(void) | ||||
| { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Write regs 5 to 32 (R820T_INIT_NB_REGS values) using data parameter and write last reg to 0 | ||||
|  */ | ||||
| void airspy_r820t_write_init(r820t_priv_t *priv, const uint8_t* data) | ||||
| { | ||||
|   for (int i = 0; i < R820T_INIT_NB_REGS; i++) { | ||||
|     priv->write_reg(i+REG_SHADOW_START, data[i], priv->ctx); | ||||
|   } | ||||
|   priv->write_reg(0x1F, 0, priv->ctx); | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Read from one or more contiguous registers. data[0] should be the first | ||||
|  * register number, one or more values follow. | ||||
|  */ | ||||
|  const uint8_t lut[16] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, | ||||
|       0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; | ||||
|  | ||||
| static uint8_t r82xx_bitrev(uint8_t byte) | ||||
| { | ||||
|  return (lut[byte & 0xf] << 4) | lut[byte >> 4]; | ||||
| } | ||||
|  | ||||
| static int r820t_write_reg(r820t_priv_t *priv, uint8_t reg, uint8_t val) | ||||
| { | ||||
|   if (r820t_read_cache_reg(priv, reg) == val) | ||||
|     return 0; | ||||
|   priv->write_reg(reg, val, priv->ctx); | ||||
|   priv->regs[reg - REG_SHADOW_START] = val; | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| static int r820t_read_cache_reg(r820t_priv_t *priv, int reg) | ||||
| { | ||||
|   reg -= REG_SHADOW_START; | ||||
|  | ||||
|   if (reg >= 0 && reg < NUM_REGS) | ||||
|     return priv->regs[reg]; | ||||
|   else | ||||
|     return -1; | ||||
| } | ||||
|  | ||||
| static int r820t_write_reg_mask(r820t_priv_t *priv, uint8_t reg, uint8_t val, uint8_t bit_mask) | ||||
| { | ||||
|   int rc = r820t_read_cache_reg(priv, reg); | ||||
|  | ||||
|   if (rc < 0) | ||||
|     return rc; | ||||
|  | ||||
|   val = (rc & ~bit_mask) | (val & bit_mask); | ||||
|  | ||||
|   return r820t_write_reg(priv, reg, val); | ||||
| } | ||||
|  | ||||
| static int r820t_read(r820t_priv_t *priv, uint8_t *val, int len) | ||||
| { | ||||
|   /* reg not used and assumed to be always 0 because start from reg0 to reg0+len */ | ||||
|   priv->read_reg(val, len, priv->ctx); | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * r820t tuning logic | ||||
|  */ | ||||
| #ifdef OPTIM_SET_MUX | ||||
| int r820t_set_mux_freq_idx = -1; /* Default set to invalid value in order to force set_mux */ | ||||
| #endif | ||||
|  | ||||
| /* | ||||
| "inspired by Mauro Carvalho Chehab set_mux technique" | ||||
| https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c | ||||
| part of r820t_set_mux() (set tracking filter) | ||||
| */ | ||||
| static int r820t_set_tf(r820t_priv_t *priv, uint32_t freq) | ||||
| { | ||||
|   const struct r820t_freq_range *range; | ||||
|   int freq_idx; | ||||
|   int rc = 0; | ||||
|  | ||||
|   /* Get the proper frequency range in MHz instead of Hz */ | ||||
|   /* Fast divide freq by 1000000 */ | ||||
|   freq = (uint32_t)((uint64_t)freq * 4295 >> 32); | ||||
|  | ||||
|   freq_idx = r820t_freq_get_idx(freq); | ||||
|   range = &freq_ranges[freq_idx]; | ||||
|  | ||||
|   /* Only reconfigure mux freq if modified vs previous range */ | ||||
| #ifdef OPTIM_SET_MUX | ||||
|   if(freq_idx != r820t_set_mux_freq_idx) | ||||
|   { | ||||
| #endif | ||||
|     /* Open Drain */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* RF_MUX,Polymux */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* TF BAND */ | ||||
|     rc = r820t_write_reg(priv, 0x1b, range->tf_c); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* XTAL CAP & Drive */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x10, 0x08, 0x0b); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     rc = r820t_write_reg_mask(priv, 0x08, 0x00, 0x3f); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     rc = r820t_write_reg_mask(priv, 0x09, 0x00, 0x3f); | ||||
| #ifdef OPTIM_SET_MUX | ||||
|   } | ||||
|   r820t_set_mux_freq_idx = freq_idx; | ||||
| #endif | ||||
|  | ||||
|   return rc; | ||||
| } | ||||
|  | ||||
| int r820t_set_pll(r820t_priv_t *priv, uint32_t freq) | ||||
| { | ||||
|   const uint32_t vco_min = 1770000000; | ||||
|   const uint32_t vco_max = 3900000000; | ||||
|   uint32_t ref = priv->xtal_freq >> 1; | ||||
|  | ||||
|   int rc; | ||||
|   uint32_t div_num; | ||||
|   uint32_t vco; | ||||
|   uint32_t rem; | ||||
|   uint32_t mask; | ||||
|   uint16_t sdm; | ||||
|   uint8_t nint; | ||||
|   uint8_t ni; | ||||
|   uint8_t si; | ||||
|   uint8_t div_found; | ||||
|  | ||||
|   /* Find a suitable divider */ | ||||
|   div_found = 0; | ||||
|   for (div_num = 0; div_num <= 5; div_num++) | ||||
|   { | ||||
|     vco = freq << (div_num + 1); | ||||
|     if (vco >= vco_min && vco <= vco_max) | ||||
|     { | ||||
|       div_found = 1; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (!div_found) | ||||
|     return -1; | ||||
|  | ||||
|   vco += ref >> 16; | ||||
|   ref <<= 8; | ||||
|   mask = 1 << 23; | ||||
|   rem = 0; | ||||
|   while (mask > 0 && vco > 0) | ||||
|   { | ||||
|     if (vco >= ref) | ||||
|     { | ||||
|       rem |= mask; | ||||
|       vco -= ref; | ||||
|     } | ||||
|     ref >>= 1; | ||||
|     mask >>= 1; | ||||
|   } | ||||
|  | ||||
|   nint = rem >> 16; | ||||
|   sdm = rem & 0xffff; | ||||
|  | ||||
|   nint -= 13; | ||||
|   ni = nint >> 2; | ||||
|   si = nint & 3; | ||||
|  | ||||
|   /* Set the phase splitter */ | ||||
|   rc = r820t_write_reg_mask(priv, 0x10, (uint8_t) (div_num << 5), 0xe0); | ||||
|   if(rc < 0) | ||||
|     return rc; | ||||
|  | ||||
|   /* Set the integer part of the PLL */ | ||||
|   rc = r820t_write_reg(priv, 0x14, (uint8_t) (ni + (si << 6))); | ||||
|   if(rc < 0) | ||||
|     return rc; | ||||
|  | ||||
|   if (sdm == 0) | ||||
|   { | ||||
|     /* Disable SDM */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x12, 0x08, 0x08); | ||||
|     if(rc < 0) | ||||
|       return rc; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
| 	/* Write SDM */ | ||||
|     rc = r820t_write_reg(priv, 0x15, (uint8_t)(sdm & 0xff)); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     rc = r820t_write_reg(priv, 0x16, (uint8_t)(sdm >> 8)); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* Enable SDM */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x12, 0x00, 0x08); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|   } | ||||
|   return rc; | ||||
| } | ||||
|  | ||||
| int r820t_set_freq(r820t_priv_t *priv, uint32_t freq) | ||||
| { | ||||
|   int rc; | ||||
|   uint32_t lo_freq = freq + priv->if_freq; | ||||
|  | ||||
|   rc = r820t_set_tf(priv, freq); | ||||
|   if (rc < 0) | ||||
|     return rc; | ||||
|  | ||||
|   rc = r820t_set_pll(priv, lo_freq); | ||||
|   if (rc < 0) | ||||
|     return rc; | ||||
|  | ||||
|   priv->freq = freq; | ||||
|  | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index) | ||||
| { | ||||
|   return r820t_write_reg_mask(priv, 0x05, gain_index, 0x0f); | ||||
| } | ||||
|  | ||||
| int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index) | ||||
| { | ||||
|   return r820t_write_reg_mask(priv, 0x07, gain_index, 0x0f); | ||||
| } | ||||
|  | ||||
| int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index) | ||||
| { | ||||
|   return r820t_write_reg_mask(priv, 0x0c, gain_index, 0x0f); | ||||
| } | ||||
|  | ||||
| int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value) | ||||
| { | ||||
|   value = value != 0 ? 0x00 : 0x10; | ||||
|   return r820t_write_reg_mask(priv, 0x05, value, 0x10); | ||||
| } | ||||
|  | ||||
| int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value) | ||||
| { | ||||
|   value = value != 0 ? 0x10 : 0x00; | ||||
|   return r820t_write_reg_mask(priv, 0x07, value, 0x10); | ||||
| } | ||||
|  | ||||
| /*  | ||||
| "inspired by Mauro Carvalho Chehab calibration technique" | ||||
| https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c | ||||
| part of r820t_set_tv_standard() | ||||
| */ | ||||
| int r820t_calibrate(r820t_priv_t *priv) | ||||
| { | ||||
|   int i, rc, cal_code; | ||||
|   uint8_t data[5]; | ||||
|  | ||||
|   for (i = 0; i < 5; i++) | ||||
|   { | ||||
|     /* Set filt_cap */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x0b, 0x08, 0x60); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* set cali clk =on */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* X'tal cap 0pF for PLL */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     rc = r820t_set_pll(priv, CALIBRATION_LO * 1000); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* Start Trigger */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||||
|  | ||||
|     /* Stop Trigger */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* set cali clk =off */ | ||||
|     rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     /* Check if calibration worked */ | ||||
|     rc = r820t_read(priv, data, sizeof(data)); | ||||
|     if (rc < 0) | ||||
|       return rc; | ||||
|  | ||||
|     cal_code = data[4] & 0x0f; | ||||
|     if (cal_code && cal_code != 0x0f) | ||||
|       return 0; | ||||
|   } | ||||
|  | ||||
|   return -1; | ||||
| } | ||||
|  | ||||
| int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx) | ||||
| { | ||||
|   int rc; | ||||
|   uint32_t saved_freq; | ||||
|  | ||||
|   r820t_state_standby = 0; | ||||
|   priv->if_freq = if_freq; | ||||
|   priv->write_reg = write_reg; | ||||
|   priv->read_reg = read_reg; | ||||
|   priv->ctx = ctx; | ||||
|   /* Initialize registers */ | ||||
|   airspy_r820t_write_init(priv, priv->regs); | ||||
|  | ||||
|   r820t_set_freq(priv, priv->freq); | ||||
|  | ||||
|   /* Calibrate the IF filter */ | ||||
|   saved_freq = priv->freq; | ||||
|   rc = r820t_calibrate(priv); | ||||
|   priv->freq = saved_freq; | ||||
|   if (rc < 0) | ||||
|   { | ||||
|     saved_freq = priv->freq; | ||||
|     r820t_calibrate(priv); | ||||
|     priv->freq = saved_freq; | ||||
|   } | ||||
|  | ||||
|   /* Restore freq as it has been modified by r820t_calibrate() */ | ||||
|   rc = r820t_set_freq(priv, priv->freq); | ||||
|   return rc; | ||||
| } | ||||
|  | ||||
| void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw) | ||||
| { | ||||
|     const uint8_t modes[] = { 0xE0, 0x80, 0x60, 0x00 }; | ||||
|     const uint8_t opt[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; | ||||
|     uint8_t a = 0xB0 | opt[bw & 0x0F]; | ||||
|     uint8_t b = 0x0F | modes[bw >> 4]; | ||||
|     r820t_write_reg(priv, 0x0A, a); | ||||
|     r820t_write_reg(priv, 0x0B, b); | ||||
| } | ||||
							
								
								
									
										58
									
								
								source_modules/badgesdr_source/src/r820t.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								source_modules/badgesdr_source/src/r820t.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
|  * Copyright 2013-2016 Benjamin Vernoux <bvernoux@airspy.com> | ||||
|  * | ||||
|  * This file is part of AirSpy. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation; either version 2, or (at your option) | ||||
|  * any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; see the file COPYING.  If not, write to | ||||
|  * the Free Software Foundation, Inc., 51 Franklin Street, | ||||
|  * Boston, MA 02110-1301, USA. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include <stdint.h> | ||||
|  | ||||
| #define REG_SHADOW_START 5 | ||||
| #define NUM_REGS 30 | ||||
|  | ||||
| /* R820T Clock */ | ||||
| #define CALIBRATION_LO 88000 | ||||
|  | ||||
| typedef void (*r820t_write_reg_f)(uint8_t reg, uint8_t value, void* ctx); | ||||
| typedef void (*r820t_read_f)(uint8_t* data, int len, void* ctx); | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
|   uint32_t xtal_freq; /* XTAL_FREQ_HZ */ | ||||
|   uint32_t freq; | ||||
|   uint32_t if_freq; | ||||
|   uint8_t regs[NUM_REGS]; | ||||
|   uint16_t padding; | ||||
|  | ||||
|   r820t_write_reg_f write_reg; | ||||
|   r820t_read_f read_reg; | ||||
|   void* ctx; | ||||
|  | ||||
| } r820t_priv_t; | ||||
|  | ||||
| void airspy_r820t_write_single(r820t_priv_t *priv, uint8_t reg, uint8_t val); | ||||
| uint8_t airspy_r820t_read_single(r820t_priv_t *priv, uint8_t reg); | ||||
|  | ||||
| int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx); | ||||
| int r820t_set_freq(r820t_priv_t *priv, uint32_t freq); | ||||
| int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index); | ||||
| int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index); | ||||
| int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index); | ||||
| int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value); | ||||
| int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value); | ||||
| void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw); | ||||
							
								
								
									
										21
									
								
								source_modules/fobossdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								source_modules/fobossdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(fobossdr_source) | ||||
|  | ||||
| file(GLOB SRC "src/*.cpp") | ||||
|  | ||||
| include(${SDRPP_MODULE_CMAKE}) | ||||
|  | ||||
| if (MSVC) | ||||
|     # Lib path | ||||
|     target_link_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/lib/") | ||||
|     target_include_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/include/") | ||||
|     target_link_libraries(fobossdr_source PRIVATE fobos) | ||||
| else (MSVC) | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|     pkg_check_modules(LIBFOBOS REQUIRED libfobos) | ||||
|  | ||||
|     target_include_directories(fobossdr_source PRIVATE ${LIBFOBOS_INCLUDE_DIRS}) | ||||
|     target_link_directories(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARY_DIRS}) | ||||
|     target_link_libraries(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARIES}) | ||||
| endif () | ||||
							
								
								
									
										560
									
								
								source_modules/fobossdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										560
									
								
								source_modules/fobossdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,560 @@ | ||||
| #include <imgui.h> | ||||
| #include <module.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/smgui.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <core.h> | ||||
| #include <utils/optionlist.h> | ||||
| #include <atomic> | ||||
| #include <fobos.h> | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
|     /* Name:            */ "fobossdr_source", | ||||
|     /* Description:     */ "FobosSDR Source Module", | ||||
|     /* Author:          */ "Ryzerth", | ||||
|     /* Version:         */ 0, 1, 0, | ||||
|     /* Max instances    */ -1 | ||||
| }; | ||||
|  | ||||
| ConfigManager config; | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| // Work around for the fobos API not including | ||||
| #define FOBOS_LNA_GAIN_MIN  1 | ||||
| #define FOBOS_LNA_GAIN_MAX  3 | ||||
| #define FOBOS_VGA_GAIN_MIN  0 | ||||
| #define FOBOS_VGA_GAIN_MAX  31 | ||||
|  | ||||
| class FobosSDRSourceModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     FobosSDRSourceModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         sampleRate = 50000000.0; | ||||
|  | ||||
|         // Initialize the DDC | ||||
|         ddc.init(&ddcIn, 50e6, 50e6, 50e6, 0.0); | ||||
|  | ||||
|         handler.ctx = this; | ||||
|         handler.selectHandler = menuSelected; | ||||
|         handler.deselectHandler = menuDeselected; | ||||
|         handler.menuHandler = menuHandler; | ||||
|         handler.startHandler = start; | ||||
|         handler.stopHandler = stop; | ||||
|         handler.tuneHandler = tune; | ||||
|         handler.stream = &ddc.out; | ||||
|  | ||||
|         // Refresh devices | ||||
|         refresh(); | ||||
|  | ||||
|         // Select device from config | ||||
|         config.acquire(); | ||||
|         std::string devSerial = config.conf["device"]; | ||||
|         config.release(); | ||||
|         select(devSerial); | ||||
|  | ||||
|         sigpath::sourceManager.registerSource("FobosSDR", &handler); | ||||
|     } | ||||
|  | ||||
|     ~FobosSDRSourceModule() { | ||||
|         // Nothing to do | ||||
|     } | ||||
|  | ||||
|     void postInit() {} | ||||
|  | ||||
|     void enable() { | ||||
|         enabled = true; | ||||
|     } | ||||
|  | ||||
|     void disable() { | ||||
|         enabled = false; | ||||
|     } | ||||
|  | ||||
|     bool isEnabled() { | ||||
|         return enabled; | ||||
|     } | ||||
|  | ||||
|     enum Port { | ||||
|         PORT_RF, | ||||
|         PORT_HF1, | ||||
|         PORT_HF2 | ||||
|     }; | ||||
|  | ||||
| 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); | ||||
|     } | ||||
|  | ||||
|     void refresh() { | ||||
|         devices.clear(); | ||||
|          | ||||
|         // Get device list | ||||
|         char serials[1024]; | ||||
|         memset(serials, 0, sizeof(serials)); | ||||
|         int devCount = fobos_rx_list_devices(serials); | ||||
|         if (devCount < 0) { | ||||
|             flog::error("Failed to get device list: {}", devCount); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // If no device, give up | ||||
|         if (!devCount) { return; } | ||||
|  | ||||
|         // Generate device entries | ||||
|         const char* _serials = serials; | ||||
|         int index = 0; | ||||
|         while (*_serials) { | ||||
|             // Read serial until space | ||||
|             std::string serial = ""; | ||||
|             while (*_serials) { | ||||
|                 // Get a character | ||||
|                 char c = *(_serials++); | ||||
|  | ||||
|                 // If it's a space, we're done | ||||
|                 if (c == ' ') { break; } | ||||
|  | ||||
|                 // Otherwise, add it to the string | ||||
|                 serial += c; | ||||
|             } | ||||
|  | ||||
|             // Create entry | ||||
|             devices.define(serial, serial, index++); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void select(const std::string& serial) { | ||||
|         // If there are no devices, give up | ||||
|         if (devices.empty()) { | ||||
|             selectedSerial.clear(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // If the serial was not found, select the first available serial | ||||
|         if (!devices.keyExists(serial)) { | ||||
|             select(devices.key(0)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Get the ID in the list | ||||
|         int id = devices.keyId(serial); | ||||
|         selectedDevId = devices[id]; | ||||
|  | ||||
|         // Open the device | ||||
|         fobos_dev_t* dev; | ||||
|         int err = fobos_rx_open(&dev, selectedDevId); | ||||
|         if (err) { | ||||
|             flog::error("Failed to open device: {}", err); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Get a list of supported samplerates | ||||
|         double srList[128]; | ||||
|         unsigned int srCount; | ||||
|         err = fobos_rx_get_samplerates(dev, srList, &srCount); | ||||
|         if (err) { | ||||
|             flog::error("Failed to get samplerate list: {}", err); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Generate samplerate list | ||||
|         samplerates.clear(); | ||||
|         for (int i = 0; i < srCount; i++) { | ||||
|             std::string str = getBandwdithScaled(srList[i]); | ||||
|             samplerates.define(srList[i], str, srList[i]); | ||||
|         } | ||||
|  | ||||
|         // Add some custom samplerates | ||||
|         samplerates.define(5e6, "5.0MHz", 5e6); | ||||
|         samplerates.define(2.5e6, "2.5MHz", 2.5e6); | ||||
|         samplerates.define(1.25e6, "1.25MHz", 1.25e6); | ||||
|  | ||||
|         // Define the ports | ||||
|         ports.clear(); | ||||
|         ports.define("rf", "RF", PORT_RF); | ||||
|         ports.define("hf1", "HF1", PORT_HF1); | ||||
|         ports.define("hf2", "HF2", PORT_HF2); | ||||
|  | ||||
|         // Define clock sources | ||||
|         clockSources.clear(); | ||||
|         clockSources.define("internal", "Internal", 0); | ||||
|         clockSources.define("external", "External", 1); | ||||
|  | ||||
|         // Close the device | ||||
|         fobos_rx_close(dev); | ||||
|  | ||||
|         // Save serial number | ||||
|         selectedSerial = serial; | ||||
|         devId = id; | ||||
|  | ||||
|         // Load default options | ||||
|         sampleRate = 50e6; | ||||
|         srId = samplerates.valueId(sampleRate); | ||||
|         port = PORT_RF; | ||||
|         portId = ports.valueId(port); | ||||
|         clkSrcId = clockSources.nameId("Internal"); | ||||
|         lnaGain = 0; | ||||
|         vgaGain = 0; | ||||
|  | ||||
|         // Load config | ||||
|         config.acquire(); | ||||
|         if (config.conf["devices"][selectedSerial].contains("samplerate")) { | ||||
|             int desiredSr = config.conf["devices"][selectedSerial]["samplerate"]; | ||||
|             if (samplerates.keyExists(desiredSr)) { | ||||
|                 srId = samplerates.keyId(desiredSr); | ||||
|                 sampleRate = samplerates[srId]; | ||||
|             } | ||||
|         } | ||||
|         if (config.conf["devices"][selectedSerial].contains("port")) { | ||||
|             std::string desiredPort = config.conf["devices"][selectedSerial]["port"]; | ||||
|             if (ports.keyExists(desiredPort)) { | ||||
|                 portId = ports.keyId(desiredPort); | ||||
|                 port = ports[portId]; | ||||
|             } | ||||
|         } | ||||
|         if (config.conf["devices"][selectedSerial].contains("clkSrc")) { | ||||
|             std::string desiredClkSrc = config.conf["devices"][selectedSerial]["clkSrc"]; | ||||
|             if (clockSources.keyExists(desiredClkSrc)) { | ||||
|                 clkSrcId = clockSources.keyId(desiredClkSrc); | ||||
|             } | ||||
|         } | ||||
|         if (config.conf["devices"][selectedSerial].contains("lnaGain")) { | ||||
|             lnaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["lnaGain"], FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX); | ||||
|         } | ||||
|         if (config.conf["devices"][selectedSerial].contains("vgaGain")) { | ||||
|             vgaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["vgaGain"], FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX); | ||||
|         } | ||||
|         config.release(); | ||||
|  | ||||
|         // Update the samplerate | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
|         FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; | ||||
|         core::setInputSampleRate(_this->sampleRate); | ||||
|         flog::info("FobosSDRSourceModule '{0}': Menu Select!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void menuDeselected(void* ctx) { | ||||
|         FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; | ||||
|         flog::info("FobosSDRSourceModule '{0}': Menu Deselect!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void start(void* ctx) { | ||||
|         FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; | ||||
|         if (_this->running) { return; } | ||||
|  | ||||
|         // Open the device | ||||
|         int err = fobos_rx_open(&_this->openDev, _this->selectedDevId); | ||||
|         if (err) { | ||||
|             flog::error("Failed to open device: {}", err); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Get the selected port | ||||
|         _this->port = _this->ports[_this->portId]; | ||||
|  | ||||
|         // Configure the device | ||||
|         double actualSr, actualFreq; | ||||
|         fobos_rx_set_samplerate(_this->openDev, (_this->sampleRate >= 50e6) ? _this->sampleRate : 50e6, &actualSr); | ||||
|         fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq); | ||||
|         fobos_rx_set_direct_sampling(_this->openDev, _this->port != PORT_RF); | ||||
|         fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]); | ||||
|         fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain); | ||||
|         fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain); | ||||
|  | ||||
|         // Configure the DDC | ||||
|         if (_this->port == PORT_RF && _this->sampleRate >= 50e6) { | ||||
|             // Set the frequency | ||||
|             fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq); | ||||
|         } | ||||
|         else if (_this->port == PORT_RF) { | ||||
|             // Set the frequency | ||||
|             fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq); | ||||
|  | ||||
|             // Configure and start the DDC for decimation only | ||||
|             _this->ddc.setInSamplerate(actualSr); | ||||
|             _this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate); | ||||
|             _this->ddc.setOffset(0.0); | ||||
|             _this->ddc.start(); | ||||
|         } | ||||
|         else { | ||||
|             // Configure and start the DDC | ||||
|             _this->ddc.setInSamplerate(actualSr); | ||||
|             _this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate); | ||||
|             _this->ddc.setOffset(_this->freq); | ||||
|             _this->ddc.start(); | ||||
|         } | ||||
|  | ||||
|         // Compute buffer size (Lower than usual, but it's a workaround for their API having broken streaming) | ||||
|         _this->bufferSize = _this->sampleRate / 400.0; | ||||
|  | ||||
|         // Start streaming | ||||
|         err = fobos_rx_start_sync(_this->openDev, _this->bufferSize); | ||||
|         if (err) { | ||||
|             flog::error("Failed to start stream: {}", err); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Start worker | ||||
|         _this->run = true; | ||||
|         _this->workerThread = std::thread(&FobosSDRSourceModule::worker, _this); | ||||
|          | ||||
|         _this->running = true; | ||||
|         flog::info("FobosSDRSourceModule '{0}': Start!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void stop(void* ctx) { | ||||
|         FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; | ||||
|         if (!_this->running) { return; } | ||||
|         _this->running = false; | ||||
|  | ||||
|         // Stop worker | ||||
|         _this->run = false; | ||||
|         if (_this->port == PORT_RF && _this->sampleRate >= 50e6) { | ||||
|             _this->ddc.out.stopWriter(); | ||||
|             if (_this->workerThread.joinable()) { _this->workerThread.join(); } | ||||
|             _this->ddc.out.clearWriteStop(); | ||||
|         } | ||||
|         else { | ||||
|             _this->ddcIn.stopWriter(); | ||||
|             if (_this->workerThread.joinable()) { _this->workerThread.join(); } | ||||
|             _this->ddcIn.clearWriteStop(); | ||||
|         } | ||||
|  | ||||
|         // Stop streaming | ||||
|         fobos_rx_stop_sync(_this->openDev); | ||||
|  | ||||
|         // Stop the DDC | ||||
|         _this->ddc.stop(); | ||||
|  | ||||
|         // Close the device | ||||
|         fobos_rx_close(_this->openDev); | ||||
|  | ||||
|         flog::info("FobosSDRSourceModule '{0}': Stop!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             if (_this->port == PORT_RF) { | ||||
|                 double actual; // Dummy, don't care | ||||
|                 fobos_rx_set_frequency(_this->openDev, freq, &actual); | ||||
|             } | ||||
|             else { | ||||
|                 _this->ddc.setOffset(freq); | ||||
|             } | ||||
|         } | ||||
|         _this->freq = freq; | ||||
|         flog::info("FobosSDRSourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
|     } | ||||
|  | ||||
|     static void menuHandler(void* ctx) { | ||||
|         FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx; | ||||
|          | ||||
|         if (_this->running) { SmGui::BeginDisabled(); } | ||||
|  | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_fobossdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { | ||||
|             _this->select(_this->devices.key(_this->devId)); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             config.acquire(); | ||||
|             config.conf["device"] = _this->selectedSerial; | ||||
|             config.release(true); | ||||
|         } | ||||
|  | ||||
|         if (SmGui::Combo(CONCAT("##_fobossdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) { | ||||
|             _this->sampleRate = _this->samplerates.value(_this->srId); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             if (!_this->selectedSerial.empty()) { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->samplerates.key(_this->srId); | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SmGui::SameLine(); | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Button(CONCAT("Refresh##_fobossdr_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             _this->select(_this->selectedSerial); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|         } | ||||
|  | ||||
|         SmGui::LeftLabel("Antenna Port"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::Combo(CONCAT("##_fobossdr_port_", _this->name), &_this->portId, _this->ports.txt)) { | ||||
|             if (!_this->selectedSerial.empty()) { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedSerial]["port"] = _this->ports.key(_this->portId); | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|  | ||||
|         SmGui::LeftLabel("Clock Source"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::Combo(CONCAT("##_fobossdr_clk_", _this->name), &_this->clkSrcId, _this->clockSources.txt)) { | ||||
|             if (_this->running) { | ||||
|                 fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]); | ||||
|             } | ||||
|             if (!_this->selectedSerial.empty()) { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedSerial]["clkSrc"] = _this->clockSources.key(_this->clkSrcId); | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (_this->port == PORT_RF) { | ||||
|             SmGui::LeftLabel("LNA Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_fobossdr_lna_gain_", _this->name), &_this->lnaGain, FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX)) { | ||||
|                 if (_this->running) { | ||||
|                     fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain); | ||||
|                 } | ||||
|                 if (!_this->selectedSerial.empty()) { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerial]["lnaGain"] = _this->lnaGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             SmGui::LeftLabel("VGA Gain"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::SliderInt(CONCAT("##_fobossdr_vga_gain_", _this->name), &_this->vgaGain, FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX)) { | ||||
|                 if (_this->running) { | ||||
|                     fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain); | ||||
|                 } | ||||
|                 if (!_this->selectedSerial.empty()) { | ||||
|                     config.acquire(); | ||||
|                     config.conf["devices"][_this->selectedSerial]["vgaGain"] = _this->vgaGain; | ||||
|                     config.release(true); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void worker() { | ||||
|         // Select different processing depending on the mode | ||||
|         if (port == PORT_RF && sampleRate >= 50e6) { | ||||
|             while (run) { | ||||
|                 // Read samples | ||||
|                 unsigned int sampCount = 0; | ||||
|                 int err = fobos_rx_read_sync(openDev, (float*)ddc.out.writeBuf, &sampCount); | ||||
|                 if (err) { break; } | ||||
|                  | ||||
|                 // Send out samples to the core | ||||
|                 if (!ddc.out.swap(sampCount)) { break; } | ||||
|             } | ||||
|         } | ||||
|         else if (port == PORT_RF) { | ||||
|             while (run) { | ||||
|                 // Read samples | ||||
|                 unsigned int sampCount = 0; | ||||
|                 int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount); | ||||
|                 if (err) { break; } | ||||
|                  | ||||
|                 // Send samples to the DDC | ||||
|                 if (!ddcIn.swap(sampCount)) { break; } | ||||
|             } | ||||
|         } | ||||
|         else if (port == PORT_HF1) { | ||||
|             while (run) { | ||||
|                 // Read samples | ||||
|                 unsigned int sampCount = 0; | ||||
|                 int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount); | ||||
|                 if (err) { break; } | ||||
|  | ||||
|                 // Null out the HF2 samples | ||||
|                 for (int i = 0; i < sampCount; i++) { | ||||
|                     ddcIn.writeBuf[i].im = 0.0f; | ||||
|                 } | ||||
|                  | ||||
|                 // Send samples to the DDC | ||||
|                 if (!ddcIn.swap(sampCount)) { break; } | ||||
|             } | ||||
|         } | ||||
|         else if (port == PORT_HF2) { | ||||
|             while (run) { | ||||
|                 // Read samples | ||||
|                 unsigned int sampCount = 0; | ||||
|                 int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount); | ||||
|                 if (err) { break; } | ||||
|  | ||||
|                 // Null out the HF2 samples | ||||
|                 for (int i = 0; i < sampCount; i++) { | ||||
|                     ddcIn.writeBuf[i].re = 0.0f; | ||||
|                 } | ||||
|                  | ||||
|                 // Send samples to the DDC | ||||
|                 if (!ddcIn.swap(sampCount)) { break; } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|     double sampleRate; | ||||
|     SourceManager::SourceHandler handler; | ||||
|     bool running = false; | ||||
|     double freq; | ||||
|  | ||||
|     OptionList<std::string, int> devices; | ||||
|     OptionList<int, double> samplerates; | ||||
|     OptionList<std::string, Port> ports; | ||||
|     OptionList<std::string, int> clockSources; | ||||
|     int devId = 0; | ||||
|     int srId = 0; | ||||
|     int portId = 0; | ||||
|     int clkSrcId = 0; | ||||
|     Port port; | ||||
|     int lnaGain = 0; | ||||
|     int vgaGain = 0; | ||||
|     std::string selectedSerial; | ||||
|     int selectedDevId; | ||||
|  | ||||
|     fobos_dev_t* openDev; | ||||
|  | ||||
|     int bufferSize; | ||||
|     std::thread workerThread; | ||||
|     std::atomic<bool> run = false; | ||||
|  | ||||
|     dsp::stream<dsp::complex_t> ddcIn; | ||||
|     dsp::channel::RxVFO ddc; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|     json def = json({}); | ||||
|     def["devices"] = json({}); | ||||
|     def["device"] = ""; | ||||
|     config.setPath(core::args["root"].s() + "/fobossdr_config.json"); | ||||
|     config.load(def); | ||||
|     config.enableAutoSave(); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new FobosSDRSourceModule(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { | ||||
|     delete (FobosSDRSourceModule*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|     config.disableAutoSave(); | ||||
|     config.save(); | ||||
| } | ||||
| @@ -304,6 +304,7 @@ private: | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_hackrf_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { | ||||
|             _this->selectBySerial(_this->devList[_this->devId]); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             config.acquire(); | ||||
|             config.conf["device"] = _this->selectedSerial; | ||||
|             config.release(true); | ||||
|   | ||||
							
								
								
									
										17
									
								
								source_modules/harogic_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								source_modules/harogic_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(harogic_source) | ||||
|  | ||||
| file(GLOB SRC "src/*.cpp") | ||||
|  | ||||
| include(${SDRPP_MODULE_CMAKE}) | ||||
|  | ||||
| if (MSVC) | ||||
|     # Lib path | ||||
|     target_link_directories(harogic_source PRIVATE "C:/radio/HTRA_API/x64/htra_api/") | ||||
|     target_include_directories(harogic_source PRIVATE "C:/radio/HTRA_API/x64/htra_api/") | ||||
|     target_link_libraries(harogic_source PRIVATE htra_api) | ||||
| else (MSVC) | ||||
|     target_link_directories(harogic_source PRIVATE "/opt/htraapi/lib/${CMAKE_SYSTEM_PROCESSOR}/") | ||||
|     target_include_directories(harogic_source PRIVATE "/opt/htraapi/inc/") | ||||
|     target_link_libraries(harogic_source PRIVATE htraapi) | ||||
| endif () | ||||
							
								
								
									
										510
									
								
								source_modules/harogic_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										510
									
								
								source_modules/harogic_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,510 @@ | ||||
| #include <imgui.h> | ||||
| #include <module.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/smgui.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <core.h> | ||||
| #include <utils/optionlist.h> | ||||
| #include <htra_api.h> | ||||
| #include <atomic> | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
|     /* Name:            */ "harogic_source", | ||||
|     /* Description:     */ "harogic Source Module", | ||||
|     /* Author:          */ "Ryzerth", | ||||
|     /* Version:         */ 0, 1, 0, | ||||
|     /* Max instances    */ -1 | ||||
| }; | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| class HarogicSourceModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     HarogicSourceModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         sampleRate = 61440000.0; | ||||
|  | ||||
|         handler.ctx = this; | ||||
|         handler.selectHandler = menuSelected; | ||||
|         handler.deselectHandler = menuDeselected; | ||||
|         handler.menuHandler = menuHandler; | ||||
|         handler.startHandler = start; | ||||
|         handler.stopHandler = stop; | ||||
|         handler.tuneHandler = tune; | ||||
|         handler.stream = &stream; | ||||
|  | ||||
|         // Refresh devices | ||||
|         refresh(); | ||||
|  | ||||
|         // Select first (TODO: Select from config) | ||||
|         select(""); | ||||
|  | ||||
|         sigpath::sourceManager.registerSource("Harogic", &handler); | ||||
|     } | ||||
|  | ||||
|     ~HarogicSourceModule() { | ||||
|          | ||||
|     } | ||||
|  | ||||
|     void postInit() {} | ||||
|  | ||||
|     void enable() { | ||||
|         enabled = true; | ||||
|     } | ||||
|  | ||||
|     void disable() { | ||||
|         enabled = false; | ||||
|     } | ||||
|  | ||||
|     bool isEnabled() { | ||||
|         return enabled; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     void refresh() { | ||||
|         devices.clear(); | ||||
|          | ||||
|         // Set up the device parameters | ||||
|         BootProfile_TypeDef profile = {}; | ||||
|         profile.PhysicalInterface = PhysicalInterface_TypeDef::USB; | ||||
|         profile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortOnly; | ||||
|  | ||||
|         // Working variables | ||||
|         void* dev; | ||||
|         BootInfo_TypeDef binfo; | ||||
|          | ||||
|         for (int i = 0; i < 128; i++) { | ||||
|             // Attempt to open the device with the given ID | ||||
|             int ret = Device_Open(&dev, i, &profile, &binfo); | ||||
|             if (ret < 0) { break; } | ||||
|  | ||||
|             // Create serial string | ||||
|             char serial[64]; | ||||
|             sprintf(serial, "%" PRIX64, binfo.DeviceInfo.DeviceUID); | ||||
|              | ||||
|             // Add the device to the list | ||||
|             devices.define(serial, serial, i); | ||||
|  | ||||
|             // Close the device | ||||
|             Device_Close(&dev); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void select(const std::string& serial) { | ||||
|         // If there are no devices, give up | ||||
|         if (devices.empty()) { | ||||
|             selectedSerial.clear(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // If the serial was not found, select the first available serial | ||||
|         if (!devices.keyExists(serial)) { | ||||
|             select(devices.key(0)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Get the menu ID | ||||
|         devId = devices.keyId(serial); | ||||
|         selectedDevIndex = devices.value(devId); | ||||
|  | ||||
|         // Set up the device parameters | ||||
|         BootProfile_TypeDef bprofile = {}; | ||||
|         bprofile.PhysicalInterface = PhysicalInterface_TypeDef::USB; | ||||
|         bprofile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortOnly; | ||||
|  | ||||
|         // Working variables | ||||
|         BootInfo_TypeDef binfo; | ||||
|  | ||||
|         // Attempt to open the device by ID | ||||
|         void* dev; | ||||
|         int ret = Device_Open(&dev, selectedDevIndex, &bprofile, &binfo); | ||||
|         if (ret < 0) { | ||||
|             flog::error("Could not open device: {}", ret); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Get the default streaming parameters to query some info | ||||
|         IQS_Profile_TypeDef profile; | ||||
|         IQS_ProfileDeInit(&dev, &profile); | ||||
|          | ||||
|         // Compute all available samplerates | ||||
|         samplerates.clear(); | ||||
|         for (int i = 0; i < 8; i++) { | ||||
|             double sr = profile.NativeIQSampleRate_SPS / (double)(1 << i); | ||||
|             char buf[128]; | ||||
|             sprintf(buf, "%.02fMHz", sr / 1e6); | ||||
|             samplerates.define(1 << i, buf, sr); | ||||
|         } | ||||
|  | ||||
|         // Define RX ports | ||||
|         rxPorts.clear(); | ||||
|         rxPorts.define("external", "External", ExternalPort); | ||||
|         rxPorts.define("internal", "Internal", InternalPort); | ||||
|         rxPorts.define("ant", "ANT", ANT_Port); | ||||
|         rxPorts.define("tr", "T/R", TR_Port); | ||||
|         rxPorts.define("swr", "SWR", SWR_Port); | ||||
|         rxPorts.define("int", "INT", INT_Port); | ||||
|  | ||||
|         // Define gain strategies | ||||
|         gainStategies.clear(); | ||||
|         gainStategies.define("lowNoise", "Low Noise", LowNoisePreferred); | ||||
|         gainStategies.define("highLinearity", "High Linearity", HighLinearityPreferred); | ||||
|  | ||||
|         // Define preamplifier modes | ||||
|         preampModes.clear(); | ||||
|         preampModes.define("auto", "Auto", AutoOn); | ||||
|         preampModes.define("off", "Off", ForcedOff); | ||||
|  | ||||
|         // Define LO modes | ||||
|         loModes.clear(); | ||||
|         loModes.define("auto", "Auto", LOOpt_Auto); | ||||
|         loModes.define("speed", "Speed", LOOpt_Speed); | ||||
|         loModes.define("spurs", "Spurs", LOOpt_Spur); | ||||
|         loModes.define("phaseNoise", "Phase Noise", LOOpt_PhaseNoise); | ||||
|  | ||||
|         // Close the device | ||||
|         Device_Close(&dev); | ||||
|  | ||||
|         // TODO: Load configuration | ||||
|         sampleRate = samplerates.value(0); | ||||
|         refLvl = 0; | ||||
|         portId = rxPorts.valueId(ExternalPort); | ||||
|         gainStratId = gainStategies.valueId(LowNoisePreferred); | ||||
|         preampModeId = preampModes.valueId(AutoOn); | ||||
|         ifAgc = false; | ||||
|         loModeId = loModes.valueId(LOOpt_Auto); | ||||
|  | ||||
|         // Update the samplerate | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|  | ||||
|         // Save serial number | ||||
|         selectedSerial = serial; | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
|         HarogicSourceModule* _this = (HarogicSourceModule*)ctx; | ||||
|         core::setInputSampleRate(_this->sampleRate); | ||||
|         flog::info("HarogicSourceModule '{0}': Menu Select!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void menuDeselected(void* ctx) { | ||||
|         HarogicSourceModule* _this = (HarogicSourceModule*)ctx; | ||||
|         flog::info("HarogicSourceModule '{0}': Menu Deselect!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void start(void* ctx) { | ||||
|         HarogicSourceModule* _this = (HarogicSourceModule*)ctx; | ||||
|         if (_this->running) { return; } | ||||
|  | ||||
|         // Set up the device parameters | ||||
|         BootProfile_TypeDef bprofile = {}; | ||||
|         bprofile.PhysicalInterface = PhysicalInterface_TypeDef::USB; | ||||
|         bprofile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortAndPowerPort; | ||||
|  | ||||
|         // Working variables | ||||
|         BootInfo_TypeDef binfo; | ||||
|  | ||||
|         // Attempt to open the device by ID | ||||
|         int ret = Device_Open(&_this->openDev, _this->selectedDevIndex, &bprofile, &binfo); | ||||
|         if (ret < 0) { | ||||
|             flog::error("Could not open device: {}", ret); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Get the decimation amount | ||||
|         int dec = _this->samplerates.key(_this->samplerates.valueId(_this->sampleRate)); | ||||
|         flog::debug("Using decimation factor: {}", dec); | ||||
|  | ||||
|         // Decide to use either 8 or 16bit samples | ||||
|         _this->sampsInt8 = (_this->sampleRate > 64e6); | ||||
|  | ||||
|         // Get the default configuration | ||||
|         IQS_ProfileDeInit(&_this->openDev, &_this->profile); | ||||
|          | ||||
|         // Automatic configuration | ||||
|         _this->profile.Atten = -1; | ||||
|         _this->profile.BusTimeout_ms = 100; | ||||
|         _this->profile.TriggerSource = Bus;  | ||||
|         _this->profile.TriggerMode = Adaptive; | ||||
|         _this->profile.DataFormat = _this->sampsInt8 ? Complex8bit : Complex16bit; | ||||
|  | ||||
|         // User selectable config | ||||
|         _this->profile.CenterFreq_Hz = _this->freq; | ||||
|         _this->profile.RefLevel_dBm = _this->refLvl; | ||||
|         _this->profile.DecimateFactor = dec; | ||||
|         _this->profile.RxPort = _this->rxPorts.value(_this->portId); | ||||
|         _this->profile.GainStrategy = _this->gainStategies.value(_this->gainStratId); | ||||
|         _this->profile.Preamplifier = _this->preampModes.value(_this->preampModeId); | ||||
|         _this->profile.EnableIFAGC = _this->ifAgc; | ||||
|         _this->profile.LOOptimization = _this->loModes.value(_this->loModeId); | ||||
|  | ||||
|         // Apply the configuration | ||||
|         IQS_StreamInfo_TypeDef info; | ||||
|         ret = IQS_Configuration(&_this->openDev, &_this->profile, &_this->profile, &info); | ||||
|         if (ret < 0) { | ||||
|             flog::error("Could not configure device: {}", ret); | ||||
|             Device_Close(&_this->openDev); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Save the stream configuration | ||||
|         _this->bufferSize = info.PacketSamples; | ||||
|         flog::debug("Got buffer size: {}", _this->bufferSize); | ||||
|  | ||||
|         // Start the stream | ||||
|         ret = IQS_BusTriggerStart(&_this->openDev); | ||||
|         if (ret < 0) { | ||||
|             flog::error("Could not start stream: {}", ret); | ||||
|             Device_Close(&_this->openDev); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Start worker | ||||
|         _this->run = true; | ||||
|         _this->workerThread = std::thread(&HarogicSourceModule::worker, _this); | ||||
|          | ||||
|         _this->running = true; | ||||
|         flog::info("HarogicSourceModule '{0}': Start!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void stop(void* ctx) { | ||||
|         HarogicSourceModule* _this = (HarogicSourceModule*)ctx; | ||||
|         if (!_this->running) { return; } | ||||
|         _this->running = false; | ||||
|          | ||||
|         // Stop worker | ||||
|         _this->run = false; | ||||
|         _this->stream.stopWriter(); | ||||
|         if (_this->workerThread.joinable()) { _this->workerThread.join(); } | ||||
|         _this->stream.clearWriteStop(); | ||||
|  | ||||
|         // Stop the stream | ||||
|         IQS_BusTriggerStop(&_this->openDev); | ||||
|  | ||||
|         // Close the device | ||||
|         Device_Close(&_this->openDev); | ||||
|  | ||||
|         flog::info("HarogicSourceModule '{0}': Stop!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         HarogicSourceModule* _this = (HarogicSourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             // Update the frequency in the configuration | ||||
|             _this->profile.CenterFreq_Hz = freq; | ||||
|             _this->applyProfile(); | ||||
|         } | ||||
|         _this->freq = freq; | ||||
|         flog::info("HarogicSourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
|     } | ||||
|  | ||||
|     static void menuHandler(void* ctx) { | ||||
|         HarogicSourceModule* _this = (HarogicSourceModule*)ctx; | ||||
|          | ||||
|         if (_this->running) { SmGui::BeginDisabled(); } | ||||
|  | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_harogic_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { | ||||
|             _this->select(_this->devices.key(_this->devId)); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         if (SmGui::Combo(CONCAT("##_harogic_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) { | ||||
|             _this->sampleRate = _this->samplerates.value(_this->srId); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::SameLine(); | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Button(CONCAT("Refresh##_harogic_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             _this->select(_this->selectedSerial); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|         } | ||||
|  | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|  | ||||
|         SmGui::LeftLabel("RX Port"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::Combo(CONCAT("##_harogic_port_", _this->name), &_this->portId, _this->rxPorts.txt)) { | ||||
|             if (_this->running) { | ||||
|                 _this->profile.RxPort = _this->rxPorts.value(_this->portId); | ||||
|                 _this->applyProfile(); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::LeftLabel("LO Mode"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::Combo(CONCAT("##_lo_mode_", _this->name), &_this->loModeId, _this->loModes.txt)) { | ||||
|             if (_this->running) { | ||||
|                 _this->profile.LOOptimization = _this->loModes.value(_this->loModeId); | ||||
|                 _this->applyProfile(); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::LeftLabel("Gain Mode"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::Combo(CONCAT("##_harogic_gain_mode_", _this->name), &_this->gainStratId, _this->gainStategies.txt)) { | ||||
|             if (_this->running) { | ||||
|                 _this->profile.GainStrategy = _this->gainStategies.value(_this->gainStratId); | ||||
|                 _this->applyProfile(); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::LeftLabel("Preamp Mode"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::Combo(CONCAT("##_harogic_preamp_mode_", _this->name), &_this->preampModeId, _this->preampModes.txt)) { | ||||
|             if (_this->running) { | ||||
|                 _this->profile.Preamplifier = _this->preampModes.value(_this->preampModeId); | ||||
|                 _this->applyProfile(); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::LeftLabel("Reference"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::SliderInt(CONCAT("##_harogic_ref_", _this->name), &_this->refLvl, _this->minRef, _this->maxRef)) { | ||||
|             if (_this->running) { | ||||
|                 _this->profile.RefLevel_dBm = _this->refLvl; | ||||
|                 _this->applyProfile(); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         if (SmGui::Checkbox(CONCAT("IF AGC##_harogic_if_agc_", _this->name), &_this->ifAgc)) { | ||||
|             if (_this->running) { | ||||
|                 _this->profile.EnableIFAGC = _this->ifAgc; | ||||
|                 _this->applyProfile(); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void applyProfile() { | ||||
|         // Acquire device | ||||
|         std::lock_guard<std::mutex> lck(devMtx); | ||||
|  | ||||
|         // Configure the device | ||||
|         IQS_StreamInfo_TypeDef info; | ||||
|         int ret = IQS_Configuration(&openDev, &profile, &profile, &info); | ||||
|         if (ret < 0) { | ||||
|             flog::error("Failed to apply tuning config: {}", ret); | ||||
|         } | ||||
|  | ||||
|         // Re-trigger the stream | ||||
|         ret = IQS_BusTriggerStart(&openDev); | ||||
|         if (ret < 0) { | ||||
|             flog::error("Could not start stream: {}", ret); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void worker() { | ||||
|         // Allocate sample buffer | ||||
|         int realSamps = bufferSize*2; | ||||
|         IQStream_TypeDef iqs; | ||||
|  | ||||
|         // Define number of buffers per swap to maintain 200 fps | ||||
|         int maxBufCount = STREAM_BUFFER_SIZE / bufferSize; | ||||
|         int bufCount = (sampleRate / bufferSize) / 200; | ||||
|         if (bufCount <= 0) { bufCount = 1; } | ||||
|         if (bufCount > maxBufCount) { bufCount = maxBufCount; } | ||||
|         int count = 0; | ||||
|  | ||||
|         flog::debug("Swapping will be done {} buffers at a time", bufCount); | ||||
|  | ||||
|         // Worker loop | ||||
|         while (run) { | ||||
|             // Read samples | ||||
|             devMtx.lock(); | ||||
|             int ret = IQS_GetIQStream_PM1(&openDev, &iqs); | ||||
|             devMtx.unlock(); | ||||
|             if (ret < 0) { | ||||
|                 if (ret == APIRETVAL_WARNING_BusTimeOut) { | ||||
|                     flog::warn("Stream timed out"); | ||||
|                     continue; | ||||
|                 } | ||||
|                 else if (ret <= APIRETVAL_WARNING_IFOverflow && ret >= APIRETVAL_WARNING_ADCConfigError) { | ||||
|                     // Just warnings, do nothing | ||||
|                 } | ||||
|                 else { | ||||
|                     flog::error("Streaming error: {}", ret); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Convert them to floating point | ||||
|             if (sampsInt8) { | ||||
|                 volk_8i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*bufferSize], (int8_t*)iqs.AlternIQStream, 128.0f, realSamps); | ||||
|             } | ||||
|             else { | ||||
|                 volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*bufferSize], (int16_t*)iqs.AlternIQStream, 32768.0f, realSamps); | ||||
|             } | ||||
|  | ||||
|             // Send them off if we have enough | ||||
|             if (count >= bufCount) { | ||||
|                 count = 0; | ||||
|                 if (!stream.swap(bufferSize*bufCount)) { break; } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
|     double sampleRate; | ||||
|     SourceManager::SourceHandler handler; | ||||
|     bool running = false; | ||||
|     double freq; | ||||
|  | ||||
|     OptionList<std::string, int> devices; | ||||
|     OptionList<int, double> samplerates; | ||||
|     OptionList<std::string, RxPort_TypeDef> rxPorts; | ||||
|     OptionList<std::string, GainStrategy_TypeDef> gainStategies; | ||||
|     OptionList<std::string, PreamplifierState_TypeDef> preampModes; | ||||
|     OptionList<std::string, LOOptimization_TypeDef> loModes; | ||||
|     int devId = 0; | ||||
|     int srId = 0; | ||||
|     int refLvl = -30; | ||||
|     int minRef = -100; | ||||
|     int maxRef = 7; | ||||
|     int portId = 0; | ||||
|     int gainStratId = 0; | ||||
|     int preampModeId = 0; | ||||
|     int loModeId = 0; | ||||
|     bool ifAgc = false; | ||||
|     std::string selectedSerial; | ||||
|     int selectedDevIndex; | ||||
|  | ||||
|     void* openDev; | ||||
|     IQS_Profile_TypeDef profile; | ||||
|  | ||||
|     int bufferSize; | ||||
|     std::thread workerThread; | ||||
|     std::atomic<bool> run = false; | ||||
|     std::mutex devMtx; | ||||
|     bool sampsInt8; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|     // Nothing here | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new HarogicSourceModule(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { | ||||
|     delete (HarogicSourceModule*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|     // Nothing here | ||||
| } | ||||
| @@ -118,7 +118,6 @@ private: | ||||
|  | ||||
|         // Update host samplerate | ||||
|         sampleRate = samplerates.key(srId); | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
| @@ -199,6 +198,7 @@ private: | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_hermes_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { | ||||
|             _this->selectMac(_this->devices.key(_this->devId)); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             if (!_this->selectedMac.empty()) { | ||||
|                 config.acquire(); | ||||
|                 config.conf["device"] = _this->devices.key(_this->devId); | ||||
| @@ -225,6 +225,7 @@ private: | ||||
|             std::string mac = config.conf["device"]; | ||||
|             config.release(); | ||||
|             _this->selectMac(mac); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|         } | ||||
|  | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|   | ||||
| @@ -210,7 +210,6 @@ public: | ||||
|  | ||||
|         // Update samplerate | ||||
|         sampleRate = srList[srId]; | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|  | ||||
|         // Close device | ||||
|         perseus_close(dev); | ||||
| @@ -329,6 +328,7 @@ private: | ||||
|         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); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             config.acquire(); | ||||
|             config.conf["device"] = serial; | ||||
|             config.release(true); | ||||
|   | ||||
| @@ -244,9 +244,6 @@ private: | ||||
|             bwId = 0; | ||||
|             bandwidth = bandwidths.value(bwId); | ||||
|         } | ||||
|          | ||||
|         // Update core samplerate | ||||
|         core::setInputSampleRate(samplerate); | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
| @@ -351,6 +348,7 @@ private: | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo("##plutosdr_dev_sel", &_this->devId, _this->devices.txt)) { | ||||
|             _this->select(_this->devices.key(_this->devId)); | ||||
|             core::setInputSampleRate(_this->samplerate); | ||||
|             config.acquire(); | ||||
|             config.conf["device"] = _this->devices.key(_this->devId); | ||||
|             config.release(true); | ||||
| @@ -373,7 +371,7 @@ private: | ||||
|         if (SmGui::Button(CONCAT("Refresh##_pluto_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             _this->select(_this->devDesc); | ||||
|  | ||||
|             core::setInputSampleRate(_this->samplerate); | ||||
|         } | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|  | ||||
|   | ||||
							
								
								
									
										21
									
								
								source_modules/rfnm_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								source_modules/rfnm_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(rfnm_source) | ||||
|  | ||||
| file(GLOB SRC "src/*.cpp") | ||||
|  | ||||
| include(${SDRPP_MODULE_CMAKE}) | ||||
|  | ||||
| if (MSVC) | ||||
|     # Lib path | ||||
|     target_link_directories(rfnm_source PRIVATE "C:/Program Files/RFNM/lib/") | ||||
|     target_include_directories(rfnm_source PUBLIC "C:/Program Files/RFNM/include/") | ||||
|     target_link_libraries(rfnm_source PRIVATE rfnm) | ||||
| else (MSVC) | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|     pkg_check_modules(LIBRFNM REQUIRED librfnm) | ||||
|  | ||||
|     target_include_directories(rfnm_source PRIVATE ${LIBRFNM_INCLUDE_DIRS}) | ||||
|     target_link_directories(rfnm_source PRIVATE ${LIBRFNM_LIBRARY_DIRS}) | ||||
|     target_link_libraries(rfnm_source PRIVATE ${LIBRFNM_LIBRARIES}) | ||||
| endif () | ||||
							
								
								
									
										512
									
								
								source_modules/rfnm_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										512
									
								
								source_modules/rfnm_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,512 @@ | ||||
| #include <imgui.h> | ||||
| #include <module.h> | ||||
| #include <gui/gui.h> | ||||
| #include <gui/smgui.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <librfnm/librfnm.h> | ||||
| #include <core.h> | ||||
| #include <utils/optionlist.h> | ||||
| #include <atomic> | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
|     /* Name:            */ "rfnm_source", | ||||
|     /* Description:     */ "RFNM Source Module", | ||||
|     /* Author:          */ "Ryzerth", | ||||
|     /* Version:         */ 0, 1, 0, | ||||
|     /* Max instances    */ -1 | ||||
| }; | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| class RFNMSourceModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     RFNMSourceModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         sampleRate = 61440000.0; | ||||
|  | ||||
|         handler.ctx = this; | ||||
|         handler.selectHandler = menuSelected; | ||||
|         handler.deselectHandler = menuDeselected; | ||||
|         handler.menuHandler = menuHandler; | ||||
|         handler.startHandler = start; | ||||
|         handler.stopHandler = stop; | ||||
|         handler.tuneHandler = tune; | ||||
|         handler.stream = &stream; | ||||
|  | ||||
|         // Refresh devices | ||||
|         refresh(); | ||||
|  | ||||
|         // Select first (TODO: Select from config) | ||||
|         select(""); | ||||
|  | ||||
|         sigpath::sourceManager.registerSource("RFNM", &handler); | ||||
|     } | ||||
|  | ||||
|     ~RFNMSourceModule() { | ||||
|          | ||||
|     } | ||||
|  | ||||
|     void postInit() {} | ||||
|  | ||||
|     void enable() { | ||||
|         enabled = true; | ||||
|     } | ||||
|  | ||||
|     void disable() { | ||||
|         enabled = false; | ||||
|     } | ||||
|  | ||||
|     bool isEnabled() { | ||||
|         return enabled; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     void refresh() { | ||||
|         devices.clear(); | ||||
|         auto list = librfnm::find(librfnm_transport::LIBRFNM_TRANSPORT_USB); | ||||
|         for (const auto& info : list) { | ||||
|             // Format device name | ||||
|             std::string devName = "RFNM "; | ||||
|             devName += info.motherboard.user_readable_name; | ||||
|             devName += " ["; | ||||
|             devName += (char*)info.motherboard.serial_number; | ||||
|             devName += ']'; | ||||
|  | ||||
|             // Save device | ||||
|             devices.define((char*)info.motherboard.serial_number, devName, (char*)info.motherboard.serial_number); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void select(const std::string& serial) { | ||||
|         // If there are no devices, give up | ||||
|         if (devices.empty()) { | ||||
|             selectedSerial.clear(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // If the serial was not found, select the first available serial | ||||
|         if (!devices.keyExists(serial)) { | ||||
|             select(devices.key(0)); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Open the device | ||||
|         librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial); | ||||
|  | ||||
|         // Define samplerates | ||||
|         samplerates.clear(); | ||||
|         samplerates.define(61440000, "61.44 MHz", 2); | ||||
|         samplerates.define(122880000, "122.88 MHz", 1); | ||||
|  | ||||
|         // Define daughterboards | ||||
|         daughterboards.clear(); | ||||
|         for (int i = 0; i < 2; i++) { | ||||
|             // If not present, skip | ||||
|             if (!dev->s->hwinfo.daughterboard[i].board_id) { continue; } | ||||
|  | ||||
|             // Format the daughterboard name | ||||
|             std::string name = (i ? "[SEC] " : "[PRI] ") + std::string(dev->s->hwinfo.daughterboard[i].user_readable_name); | ||||
|  | ||||
|             // Add the daughterboard to the list | ||||
|             daughterboards.define(name, name, i); | ||||
|         } | ||||
|  | ||||
|         // Load options (TODO) | ||||
|         srId = samplerates.keyId(61440000); | ||||
|         dgbId = 0; | ||||
|  | ||||
|         // Select the daughterboard | ||||
|         selectDaughterboard(dev, 0); | ||||
|  | ||||
|         // Update samplerate | ||||
|         sampleRate = samplerates.key(srId); | ||||
|  | ||||
|         // Close device | ||||
|         delete dev; | ||||
|  | ||||
|         // Save serial number | ||||
|         selectedSerial = serial; | ||||
|     } | ||||
|  | ||||
|     struct PathConfig { | ||||
|         rfnm_rf_path path; | ||||
|         int chId; | ||||
|         uint16_t appliesCh; | ||||
|  | ||||
|         bool operator==(const PathConfig& b) const { | ||||
|             return b.path == path; | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     void selectDaughterboard(librfnm* dev, int id) { | ||||
|         // If no daugherboard is populated, give up | ||||
|         if (!dev->s->hwinfo.daughterboard[0].board_id && !dev->s->hwinfo.daughterboard[1].board_id) { | ||||
|             flog::error("The selected device has no daughterboards"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // If the ID is not populated, select the other one | ||||
|         if (id >= 2 || !dev->s->hwinfo.daughterboard[id].board_id) { | ||||
|             selectDaughterboard(dev, 1 - id); | ||||
|         } | ||||
|  | ||||
|         // Compute the channel offset | ||||
|         int offset = 0; | ||||
|         for (int i = 0; i < id; i++) { | ||||
|             offset += dev->s->hwinfo.daughterboard[i].rx_ch_cnt; | ||||
|         } | ||||
|  | ||||
|         // Define antenna paths by going through all channels | ||||
|         paths.clear(); | ||||
|         int count = dev->s->hwinfo.daughterboard[id].rx_ch_cnt; | ||||
|         for (int i = 0; i < count; i++) { | ||||
|             // Go through each possible path | ||||
|             for (int j = 0; j < 10; j++) { | ||||
|                 // If it's the null path, stop searching | ||||
|                 rfnm_rf_path path = dev->s->rx.ch[offset + i].path_possible[j]; | ||||
|                 if (path == RFNM_PATH_NULL) { continue; } | ||||
|  | ||||
|                 // Get the path | ||||
|                 PathConfig pc = { path, offset + i, (uint16_t)(1 << (offset + i + 8))}; | ||||
|                  | ||||
|                 // If it's not in the list, add it | ||||
|                 if (!paths.valueExists(pc)) { | ||||
|                     std::string name = librfnm::rf_path_to_string(pc.path); | ||||
|                     std::string capName = name; | ||||
|                     if (std::islower(capName[0])) { capName[0] = std::toupper(capName[0]); } | ||||
|                     paths.define(name, capName, pc); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             // Get the preferred path | ||||
|             PathConfig preferred_pc = { dev->s->rx.ch[offset + i].path_preferred, 0, 0 }; | ||||
|  | ||||
|             // Make sure the path is accessible or give up | ||||
|             if (!paths.valueExists(preferred_pc)) { continue; } | ||||
|              | ||||
|             // Set this channel as the channel of its prefered path (cursed af but lazy) | ||||
|             const PathConfig& pc = paths.value(paths.valueId(preferred_pc)); | ||||
|             ((PathConfig*)&pc)->chId = offset + i; | ||||
|             ((PathConfig*)&pc)->appliesCh = (uint16_t)(1 << (offset + i + 8)); | ||||
|         } | ||||
|  | ||||
|         // Dump antenna paths | ||||
|         for (int i = 0; i < paths.size(); i++) { | ||||
|             flog::debug("PATH[{}]: Name={}, Ch={}, Path={}", i, paths.name(i), paths.value(i).chId, (int)paths.value(i).path); | ||||
|         } | ||||
|  | ||||
|         // Load configuration (TODO) | ||||
|         selectedPath = paths.key(0); | ||||
|  | ||||
|         // Select antenna path | ||||
|         selectPath(dev, id, selectedPath); | ||||
|  | ||||
|         // Save selected daughterboard | ||||
|         dgbId = id; | ||||
|     } | ||||
|  | ||||
|     void selectPath(librfnm* dev, int dgbId, const std::string& path) { | ||||
|         // If the path doesn't exist, select the first path | ||||
|         if (!paths.keyExists(path)) { | ||||
|             selectPath(dev, dgbId, paths.key(0)); | ||||
|         } | ||||
|  | ||||
|         // Save selected path | ||||
|         selectedPath = path; | ||||
|         pathId = paths.keyId(path); | ||||
|         currentPath = paths.value(pathId); | ||||
|  | ||||
|         // Define bandwidths | ||||
|         bandwidths.clear(); | ||||
|         bandwidths.define(-1, "Auto", -1); | ||||
|         for (int i = 1; i <= 100; i++) { | ||||
|             char buf[128]; | ||||
|             sprintf(buf, "%d MHz", i); | ||||
|             bandwidths.define(i, buf, i); | ||||
|         } | ||||
|  | ||||
|         // Get gain range | ||||
|         gainMin = dev->s->rx.ch[currentPath.chId].gain_range.min; | ||||
|         gainMax = dev->s->rx.ch[currentPath.chId].gain_range.max; | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
|         RFNMSourceModule* _this = (RFNMSourceModule*)ctx; | ||||
|         core::setInputSampleRate(_this->sampleRate); | ||||
|         flog::info("RFNMSourceModule '{0}': Menu Select!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void menuDeselected(void* ctx) { | ||||
|         RFNMSourceModule* _this = (RFNMSourceModule*)ctx; | ||||
|         flog::info("RFNMSourceModule '{0}': Menu Deselect!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void start(void* ctx) { | ||||
|         RFNMSourceModule* _this = (RFNMSourceModule*)ctx; | ||||
|         if (_this->running) { return; } | ||||
|  | ||||
|         // Open the device | ||||
|         _this->openDev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial); | ||||
|  | ||||
|         // Configure the stream | ||||
|         _this->bufferSize = -1; | ||||
|         _this->openDev->rx_stream(librfnm_stream_format::LIBRFNM_STREAM_FORMAT_CS16, &_this->bufferSize); | ||||
|         if (_this->bufferSize <= 0) { | ||||
|             flog::error("Failed to configure stream"); | ||||
|         } | ||||
|  | ||||
|         // Allocate and queue buffers | ||||
|         flog::debug("BUFFER SIZE: {}", _this->bufferSize); | ||||
|         for (int i = 0; i < LIBRFNM_MIN_RX_BUFCNT; i++) { | ||||
|             _this->rxBuf[i].buf = dsp::buffer::alloc<uint8_t>(_this->bufferSize); | ||||
|             _this->openDev->rx_qbuf(&_this->rxBuf[i]); | ||||
|         } | ||||
|  | ||||
|         // Flush buffers | ||||
|         _this->openDev->rx_flush(); | ||||
|  | ||||
|         // Configure the device | ||||
|         _this->openDev->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_ON; | ||||
|         _this->openDev->s->rx.ch[_this->currentPath.chId].samp_freq_div_n = _this->samplerates[_this->srId]; | ||||
|         _this->openDev->s->rx.ch[_this->currentPath.chId].freq = _this->freq; | ||||
|         _this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain; | ||||
|         _this->openDev->s->rx.ch[_this->currentPath.chId].rfic_lpf_bw = 100; | ||||
|         _this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF; | ||||
|         _this->openDev->s->rx.ch[_this->currentPath.chId].path = _this->currentPath.path; | ||||
|         rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh); | ||||
|         if (fail != rfnm_api_failcode::RFNM_API_OK) { | ||||
|             flog::error("Failed to configure device: {}", (int)fail); | ||||
|         } | ||||
|  | ||||
|         // Start worker | ||||
|         _this->run = true; | ||||
|         _this->workerThread = std::thread(&RFNMSourceModule::worker, _this); | ||||
|          | ||||
|         _this->running = true; | ||||
|         flog::info("RFNMSourceModule '{0}': Start!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void stop(void* ctx) { | ||||
|         RFNMSourceModule* _this = (RFNMSourceModule*)ctx; | ||||
|         if (!_this->running) { return; } | ||||
|         _this->running = false; | ||||
|          | ||||
|         // Stop worker | ||||
|         _this->run = false; | ||||
|         _this->stream.stopWriter(); | ||||
|         if (_this->workerThread.joinable()) { _this->workerThread.join(); } | ||||
|         _this->stream.clearWriteStop(); | ||||
|  | ||||
|         // Stop the RX streaming | ||||
|         _this->openDev->rx_stream_stop(); | ||||
|  | ||||
|         // Disable channel | ||||
|         _this->openDev->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_OFF; | ||||
|         _this->openDev->set(_this->currentPath.appliesCh); | ||||
|  | ||||
|         // Flush buffers | ||||
|         _this->openDev->rx_flush(); | ||||
|  | ||||
|         // Close device | ||||
|         delete _this->openDev; | ||||
|  | ||||
|         // Free buffers | ||||
|         for (int i = 0; i < LIBRFNM_MIN_RX_BUFCNT; i++) { | ||||
|             dsp::buffer::free(_this->rxBuf[i].buf); | ||||
|         } | ||||
|  | ||||
|         flog::info("RFNMSourceModule '{0}': Stop!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         RFNMSourceModule* _this = (RFNMSourceModule*)ctx; | ||||
|         if (_this->running) { | ||||
|             _this->openDev->s->rx.ch[_this->currentPath.chId].freq = freq; | ||||
|             rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh); | ||||
|             if (fail != rfnm_api_failcode::RFNM_API_OK) { | ||||
|                 flog::error("Failed to tune: {}", (int)fail); | ||||
|             } | ||||
|         } | ||||
|         _this->freq = freq; | ||||
|         flog::info("RFNMSourceModule '{0}': Tune: {1}!", _this->name, freq); | ||||
|     } | ||||
|  | ||||
|     static void menuHandler(void* ctx) { | ||||
|         RFNMSourceModule* _this = (RFNMSourceModule*)ctx; | ||||
|          | ||||
|         if (_this->running) { SmGui::BeginDisabled(); } | ||||
|  | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_rfnm_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { | ||||
|             _this->select(_this->devices.key(_this->devId)); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         if (SmGui::Combo(CONCAT("##_rfnm_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) { | ||||
|             _this->sampleRate = _this->samplerates.key(_this->srId); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::SameLine(); | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Button(CONCAT("Refresh##_rfnm_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             _this->select(_this->selectedSerial); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|         } | ||||
|  | ||||
|         if (_this->daughterboards.size() > 1) { | ||||
|             SmGui::LeftLabel("Daughterboard"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::Combo(CONCAT("##_rfnm_dgb_sel_", _this->name), &_this->dgbId, _this->daughterboards.txt)) { | ||||
|                 // Open the device | ||||
|                 librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial); | ||||
|  | ||||
|                 // Select the daughterboard | ||||
|                 _this->selectDaughterboard(dev, _this->dgbId); | ||||
|  | ||||
|                 // Close device | ||||
|                 delete dev; | ||||
|  | ||||
|                 // TODO: Save | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         if (_this->paths.size() > 1) { | ||||
|             SmGui::LeftLabel("Antenna Path"); | ||||
|             SmGui::FillWidth(); | ||||
|             if (SmGui::Combo(CONCAT("##_rfnm_path_sel_", _this->name), &_this->pathId, _this->paths.txt)) { | ||||
|                 // Open the device | ||||
|                 librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial); | ||||
|  | ||||
|                 // Select the atennna path | ||||
|                 _this->selectPath(dev, _this->dgbId, _this->paths.key(_this->pathId)); | ||||
|  | ||||
|                 // Close device | ||||
|                 delete dev; | ||||
|  | ||||
|                 // TODO: Save | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|  | ||||
|         SmGui::LeftLabel("Bandwidth"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::Combo(CONCAT("##_rfnm_bw_sel_", _this->name), &_this->bwId, _this->bandwidths.txt)) { | ||||
|             if (_this->running) { | ||||
|                 // TODO: Set | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         SmGui::LeftLabel("Gain"); | ||||
|         SmGui::FillWidth(); | ||||
|         if (SmGui::SliderInt(CONCAT("##_rfnm_gain_", _this->name), &_this->gain, _this->gainMin, _this->gainMax)) { | ||||
|             if (_this->running) { | ||||
|                 _this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain; | ||||
|                 rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|  | ||||
|         if (SmGui::Checkbox(CONCAT("FM Notch##_rfnm_", _this->name), &_this->fmNotch)) { | ||||
|             if (_this->running) { | ||||
|                 _this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF; | ||||
|                 rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh); | ||||
|             } | ||||
|             // TODO: Save | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void worker() { | ||||
|         librfnm_rx_buf* lrxbuf; | ||||
|         int sampCount = bufferSize/4; | ||||
|         uint8_t ch = (1 << currentPath.chId); | ||||
|  | ||||
|         // Define number of buffers per swap to maintain 200 fps | ||||
|         int maxBufCount = STREAM_BUFFER_SIZE / sampCount; | ||||
|         int bufCount = (sampleRate / sampCount) / 200; | ||||
|         if (bufCount <= 0) { bufCount = 1; } | ||||
|         if (bufCount > maxBufCount) { bufCount = maxBufCount; } | ||||
|  | ||||
|         int count = 0; | ||||
|         while (run) { | ||||
|             // Receive a buffer | ||||
|             auto fail = openDev->rx_dqbuf(&lrxbuf, ch, 1000); | ||||
|             if (fail == rfnm_api_failcode::RFNM_API_DQBUF_NO_DATA) { | ||||
|                 flog::error("Dequeue buffer didn't have any data"); | ||||
|                 continue; | ||||
|             } | ||||
|             else if (fail) { break; } | ||||
|  | ||||
|             // Convert buffer to CF32 | ||||
|             volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*sampCount], (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2); | ||||
|  | ||||
|             // Reque buffer | ||||
|             openDev->rx_qbuf(lrxbuf); | ||||
|  | ||||
|             // Swap data | ||||
|             if (count >= bufCount) { | ||||
|                 if (!stream.swap(count*sampCount)) { break; } | ||||
|                 count = 0; | ||||
|             } | ||||
|              | ||||
|         } | ||||
|  | ||||
|         flog::debug("Worker exiting"); | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
|     double sampleRate; | ||||
|     SourceManager::SourceHandler handler; | ||||
|     bool running = false; | ||||
|     double freq; | ||||
|  | ||||
|     OptionList<std::string, std::string> devices; | ||||
|     OptionList<std::string, int> daughterboards; | ||||
|     OptionList<std::string, PathConfig> paths; | ||||
|     OptionList<int, int> bandwidths; | ||||
|     OptionList<int, int> samplerates; | ||||
|     int gainMin = 0; | ||||
|     int gainMax = 0; | ||||
|     int devId = 0; | ||||
|     int dgbId = 0; | ||||
|     int pathId = 0; | ||||
|     int srId = 0; | ||||
|     int bwId = 0; | ||||
|     int gain = 0; | ||||
|     bool fmNotch = false; | ||||
|     std::string selectedSerial; | ||||
|     librfnm* openDev; | ||||
|     int bufferSize = -1; | ||||
|     std::string selectedPath; | ||||
|     PathConfig currentPath; | ||||
|     librfnm_rx_buf rxBuf[LIBRFNM_MIN_RX_BUFCNT]; | ||||
|  | ||||
|     std::atomic<bool> run = false; | ||||
|     std::thread workerThread; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|     // Nothing here | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new RFNMSourceModule(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(void* instance) { | ||||
|     delete (RFNMSourceModule*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|     // Nothing here | ||||
| } | ||||
| @@ -411,8 +411,6 @@ public: | ||||
|             agcSetPoint = config.conf["devices"][selectedName]["agcSetPoint"]; | ||||
|         } | ||||
|  | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|  | ||||
|         // Per device options | ||||
|         if (openDev.hwVer == SDRPLAY_RSP1_ID) { | ||||
|             // No config to load | ||||
| @@ -688,6 +686,7 @@ private: | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##sdrplay_dev", _this->name), &_this->devId, _this->devListTxt.c_str())) { | ||||
|             _this->selectById(_this->devId); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             config.acquire(); | ||||
|             config.conf["device"] = _this->devNameList[_this->devId]; | ||||
|             config.release(true); | ||||
| @@ -711,6 +710,7 @@ private: | ||||
|             if (SmGui::Button(CONCAT("Refresh##sdrplay_refresh", _this->name))) { | ||||
|                 _this->refresh(); | ||||
|                 _this->selectByName(_this->selectedName); | ||||
|                 core::setInputSampleRate(_this->sampleRate); | ||||
|             } | ||||
|  | ||||
|             SmGui::LeftLabel("Bandwidth"); | ||||
|   | ||||
| @@ -9,8 +9,13 @@ | ||||
| #include <gui/smgui.h> | ||||
| #include <utils/optionlist.h> | ||||
| #include <codecvt> | ||||
| #include <locale> | ||||
| #include <aaroniartsaapi.h> | ||||
|  | ||||
| #ifndef _WIN32 | ||||
| #include <unistd.h> | ||||
| #endif | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
| @@ -28,10 +33,13 @@ public: | ||||
|     SpectranSourceModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         if (AARTSAAPI_Init(AARTSAAPI_MEMORY_MEDIUM) != AARTSAAPI_OK) { | ||||
|         AARTSAAPI_Result res; | ||||
|         if ((res = AARTSAAPI_Init(AARTSAAPI_MEMORY_MEDIUM)) != AARTSAAPI_OK) { | ||||
|             flog::error("Failed to initialize the RTSAAPI: {}", (uint32_t)res); | ||||
|             return; | ||||
|         } | ||||
|         if (AARTSAAPI_Open(&api) != AARTSAAPI_OK) { | ||||
|         if ((res = AARTSAAPI_Open(&api)) != AARTSAAPI_OK) { | ||||
|             flog::error("Failed to open the RTSAAPI: {}", (uint32_t)res); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -189,7 +197,6 @@ public: | ||||
|  | ||||
|         // Set samplerate | ||||
|         samplerate = sampleRateList.value(srId); | ||||
|         core::setInputSampleRate(samplerate.effective); | ||||
|  | ||||
|         // Close device | ||||
|         AARTSAAPI_CloseDevice(&api, &dev); | ||||
| @@ -329,9 +336,10 @@ private: | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_spectran_dev_", _this->name), &_this->devId, _this->devList.txt)) { | ||||
|              | ||||
|             _this->selectSerial(_this->devList.key(_this->devId)); | ||||
|             core::setInputSampleRate(_this->samplerate.effective); | ||||
|         } | ||||
|         // TODO: SR sel | ||||
|          | ||||
|         if (SmGui::Combo(CONCAT("##_spectran_sr_", _this->name), &_this->srId, _this->sampleRateList.txt)) { | ||||
|             _this->samplerate = _this->sampleRateList.value(_this->srId); | ||||
|             core::setInputSampleRate(_this->samplerate.effective); | ||||
| @@ -447,13 +455,16 @@ private: | ||||
|  | ||||
|     void updateRef() { | ||||
|         // Get and update bounds | ||||
|         AARTSAAPI_Config config; | ||||
|         AARTSAAPI_ConfigInfo refInfo; | ||||
|         AARTSAAPI_ConfigFind(&dev, &croot, &config, L"main/reflevel"); | ||||
|         AARTSAAPI_ConfigGetInfo(&dev, &config, &refInfo); | ||||
|         AARTSAAPI_Config config = {}; | ||||
|         AARTSAAPI_ConfigInfo refInfo = {}; | ||||
|         auto res = AARTSAAPI_ConfigFind(&dev, &croot, &config, L"main/reflevel"); | ||||
|         flog::debug("Res A: {}", res); | ||||
|         res = AARTSAAPI_ConfigGetInfo(&dev, &config, &refInfo); | ||||
|         flog::debug("Res B: {}", res); | ||||
|         minRef = refInfo.minValue; | ||||
|         maxRef = refInfo.maxValue; | ||||
|         refStep = refInfo.stepValue; | ||||
|         flog::debug("Gain: {} -> {}", refInfo.minValue, refInfo.maxValue); | ||||
|         refLevel = std::clamp<float>(refLevel, minRef, maxRef); | ||||
|  | ||||
|         // Apply new ref level | ||||
| @@ -547,4 +558,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { | ||||
| MOD_EXPORT void _END_() { | ||||
|     config.disableAutoSave(); | ||||
|     config.save(); | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -211,7 +211,6 @@ public: | ||||
|  | ||||
|         // Apply samplerate | ||||
|         sampleRate = samplerates.key(srId); | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|     } | ||||
|  | ||||
|     void setBandwidth(double bw) { | ||||
| @@ -335,6 +334,7 @@ private: | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_usrp_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { | ||||
|             _this->select(_this->devices.key(_this->devId)); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             if (!_this->selectedSer.empty()) { | ||||
|                 config.acquire(); | ||||
|                 config.conf["device"] = _this->devices.key(_this->devId); | ||||
| @@ -357,10 +357,8 @@ private: | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Button(CONCAT("Refresh##_usrp_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             config.acquire(); | ||||
|             std::string ser = config.conf["device"]; | ||||
|             config.release(); | ||||
|             _this->select(ser); | ||||
|             _this->select(_this->selectedSer); | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|         } | ||||
|  | ||||
|         if (_this->channels.size() > 1) { | ||||
|   | ||||
| @@ -1,5 +1,4 @@ | ||||
| #include <core.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| int main(int argc, char* argv[]) { | ||||
|     return sdrpp_main(argc, argv); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user