mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 02:39:11 +01:00 
			
		
		
		
	Compare commits
	
		
			77 Commits
		
	
	
		
			debug
			...
			new_source
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					6891d0bb0f | ||
| 
						 | 
					b835d07573 | ||
| 
						 | 
					69161253e8 | ||
| 
						 | 
					5ab3428b90 | ||
| 
						 | 
					7f002f6276 | ||
| 
						 | 
					a728403a3f | ||
| 
						 | 
					0f1d2da3b7 | ||
| 
						 | 
					6d0b65c27f | ||
| 
						 | 
					f640cdcb6a | ||
| 
						 | 
					80a90e13d9 | ||
| 
						 | 
					3982db73d3 | ||
| 
						 | 
					bd64f07a20 | ||
| 
						 | 
					c9950d9331 | ||
| 
						 | 
					9bc609f4e4 | ||
| 
						 | 
					bcc8e20e66 | ||
| 
						 | 
					b07e828fed | ||
| 
						 | 
					bd24a4a5eb | ||
| 
						 | 
					fe4a7b32a7 | ||
| 
						 | 
					0e1ab29b5d | ||
| 
						 | 
					fbbafddd3d | ||
| 
						 | 
					1cbc8ec6f5 | ||
| 
						 | 
					9f65e3ec71 | ||
| 
						 | 
					08f3a7d201 | ||
| 
						 | 
					9ce62f8885 | ||
| 
						 | 
					caeaa2d46c | ||
| 
						 | 
					7ae030a3a6 | ||
| 
						 | 
					1b27379a3d | ||
| 
						 | 
					e52123038e | ||
| 
						 | 
					ec8c60111d | ||
| 
						 | 
					f61799cf5f | ||
| 
						 | 
					17eccf5156 | ||
| 
						 | 
					e835c8dd9a | ||
| 
						 | 
					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 | ||
| 
						 | 
					309717b5f8 | 
							
								
								
									
										41
									
								
								.github/workflows/build_all.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										41
									
								
								.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,7 +107,7 @@ 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 ../../
 | 
			
		||||
@@ -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,7 +164,7 @@ 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 ../../
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,11 @@ option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)
 | 
			
		||||
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_KCSDR_SOURCE "Build KCSDR Source Module (Dependencies: libkcsdr)" OFF)
 | 
			
		||||
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)
 | 
			
		||||
@@ -42,12 +45,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
 | 
			
		||||
@@ -63,6 +68,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")
 | 
			
		||||
@@ -139,14 +145,26 @@ 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)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_KCSDR_SOURCE)
 | 
			
		||||
add_subdirectory("source_modules/kcsdr_source")
 | 
			
		||||
endif (OPT_BUILD_KCSDR_SOURCE)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_LIMESDR_SOURCE)
 | 
			
		||||
add_subdirectory("source_modules/limesdr_source")
 | 
			
		||||
endif (OPT_BUILD_LIMESDR_SOURCE)
 | 
			
		||||
@@ -235,6 +253,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)
 | 
			
		||||
@@ -259,6 +281,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)
 | 
			
		||||
@@ -312,6 +338,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 ()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -173,16 +173,24 @@ 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"]["Network Source"]["module"] = "network_source";
 | 
			
		||||
    defConfig["moduleInstances"]["Network 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 +201,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";
 | 
			
		||||
@@ -220,12 +232,19 @@ int sdrpp_main(int argc, char* argv[]) {
 | 
			
		||||
 | 
			
		||||
    defConfig["modules"] = json::array();
 | 
			
		||||
 | 
			
		||||
    defConfig["offsetMode"] = (int)0; // Off
 | 
			
		||||
    defConfig["offset"] = 0.0;
 | 
			
		||||
    defConfig["offsets"]["SpyVerter"] = 120000000.0;
 | 
			
		||||
    defConfig["offsets"]["Ham-It-Up"] = 125000000.0;
 | 
			
		||||
    defConfig["offsets"]["MMDS S-band (1998MHz)"] = -1998000000.0;
 | 
			
		||||
    defConfig["offsets"]["DK5AV X-Band"] = -6800000000.0;
 | 
			
		||||
    defConfig["offsets"]["Ku LNB (9750MHz)"] = -9750000000.0;
 | 
			
		||||
    defConfig["offsets"]["Ku LNB (10700MHz)"] = -10700000000.0;
 | 
			
		||||
 | 
			
		||||
    defConfig["selectedOffset"] = "None";
 | 
			
		||||
    defConfig["manualOffset"] = 0.0;
 | 
			
		||||
    defConfig["showMenu"] = true;
 | 
			
		||||
    defConfig["showWaterfall"] = true;
 | 
			
		||||
    defConfig["source"] = "";
 | 
			
		||||
    defConfig["decimationPower"] = 0;
 | 
			
		||||
    defConfig["decimation"] = 1;
 | 
			
		||||
    defConfig["iqCorrection"] = false;
 | 
			
		||||
    defConfig["invertIQ"] = false;
 | 
			
		||||
 | 
			
		||||
@@ -306,12 +325,18 @@ int sdrpp_main(int argc, char* argv[]) {
 | 
			
		||||
 | 
			
		||||
    // Remove unused elements
 | 
			
		||||
    auto items = core::configManager.conf.items();
 | 
			
		||||
    auto newConf = core::configManager.conf;
 | 
			
		||||
    bool configCorrected = false;
 | 
			
		||||
    for (auto const& item : items) {
 | 
			
		||||
        if (!defConfig.contains(item.key())) {
 | 
			
		||||
            flog::info("Unused key in config {0}, repairing", item.key());
 | 
			
		||||
            core::configManager.conf.erase(item.key());
 | 
			
		||||
            newConf.erase(item.key());
 | 
			
		||||
            configCorrected = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (configCorrected) {
 | 
			
		||||
        core::configManager.conf = newConf;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update to new module representation in config if needed
 | 
			
		||||
    for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {
 | 
			
		||||
 
 | 
			
		||||
@@ -37,9 +37,12 @@ namespace sdrpp_credits {
 | 
			
		||||
    const char* hardwareDonators[] = {
 | 
			
		||||
        "Aaronia AG",
 | 
			
		||||
        "Airspy",
 | 
			
		||||
        "Alex 4Z5LV",
 | 
			
		||||
        "Analog Devices",
 | 
			
		||||
        "CaribouLabs",
 | 
			
		||||
        "Deepace",
 | 
			
		||||
        "Ettus Research",
 | 
			
		||||
        "Harogic",
 | 
			
		||||
        "Howard Su",
 | 
			
		||||
        "MicroPhase",
 | 
			
		||||
        "Microtelecom",
 | 
			
		||||
@@ -47,6 +50,7 @@ namespace sdrpp_credits {
 | 
			
		||||
        "Nuand",
 | 
			
		||||
        "RFNM",
 | 
			
		||||
        "RFspace",
 | 
			
		||||
        "RigExpert",
 | 
			
		||||
        "RTL-SDRblog",
 | 
			
		||||
        "SDRplay"
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <volk/volk.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
namespace dsp::buffer {
 | 
			
		||||
    template<class T>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <volk/volk.h>
 | 
			
		||||
#include "../buffer/buffer.h"
 | 
			
		||||
 | 
			
		||||
namespace dsp {
 | 
			
		||||
    template<class T>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,113 +5,120 @@
 | 
			
		||||
#include <gui/main_window.h>
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <utils/optionlist.h>
 | 
			
		||||
#include <gui/dialogs/dialog_box.h>
 | 
			
		||||
 | 
			
		||||
namespace sourcemenu {
 | 
			
		||||
    int offsetMode = 0;
 | 
			
		||||
    int sourceId = 0;
 | 
			
		||||
    double customOffset = 0.0;
 | 
			
		||||
    double effectiveOffset = 0.0;
 | 
			
		||||
    int decimationPower = 0;
 | 
			
		||||
    EventHandler<std::string> sourcesChangedHandler;
 | 
			
		||||
    EventHandler<std::string> sourceUnregisterHandler;
 | 
			
		||||
    OptionList<std::string, std::string> sources;
 | 
			
		||||
    std::string selectedSource;
 | 
			
		||||
 | 
			
		||||
    int decimId = 0;
 | 
			
		||||
    OptionList<int, int> decimations;
 | 
			
		||||
 | 
			
		||||
    bool iqCorrection = false;
 | 
			
		||||
    bool invertIQ = false;
 | 
			
		||||
 | 
			
		||||
    EventHandler<std::string> sourceRegisteredHandler;
 | 
			
		||||
    EventHandler<std::string> sourceUnregisterHandler;
 | 
			
		||||
    EventHandler<std::string> sourceUnregisteredHandler;
 | 
			
		||||
    int offsetId = 0;
 | 
			
		||||
    double manualOffset = 0.0;
 | 
			
		||||
    std::string selectedOffset;
 | 
			
		||||
    double effectiveOffset = 0.0;
 | 
			
		||||
    OptionList<std::string, double> offsets;
 | 
			
		||||
    std::map<std::string, double> namedOffsets;
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> sourceNames;
 | 
			
		||||
    std::string sourceNamesTxt;
 | 
			
		||||
    std::string selectedSource;
 | 
			
		||||
    bool showAddOffsetDialog = false;
 | 
			
		||||
    char newOffsetName[1024];
 | 
			
		||||
    double newOffset = 0.0;
 | 
			
		||||
 | 
			
		||||
    bool showDelOffsetDialog = false;
 | 
			
		||||
    std::string delOffsetName = "";
 | 
			
		||||
 | 
			
		||||
    // Offset IDs
 | 
			
		||||
    enum {
 | 
			
		||||
        OFFSET_MODE_NONE,
 | 
			
		||||
        OFFSET_MODE_CUSTOM,
 | 
			
		||||
        OFFSET_MODE_SPYVERTER,
 | 
			
		||||
        OFFSET_MODE_HAM_IT_UP,
 | 
			
		||||
        OFFSET_MODE_MMDS_SB_1998,
 | 
			
		||||
        OFFSET_MODE_DK5AV_XB,
 | 
			
		||||
        OFFSET_MODE_KU_LNB_9750,
 | 
			
		||||
        OFFSET_MODE_KU_LNB_10700,
 | 
			
		||||
        _OFFSET_MODE_COUNT
 | 
			
		||||
        OFFSET_ID_NONE,
 | 
			
		||||
        OFFSET_ID_MANUAL,
 | 
			
		||||
        OFFSET_ID_CUSTOM_BASE
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const char* offsetModesTxt = "None\0"
 | 
			
		||||
                                 "Custom\0"
 | 
			
		||||
                                 "SpyVerter\0"
 | 
			
		||||
                                 "Ham-It-Up\0"
 | 
			
		||||
                                 "MMDS S-band (1998MHz)\0"
 | 
			
		||||
                                 "DK5AV X-Band\0"
 | 
			
		||||
                                 "Ku LNB (9750MHz)\0"
 | 
			
		||||
                                 "Ku LNB (10700MHz)\0";
 | 
			
		||||
 | 
			
		||||
    const char* decimationStages = "None\0"
 | 
			
		||||
                                   "2\0"
 | 
			
		||||
                                   "4\0"
 | 
			
		||||
                                   "8\0"
 | 
			
		||||
                                   "16\0"
 | 
			
		||||
                                   "32\0"
 | 
			
		||||
                                   "64\0";
 | 
			
		||||
 | 
			
		||||
    void updateOffset() {
 | 
			
		||||
        if (offsetMode == OFFSET_MODE_CUSTOM) { effectiveOffset = customOffset; }
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_SPYVERTER) {
 | 
			
		||||
            effectiveOffset = 120000000;
 | 
			
		||||
        } // 120MHz Up-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_HAM_IT_UP) {
 | 
			
		||||
            effectiveOffset = 125000000;
 | 
			
		||||
        } // 125MHz Up-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_MMDS_SB_1998) {
 | 
			
		||||
            effectiveOffset = -1998000000;
 | 
			
		||||
        } // 1.998GHz Down-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_DK5AV_XB) {
 | 
			
		||||
            effectiveOffset = -6800000000;
 | 
			
		||||
        } // 6.8GHz Down-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_KU_LNB_9750) {
 | 
			
		||||
            effectiveOffset = -9750000000;
 | 
			
		||||
        } // 9.750GHz Down-conversion
 | 
			
		||||
        else if (offsetMode == OFFSET_MODE_KU_LNB_10700) {
 | 
			
		||||
            effectiveOffset = -10700000000;
 | 
			
		||||
        } // 10.7GHz Down-conversion
 | 
			
		||||
        else {
 | 
			
		||||
        // Compute the effective offset
 | 
			
		||||
        switch (offsetId) {
 | 
			
		||||
        case OFFSET_ID_NONE:
 | 
			
		||||
            effectiveOffset = 0;
 | 
			
		||||
            break;
 | 
			
		||||
        case OFFSET_ID_MANUAL:
 | 
			
		||||
            effectiveOffset = manualOffset;
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            effectiveOffset = namedOffsets[offsets.name(offsetId)];
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Apply it
 | 
			
		||||
        sigpath::sourceManager.setTuningOffset(effectiveOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void selectOffsetById(int id) {
 | 
			
		||||
        // Update the offset mode
 | 
			
		||||
        offsetId = id;
 | 
			
		||||
        selectedOffset = offsets.name(id);
 | 
			
		||||
 | 
			
		||||
        // Update the offset
 | 
			
		||||
        updateOffset();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void selectOffsetByName(const std::string& name) {
 | 
			
		||||
        // If the name doesn't exist, select 'None'
 | 
			
		||||
        if (!offsets.nameExists(name)) {
 | 
			
		||||
            selectOffsetById(OFFSET_ID_NONE);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Select using the ID associated with the name
 | 
			
		||||
        selectOffsetById(offsets.nameId(name));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void refreshSources() {
 | 
			
		||||
        sourceNames = sigpath::sourceManager.getSourceNames();
 | 
			
		||||
        sourceNamesTxt.clear();
 | 
			
		||||
        // Get sources
 | 
			
		||||
        auto sourceNames = sigpath::sourceManager.getSourceNames();
 | 
			
		||||
 | 
			
		||||
        // Define source options
 | 
			
		||||
        sources.clear();
 | 
			
		||||
        for (auto name : sourceNames) {
 | 
			
		||||
            sourceNamesTxt += name;
 | 
			
		||||
            sourceNamesTxt += '\0';
 | 
			
		||||
            sources.define(name, name, name);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void selectSource(std::string name) {
 | 
			
		||||
        if (sourceNames.empty()) {
 | 
			
		||||
        // If there is no source, give up
 | 
			
		||||
        if (sources.empty()) {
 | 
			
		||||
            sourceId = 0;
 | 
			
		||||
            selectedSource.clear();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        auto it = std::find(sourceNames.begin(), sourceNames.end(), name);
 | 
			
		||||
        if (it == sourceNames.end()) {
 | 
			
		||||
            selectSource(sourceNames[0]);
 | 
			
		||||
 | 
			
		||||
        // If a source with the given name doesn't exist, select the first source instead
 | 
			
		||||
        if (!sources.valueExists(name)) {
 | 
			
		||||
            selectSource(sources.value(0));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        sourceId = std::distance(sourceNames.begin(), it);
 | 
			
		||||
        selectedSource = sourceNames[sourceId];
 | 
			
		||||
        sigpath::sourceManager.selectSource(sourceNames[sourceId]);
 | 
			
		||||
 | 
			
		||||
        // Update the GUI variables
 | 
			
		||||
        sourceId = sources.valueId(name);
 | 
			
		||||
        selectedSource = name;
 | 
			
		||||
 | 
			
		||||
        // Select the source module
 | 
			
		||||
        sigpath::sourceManager.selectSource(name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void onSourceRegistered(std::string name, void* ctx) {
 | 
			
		||||
    void onSourcesChanged(std::string name, void* ctx) {
 | 
			
		||||
        // Update the source list
 | 
			
		||||
        refreshSources();
 | 
			
		||||
 | 
			
		||||
        if (selectedSource.empty()) {
 | 
			
		||||
            sourceId = 0;
 | 
			
		||||
            selectSource(sourceNames[0]);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
 | 
			
		||||
        // Reselect the current source
 | 
			
		||||
        selectSource(selectedSource);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void onSourceUnregister(std::string name, void* ctx) {
 | 
			
		||||
@@ -120,60 +127,173 @@ namespace sourcemenu {
 | 
			
		||||
        // TODO: Stop everything
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void onSourceUnregistered(std::string name, void* ctx) {
 | 
			
		||||
        refreshSources();
 | 
			
		||||
    void reloadOffsets() {
 | 
			
		||||
        // Clear list
 | 
			
		||||
        offsets.clear();
 | 
			
		||||
        namedOffsets.clear();
 | 
			
		||||
 | 
			
		||||
        if (sourceNames.empty()) {
 | 
			
		||||
            selectedSource = "";
 | 
			
		||||
            return;
 | 
			
		||||
        // Define special offset modes
 | 
			
		||||
        offsets.define("None", OFFSET_ID_NONE);
 | 
			
		||||
        offsets.define("Manual", OFFSET_ID_MANUAL);
 | 
			
		||||
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.acquire();
 | 
			
		||||
 | 
			
		||||
        // Load custom offsets
 | 
			
		||||
        auto ofs = core::configManager.conf["offsets"].items();
 | 
			
		||||
        for (auto& o : ofs) {
 | 
			
		||||
            namedOffsets[o.key()] = (double)o.value();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (name == selectedSource) {
 | 
			
		||||
            sourceId = std::clamp<int>(sourceId, 0, sourceNames.size() - 1);
 | 
			
		||||
            selectSource(sourceNames[sourceId]);
 | 
			
		||||
            return;
 | 
			
		||||
        // Define custom offsets
 | 
			
		||||
        for (auto& [name, offset] : namedOffsets) {
 | 
			
		||||
            offsets.define(name, offsets.size());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
 | 
			
		||||
        // Release the config file
 | 
			
		||||
        core::configManager.release();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void init() {
 | 
			
		||||
        // Load offset modes
 | 
			
		||||
        reloadOffsets();
 | 
			
		||||
 | 
			
		||||
        // Define decimation values
 | 
			
		||||
        decimations.define(1, "None", 1);
 | 
			
		||||
        decimations.define(2, "2x", 2);
 | 
			
		||||
        decimations.define(4, "4x", 4);
 | 
			
		||||
        decimations.define(8, "8x", 8);
 | 
			
		||||
        decimations.define(16, "16x", 16);
 | 
			
		||||
        decimations.define(32, "32x", 32);
 | 
			
		||||
        decimations.define(64, "64x", 64);
 | 
			
		||||
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.acquire();
 | 
			
		||||
        std::string selected = core::configManager.conf["source"];
 | 
			
		||||
        customOffset = core::configManager.conf["offset"];
 | 
			
		||||
        offsetMode = core::configManager.conf["offsetMode"];
 | 
			
		||||
        decimationPower = core::configManager.conf["decimationPower"];
 | 
			
		||||
 | 
			
		||||
        // Load other settings
 | 
			
		||||
        std::string selectedSource = core::configManager.conf["source"];
 | 
			
		||||
        manualOffset = core::configManager.conf["manualOffset"];
 | 
			
		||||
        std::string selectedOffset = core::configManager.conf["selectedOffset"];
 | 
			
		||||
        iqCorrection = core::configManager.conf["iqCorrection"];
 | 
			
		||||
        invertIQ = core::configManager.conf["invertIQ"];
 | 
			
		||||
        int decimation = core::configManager.conf["decimation"];
 | 
			
		||||
        if (decimations.keyExists(decimation)) {
 | 
			
		||||
            decimId = decimations.keyId(decimation);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Release the config file
 | 
			
		||||
        core::configManager.release();
 | 
			
		||||
 | 
			
		||||
        // Select the source module
 | 
			
		||||
        refreshSources();
 | 
			
		||||
        selectSource(selectedSource);
 | 
			
		||||
 | 
			
		||||
        // Update frontend settings
 | 
			
		||||
        sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
 | 
			
		||||
        sigpath::iqFrontEnd.setInvertIQ(invertIQ);
 | 
			
		||||
        updateOffset();
 | 
			
		||||
        sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
 | 
			
		||||
        selectOffsetByName(selectedOffset);
 | 
			
		||||
 | 
			
		||||
        refreshSources();
 | 
			
		||||
        selectSource(selected);
 | 
			
		||||
        sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
 | 
			
		||||
 | 
			
		||||
        sourceRegisteredHandler.handler = onSourceRegistered;
 | 
			
		||||
        // Register handlers
 | 
			
		||||
        sourcesChangedHandler.handler = onSourcesChanged;
 | 
			
		||||
        sourceUnregisterHandler.handler = onSourceUnregister;
 | 
			
		||||
        sourceUnregisteredHandler.handler = onSourceUnregistered;
 | 
			
		||||
        sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
 | 
			
		||||
        sigpath::sourceManager.onSourceRegistered.bindHandler(&sourcesChangedHandler);
 | 
			
		||||
        sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
 | 
			
		||||
        sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
 | 
			
		||||
        sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourcesChangedHandler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        core::configManager.release();
 | 
			
		||||
    void addOffset(const std::string& name, double offset) {
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.acquire();
 | 
			
		||||
 | 
			
		||||
        // Define a new offset
 | 
			
		||||
        core::configManager.conf["offsets"][name] = offset;
 | 
			
		||||
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.release(true);
 | 
			
		||||
 | 
			
		||||
        // Reload the offsets
 | 
			
		||||
        reloadOffsets();
 | 
			
		||||
 | 
			
		||||
        // Attempt to re-select the same one
 | 
			
		||||
        selectOffsetByName(selectedOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void delOffset(const std::string& name) {
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.acquire();
 | 
			
		||||
 | 
			
		||||
        // Define a new offset
 | 
			
		||||
        core::configManager.conf["offsets"].erase(name);
 | 
			
		||||
 | 
			
		||||
        // Acquire the config file
 | 
			
		||||
        core::configManager.release(true);
 | 
			
		||||
 | 
			
		||||
        // Reload the offsets
 | 
			
		||||
        reloadOffsets();
 | 
			
		||||
 | 
			
		||||
        // Attempt to re-select the same one
 | 
			
		||||
        selectOffsetByName(selectedOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool addOffsetDialog() {
 | 
			
		||||
        bool open = true;
 | 
			
		||||
        gui::mainWindow.lockWaterfallControls = true;
 | 
			
		||||
 | 
			
		||||
        float menuWidth = ImGui::GetContentRegionAvail().x;
 | 
			
		||||
 | 
			
		||||
        const char* id = "Add offset##sdrpp_add_offset_dialog_";
 | 
			
		||||
        ImGui::OpenPopup(id);
 | 
			
		||||
 | 
			
		||||
        if (ImGui::BeginPopup(id, ImGuiWindowFlags_NoResize)) {
 | 
			
		||||
            ImGui::LeftLabel("Name");
 | 
			
		||||
            ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
 | 
			
		||||
            ImGui::InputText("##sdrpp_add_offset_name", newOffsetName, 1023);
 | 
			
		||||
 | 
			
		||||
            ImGui::LeftLabel("Offset");
 | 
			
		||||
            ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
 | 
			
		||||
            ImGui::InputDouble("##sdrpp_add_offset_offset", &newOffset);
 | 
			
		||||
 | 
			
		||||
            bool nameExists = offsets.nameExists(newOffsetName);
 | 
			
		||||
            bool reservedName = !strcmp(newOffsetName, "None") || !strcmp(newOffsetName, "Manual");
 | 
			
		||||
            bool denyApply = !newOffsetName[0] || nameExists || reservedName;
 | 
			
		||||
 | 
			
		||||
            if (nameExists) {
 | 
			
		||||
                ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "An offset with the given name already exists.");
 | 
			
		||||
            }
 | 
			
		||||
            else if (reservedName) {
 | 
			
		||||
                ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "The given name is reserved.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (denyApply) { style::beginDisabled(); }
 | 
			
		||||
            if (ImGui::Button("Apply")) {
 | 
			
		||||
                addOffset(newOffsetName, newOffset);
 | 
			
		||||
                open = false;
 | 
			
		||||
            }
 | 
			
		||||
            if (denyApply) { style::endDisabled(); }
 | 
			
		||||
            ImGui::SameLine();
 | 
			
		||||
            if (ImGui::Button("Cancel")) {
 | 
			
		||||
                open = false;
 | 
			
		||||
            }
 | 
			
		||||
            ImGui::EndPopup();
 | 
			
		||||
        }
 | 
			
		||||
        return open;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void draw(void* ctx) {
 | 
			
		||||
        float itemWidth = ImGui::GetContentRegionAvail().x;
 | 
			
		||||
        float lineHeight = ImGui::GetTextLineHeightWithSpacing();
 | 
			
		||||
        float spacing = lineHeight - ImGui::GetTextLineHeight();
 | 
			
		||||
        bool running = gui::mainWindow.sdrIsRunning();
 | 
			
		||||
 | 
			
		||||
        if (running) { style::beginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth);
 | 
			
		||||
        if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
 | 
			
		||||
            selectSource(sourceNames[sourceId]);
 | 
			
		||||
        if (ImGui::Combo("##source", &sourceId, sources.txt)) {
 | 
			
		||||
            std::string newSource = sources.value(sourceId);
 | 
			
		||||
            selectSource(newSource);
 | 
			
		||||
            core::configManager.acquire();
 | 
			
		||||
            core::configManager.conf["source"] = sourceNames[sourceId];
 | 
			
		||||
            core::configManager.conf["source"] = newSource;
 | 
			
		||||
            core::configManager.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -196,21 +316,45 @@ namespace sourcemenu {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ImGui::LeftLabel("Offset mode");
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) {
 | 
			
		||||
            updateOffset();
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX() - 2.0f*(lineHeight + 1.5f*spacing));
 | 
			
		||||
        if (ImGui::Combo("##_sdrpp_offset", &offsetId, offsets.txt)) {
 | 
			
		||||
            selectOffsetById(offsetId);
 | 
			
		||||
            core::configManager.acquire();
 | 
			
		||||
            core::configManager.conf["offsetMode"] = offsetMode;
 | 
			
		||||
            core::configManager.conf["selectedOffset"] = offsets.key(offsetId);
 | 
			
		||||
            core::configManager.release(true);
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
 | 
			
		||||
        if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::BeginDisabled(); }
 | 
			
		||||
        if (ImGui::Button("-##_sdrpp_offset_del_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
 | 
			
		||||
            delOffsetName = selectedOffset;
 | 
			
		||||
            showDelOffsetDialog = true;
 | 
			
		||||
        }
 | 
			
		||||
        if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::EndDisabled(); }
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
 | 
			
		||||
        if (ImGui::Button("+##_sdrpp_offset_add_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
 | 
			
		||||
            strcpy(newOffsetName, "New Offset");
 | 
			
		||||
            showAddOffsetDialog = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Offset delete confirmation
 | 
			
		||||
        if (ImGui::GenericDialog("sdrpp_del_offset_confirm", showDelOffsetDialog, GENERIC_DIALOG_BUTTONS_YES_NO, []() {
 | 
			
		||||
            ImGui::Text("Deleting offset named \"%s\". Are you sure?", delOffsetName.c_str());
 | 
			
		||||
        }) == GENERIC_DIALOG_BUTTON_YES) {
 | 
			
		||||
            delOffset(delOffsetName);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Offset add diaglog
 | 
			
		||||
        if (showAddOffsetDialog) { showAddOffsetDialog = addOffsetDialog(); }
 | 
			
		||||
 | 
			
		||||
        ImGui::LeftLabel("Offset");
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (offsetMode == OFFSET_MODE_CUSTOM) {
 | 
			
		||||
            if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) {
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (offsetId == OFFSET_ID_MANUAL) {
 | 
			
		||||
            if (ImGui::InputDouble("##freq_offset", &manualOffset, 1.0, 100.0)) {
 | 
			
		||||
                updateOffset();
 | 
			
		||||
                core::configManager.acquire();
 | 
			
		||||
                core::configManager.conf["offset"] = customOffset;
 | 
			
		||||
                core::configManager.conf["manualOffset"] = manualOffset;
 | 
			
		||||
                core::configManager.release(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -222,11 +366,11 @@ namespace sourcemenu {
 | 
			
		||||
 | 
			
		||||
        if (running) { style::beginDisabled(); }
 | 
			
		||||
        ImGui::LeftLabel("Decimation");
 | 
			
		||||
        ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
 | 
			
		||||
        if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) {
 | 
			
		||||
            sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::Combo("##source_decim", &decimId, decimations.txt)) {
 | 
			
		||||
            sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
 | 
			
		||||
            core::configManager.acquire();
 | 
			
		||||
            core::configManager.conf["decimationPower"] = decimationPower;
 | 
			
		||||
            core::configManager.conf["decimation"] = decimations.key(decimId);
 | 
			
		||||
            core::configManager.release(true);
 | 
			
		||||
        }
 | 
			
		||||
        if (running) { style::endDisabled(); }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@
 | 
			
		||||
#include <gui/style.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <backend.h>
 | 
			
		||||
#include <utils/hrfreq.h>
 | 
			
		||||
 | 
			
		||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
 | 
			
		||||
#define IMGUI_DEFINE_MATH_OPERATORS
 | 
			
		||||
@@ -90,6 +91,7 @@ void FrequencySelect::moveCursorToDigit(int i) {
 | 
			
		||||
 | 
			
		||||
void FrequencySelect::draw() {
 | 
			
		||||
    auto window = ImGui::GetCurrentWindow();
 | 
			
		||||
    auto io = ImGui::GetIO();
 | 
			
		||||
    widgetPos = ImGui::GetWindowContentRegionMin();
 | 
			
		||||
    ImVec2 cursorPos = ImGui::GetCursorPos();
 | 
			
		||||
    widgetPos.x += window->Pos.x + cursorPos.x;
 | 
			
		||||
@@ -132,7 +134,7 @@ void FrequencySelect::draw() {
 | 
			
		||||
        ImVec2 mousePos = ImGui::GetMousePos();
 | 
			
		||||
        bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
 | 
			
		||||
        bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
 | 
			
		||||
        int mw = ImGui::GetIO().MouseWheel;
 | 
			
		||||
        int mw = io.MouseWheel;
 | 
			
		||||
        bool onDigit = false;
 | 
			
		||||
        bool hovered = false;
 | 
			
		||||
 | 
			
		||||
@@ -174,7 +176,7 @@ void FrequencySelect::draw() {
 | 
			
		||||
                    moveCursorToDigit(i + 1);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                auto chars = ImGui::GetIO().InputQueueCharacters;
 | 
			
		||||
                auto chars = io.InputQueueCharacters;
 | 
			
		||||
 | 
			
		||||
                // For each keyboard characters, type it
 | 
			
		||||
                for (int j = 0; j < chars.Size; j++) {
 | 
			
		||||
@@ -194,6 +196,34 @@ void FrequencySelect::draw() {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        digitHovered = hovered;
 | 
			
		||||
 | 
			
		||||
        if (isInArea(mousePos, digitTopMins[0], digitBottomMaxs[11])) {
 | 
			
		||||
            bool shortcutKey = io.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
 | 
			
		||||
            bool ctrlOnly = (io.KeyMods == ImGuiKeyModFlags_Ctrl);
 | 
			
		||||
            bool shiftOnly = (io.KeyMods == ImGuiKeyModFlags_Shift);
 | 
			
		||||
            bool copy  = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_C)) || (ctrlOnly  && ImGui::IsKeyPressed(ImGuiKey_Insert)));
 | 
			
		||||
            bool paste = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_V)) || (shiftOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
 | 
			
		||||
            if (copy) {
 | 
			
		||||
                // Convert the freqency to a string
 | 
			
		||||
                std::string freqStr = hrfreq::toString(frequency);
 | 
			
		||||
 | 
			
		||||
                // Write it to the clipboard
 | 
			
		||||
                ImGui::SetClipboardText(freqStr.c_str());
 | 
			
		||||
            }
 | 
			
		||||
            if (paste) {
 | 
			
		||||
                // Attempt to parse the clipboard as a number
 | 
			
		||||
                const char* clip = ImGui::GetClipboardText();
 | 
			
		||||
 | 
			
		||||
                // If the clipboard is not empty, attempt to parse it
 | 
			
		||||
                if (clip) {
 | 
			
		||||
                    double newFreq;
 | 
			
		||||
                    if (hrfreq::fromString(clip, newFreq)) {
 | 
			
		||||
                        setFrequency(abs(newFreq));
 | 
			
		||||
                        frequencyChanged = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint64_t freq = 0;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								core/src/utils/hrfreq.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								core/src/utils/hrfreq.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
#include "hrfreq.h"
 | 
			
		||||
#include <utils/flog.h>
 | 
			
		||||
 | 
			
		||||
namespace hrfreq {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    std::string toString(double freq) {
 | 
			
		||||
        // Determine the scale
 | 
			
		||||
        int maxDecimals = 0;
 | 
			
		||||
        const char* suffix = "Hz";
 | 
			
		||||
        if (freq >= 1e9) {
 | 
			
		||||
            freq /= 1e9;
 | 
			
		||||
            maxDecimals = 9;
 | 
			
		||||
            suffix = "GHz";
 | 
			
		||||
        }
 | 
			
		||||
        else if (freq >= 1e6) {
 | 
			
		||||
            freq /= 1e6;
 | 
			
		||||
            maxDecimals = 6;
 | 
			
		||||
            suffix = "MHz";
 | 
			
		||||
        }
 | 
			
		||||
        else if (freq >= 1e3) {
 | 
			
		||||
            freq /= 1e3;
 | 
			
		||||
            maxDecimals = 3;
 | 
			
		||||
            suffix = "KHz";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Convert to string (TODO: Not sure if limiting the decimals rounds)
 | 
			
		||||
        char numBuf[128];
 | 
			
		||||
        int numLen = sprintf(numBuf, "%0.*lf", maxDecimals, freq);
 | 
			
		||||
 | 
			
		||||
        // If there is a decimal point, remove the useless zeros
 | 
			
		||||
        if (maxDecimals) {
 | 
			
		||||
            for (int i = numLen-1; i >= 0; i--) {
 | 
			
		||||
                bool dot = (numBuf[i] == '.');
 | 
			
		||||
                if (numBuf[i] != '0' && !dot) { break; }
 | 
			
		||||
                numBuf[i] = 0;
 | 
			
		||||
                if (dot) { break; }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Concat the suffix
 | 
			
		||||
        char finalBuf[128];
 | 
			
		||||
        sprintf(finalBuf, "%s%s", numBuf, suffix);
 | 
			
		||||
 | 
			
		||||
        // Return the final string
 | 
			
		||||
        return finalBuf;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isNumeric(char c) {
 | 
			
		||||
        return std::isdigit(c) || c == '+' || c == '-' || c == '.' || c == ',';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool fromString(const std::string& str, double& freq) {
 | 
			
		||||
        // Skip non-numeric characters
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        char c;
 | 
			
		||||
        for (; i < str.size(); i++) {
 | 
			
		||||
            if (isNumeric(str[i])) { break; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Extract the numeric part
 | 
			
		||||
        std::string numeric;
 | 
			
		||||
        for (; i < str.size(); i++) {
 | 
			
		||||
            // Get the character
 | 
			
		||||
            c = str[i];
 | 
			
		||||
 | 
			
		||||
            // If it's a letter, stop
 | 
			
		||||
            if (std::isalpha(c)) { break; }
 | 
			
		||||
 | 
			
		||||
            // If isn't numeric, skip it
 | 
			
		||||
            if (!isNumeric(c)) { continue; }
 | 
			
		||||
 | 
			
		||||
            // If it's a comma, skip it for now. This enforces a dot as a decimal point
 | 
			
		||||
            if (c == ',') { continue; }
 | 
			
		||||
 | 
			
		||||
            // Add the character to the numeric string
 | 
			
		||||
            numeric += c;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Attempt to parse the numeric part
 | 
			
		||||
        double num;
 | 
			
		||||
        try {
 | 
			
		||||
            num = std::stod(numeric);
 | 
			
		||||
        }
 | 
			
		||||
        catch (const std::exception& e) {
 | 
			
		||||
            flog::error("Failed to parse numeric part: '{}'", numeric);
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If no more text is available, assume the numeric part gives a frequency in Hz
 | 
			
		||||
        if (i == str.size()) {
 | 
			
		||||
            flog::warn("No unit given, assuming it's Hz");
 | 
			
		||||
            freq = num;
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Scale the numeric value depending on the first scale character
 | 
			
		||||
        char scale = std::toupper(str[i]);
 | 
			
		||||
        switch (scale) {
 | 
			
		||||
        case 'G':
 | 
			
		||||
            num *= 1e9;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'M':
 | 
			
		||||
            num *= 1e6;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'K':
 | 
			
		||||
            num *= 1e3;
 | 
			
		||||
            break;
 | 
			
		||||
        case 'H':
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            flog::warn("Unknown frequency scale: '{}'", scale);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Return the frequency
 | 
			
		||||
        freq = num;
 | 
			
		||||
        return true; // TODO
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										19
									
								
								core/src/utils/hrfreq.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								core/src/utils/hrfreq.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace hrfreq {
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert a frequency to a human-readable string.
 | 
			
		||||
     * @param freq Frequency in Hz.
 | 
			
		||||
     * @return Human-readable representation of the frequency.
 | 
			
		||||
    */
 | 
			
		||||
    std::string toString(double freq);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Convert a human-readable representation of a frequency to a frequency value.
 | 
			
		||||
     * @param str String containing the human-readable frequency.
 | 
			
		||||
     * @param freq Value to write the decoded frequency to.
 | 
			
		||||
     * @return True on success, false otherwise.
 | 
			
		||||
    */
 | 
			
		||||
    bool fromString(const std::string& str, double& freq);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,3 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#define VERSION_STR "1.2.0"
 | 
			
		||||
#define VERSION_STR "1.2.1"
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										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,7 +6,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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,7 +6,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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,7 +6,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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,7 +6,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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,7 +12,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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,7 +6,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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,7 +6,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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,7 +6,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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,7 +6,7 @@ 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.15.1.run
 | 
			
		||||
@@ -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 ..
 | 
			
		||||
 
 | 
			
		||||
@@ -35,11 +35,14 @@ 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/fobossdr_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/network_source/network_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/
 | 
			
		||||
 | 
			
		||||
@@ -32,6 +35,8 @@ cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windo
 | 
			
		||||
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/network_source/Release/network_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +44,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/
 | 
			
		||||
 
 | 
			
		||||
@@ -168,10 +168,9 @@ public:
 | 
			
		||||
        writer.setSamplerate(samplerate);
 | 
			
		||||
 | 
			
		||||
        // Open file
 | 
			
		||||
        std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
 | 
			
		||||
        std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
 | 
			
		||||
        std::string extension = ".wav";
 | 
			
		||||
        std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
 | 
			
		||||
        std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, recMode, vfoName) + extension);
 | 
			
		||||
        if (!writer.open(expandedPath)) {
 | 
			
		||||
            flog::error("Failed to open file for recording: {0}", expandedPath);
 | 
			
		||||
            return;
 | 
			
		||||
@@ -249,7 +248,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 +282,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 +295,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();
 | 
			
		||||
@@ -449,7 +451,7 @@ private:
 | 
			
		||||
        { RADIO_IFACE_MODE_RAW, "RAW" }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::string genFileName(std::string templ, std::string type, std::string name) {
 | 
			
		||||
    std::string genFileName(std::string templ, int mode, std::string name) {
 | 
			
		||||
        // Get data
 | 
			
		||||
        time_t now = time(0);
 | 
			
		||||
        tm* ltm = localtime(&now);
 | 
			
		||||
@@ -459,6 +461,9 @@ private:
 | 
			
		||||
            freq += gui::waterfall.vfos[name]->generalOffset;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Select the recording type string
 | 
			
		||||
        std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
 | 
			
		||||
 | 
			
		||||
        // Format to string
 | 
			
		||||
        char freqStr[128];
 | 
			
		||||
        char hourStr[128];
 | 
			
		||||
@@ -467,7 +472,7 @@ private:
 | 
			
		||||
        char dayStr[128];
 | 
			
		||||
        char monStr[128];
 | 
			
		||||
        char yearStr[128];
 | 
			
		||||
        const char* modeStr = "Unknown";
 | 
			
		||||
        const char* modeStr = (recMode == RECORDER_MODE_AUDIO) ? "Unknown" : "IQ";
 | 
			
		||||
        sprintf(freqStr, "%.0lfHz", freq);
 | 
			
		||||
        sprintf(hourStr, "%02d", ltm->tm_hour);
 | 
			
		||||
        sprintf(minStr, "%02d", ltm->tm_min);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								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,13 +324,16 @@ 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      | Working    | 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        | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| kcsdr_source         | Unfinished | libkcsdr          | OPT_BUILD_KCSDR_SOURCE         | ⛔              | ⛔                     | ⛔                         |
 | 
			
		||||
| limesdr_source       | Working    | liblimesuite      | OPT_BUILD_LIMESDR_SOURCE       | ⛔              | ✅                     | ✅                         |
 | 
			
		||||
| network_source       | Unfinished | -                 | OPT_BUILD_NETWORK_SOURCE       | ✅              | ✅                     | ⛔                         |
 | 
			
		||||
| network_source       | Beta       | -                 | 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          | ⛔              | ⛔                     | ⛔                         |
 | 
			
		||||
| 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       | ✅              | ✅                     | ✅                         |
 | 
			
		||||
@@ -339,9 +341,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
 | 
			
		||||
 | 
			
		||||
@@ -350,14 +352,15 @@ Modules in beta are still included in releases for the most part but not enabled
 | 
			
		||||
| android_audio_sink | Working    | -            | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔              | ✅              | ✅ (Android only)          |
 | 
			
		||||
| audio_sink         | Working    | rtaudio      | OPT_BUILD_AUDIO_SINK         | ✅              | ✅              | ✅                         |
 | 
			
		||||
| network_sink       | Working    | -            | OPT_BUILD_NETWORK_SINK       | ✅              | ✅              | ✅                         |
 | 
			
		||||
| new_portaudio_sink | Beta       | portaudio    | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
| portaudio_sink     | Beta       | portaudio    | OPT_BUILD_PORTAUDIO_SINK     | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
| new_portaudio_sink | Working    | portaudio    | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
| portaudio_sink     | Working    | portaudio    | OPT_BUILD_PORTAUDIO_SINK     | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
 | 
			
		||||
## Decoders
 | 
			
		||||
 | 
			
		||||
| 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         | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
@@ -416,8 +419,8 @@ If you still have an issue, please open an issue about it or ask on the discord.
 | 
			
		||||
 | 
			
		||||
# Contributing
 | 
			
		||||
 | 
			
		||||
Feel free to submit pull requests and report bugs via the GitHub issue tracker.
 | 
			
		||||
I will soon publish a contributing.md listing the code style to use.
 | 
			
		||||
Feel free to submit band plans via the GitHub issue tracker.
 | 
			
		||||
For code changes, please create a feature request instead.
 | 
			
		||||
 | 
			
		||||
# Credits
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										117
									
								
								root/res/bandplans/ireland.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								root/res/bandplans/ireland.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "Ireland",
 | 
			
		||||
    "country_name": "Republic Of Ireland",
 | 
			
		||||
    "country_code": "IE",
 | 
			
		||||
    "author_name": "Oskar Dudek",
 | 
			
		||||
    "author_url": "",
 | 
			
		||||
    "bands": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "2200m Ham Band",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 135700,
 | 
			
		||||
            "end": 137800
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Long wave",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 148500,
 | 
			
		||||
            "end": 282500
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "AM broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 531000,
 | 
			
		||||
            "end": 1602000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "120m SW broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 2300000,
 | 
			
		||||
            "end": 2495000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "90m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 3200000,
 | 
			
		||||
            "end": 3400000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "75m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 3900000,
 | 
			
		||||
            "end": 4000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "60m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 4750000,
 | 
			
		||||
            "end": 5060000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "49m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 5900000,
 | 
			
		||||
            "end": 6200000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "40m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 7200000,
 | 
			
		||||
            "end": 7450000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "31m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 9400000,
 | 
			
		||||
            "end": 9900000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "25m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 11600000,
 | 
			
		||||
            "end": 12100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "22m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 13570000,
 | 
			
		||||
            "end": 13870000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "19m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 15100000,
 | 
			
		||||
            "end": 15800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "16m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 17480000,
 | 
			
		||||
            "end": 17900000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "15m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 18900000,
 | 
			
		||||
            "end": 19020000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "13m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 21450000,
 | 
			
		||||
            "end": 21850000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "11m SW Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 25670000,
 | 
			
		||||
            "end": 26100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "FM Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 87500000,
 | 
			
		||||
            "end": 108000000
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										549
									
								
								root/res/bandplans/republic-of-korea.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										549
									
								
								root/res/bandplans/republic-of-korea.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,549 @@
 | 
			
		||||
{
 | 
			
		||||
    "name": "Republic of Korea",
 | 
			
		||||
    "country_name": "Republic of Korea",
 | 
			
		||||
    "country_code": "KR",
 | 
			
		||||
    "author_name": "SeoyeonBae",
 | 
			
		||||
    "author_url": "https://github.com/bsy0317",
 | 
			
		||||
    "bands": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radio Navigation",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 8300,
 | 
			
		||||
            "end": 14000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Coastal Telegraph",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 14000,
 | 
			
		||||
            "end": 19950
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 19950,
 | 
			
		||||
            "end": 20250
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Coastal Telegraph",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 20250,
 | 
			
		||||
            "end": 70000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radio Navigation",
 | 
			
		||||
            "type": "navigation",
 | 
			
		||||
            "start": 70000,
 | 
			
		||||
            "end": 160000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Radio Navigation",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 160000,
 | 
			
		||||
            "end": 285000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Maritime Radiobeacon",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 285000,
 | 
			
		||||
            "end": 325000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Radio Navigation",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 325000,
 | 
			
		||||
            "end": 472000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 472000,
 | 
			
		||||
            "end": 479000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "International Distress Safety Call",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 479000,
 | 
			
		||||
            "end": 505000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Maritime Telegraph",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 505000,
 | 
			
		||||
            "end": 526500
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 526500,
 | 
			
		||||
            "end": 1606500
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radiobuoy",
 | 
			
		||||
            "type": "navigation",
 | 
			
		||||
            "start": 1606500,
 | 
			
		||||
            "end": 1800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 1800000,
 | 
			
		||||
            "end": 1825000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radiobuoy Control LORAN",
 | 
			
		||||
            "type": "radiolocation",
 | 
			
		||||
            "start": 1825000,
 | 
			
		||||
            "end": 2000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radiobuoy",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 2000000,
 | 
			
		||||
            "end": 2065000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Distress Call",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 2065000,
 | 
			
		||||
            "end": 2107000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "International Distress Search and Rescue",
 | 
			
		||||
            "type": "mobile",
 | 
			
		||||
            "start": 2173500,
 | 
			
		||||
            "end": 2190500
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Road Management",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 2194000,
 | 
			
		||||
            "end": 2495000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 2495000,
 | 
			
		||||
            "end": 2505000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Telephone",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 2505000,
 | 
			
		||||
            "end": 2850000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile R",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 2850000,
 | 
			
		||||
            "end": 3025000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile OR",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 3025000,
 | 
			
		||||
            "end": 3155000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile R",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 3400000,
 | 
			
		||||
            "end": 3500000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 3500000,
 | 
			
		||||
            "end": 3550000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Experimental Station",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 3550000,
 | 
			
		||||
            "end": 3790000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 3790000,
 | 
			
		||||
            "end": 3800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 3900000,
 | 
			
		||||
            "end": 3950000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 3995000,
 | 
			
		||||
            "end": 4005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Telephone",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4005000,
 | 
			
		||||
            "end": 4063000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Oceanographic Data",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4063000,
 | 
			
		||||
            "end": 4065000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Duplex Telephone",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4065000,
 | 
			
		||||
            "end": 4146000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Simplex Telephone",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4146000,
 | 
			
		||||
            "end": 4152000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Wideband Telegraph Fax",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4152000,
 | 
			
		||||
            "end": 4172000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station Narrowband",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4172000,
 | 
			
		||||
            "end": 4181750
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Ship Station A1A Morse Code Communication",
 | 
			
		||||
            "type": "marine",
 | 
			
		||||
            "start": 4186750,
 | 
			
		||||
            "end": 4202250
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radiolocation",
 | 
			
		||||
            "type": "radiolocation",
 | 
			
		||||
            "start": 4438000,
 | 
			
		||||
            "end": 4488000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Calling Response",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 4488000,
 | 
			
		||||
            "end": 4650000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile R",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 4650000,
 | 
			
		||||
            "end": 4850000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 4995000,
 | 
			
		||||
            "end": 5005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Search Rescue",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 5480000,
 | 
			
		||||
            "end": 5730000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 5900000,
 | 
			
		||||
            "end": 5950000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 5950000,
 | 
			
		||||
            "end": 6200000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 7000000,
 | 
			
		||||
            "end": 7100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 7100000,
 | 
			
		||||
            "end": 7200000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 7200000,
 | 
			
		||||
            "end": 7450000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 7995000,
 | 
			
		||||
            "end": 8005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 9400000,
 | 
			
		||||
            "end": 9500000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 9500000,
 | 
			
		||||
            "end": 9900000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 9995000,
 | 
			
		||||
            "end": 10005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 10100000,
 | 
			
		||||
            "end": 10150000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 10150000,
 | 
			
		||||
            "end": 11600000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 11600000,
 | 
			
		||||
            "end": 11650000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 11650000,
 | 
			
		||||
            "end": 12050000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 12050000,
 | 
			
		||||
            "end": 12100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 13260000,
 | 
			
		||||
            "end": 13360000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Radio Astronomy",
 | 
			
		||||
            "type": "astronomy",
 | 
			
		||||
            "start": 13360000,
 | 
			
		||||
            "end": 13410000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 13570000,
 | 
			
		||||
            "end": 13600000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 13600000,
 | 
			
		||||
            "end": 13800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 13800000,
 | 
			
		||||
            "end": 13870000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 14000000,
 | 
			
		||||
            "end": 14350000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Aviation Mobile",
 | 
			
		||||
            "type": "aviation",
 | 
			
		||||
            "start": 15010000,
 | 
			
		||||
            "end": 15100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 15100000,
 | 
			
		||||
            "end": 15600000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 15600000,
 | 
			
		||||
            "end": 15800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 15800000,
 | 
			
		||||
            "end": 15995000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Standard Frequency Time Signal",
 | 
			
		||||
            "type": "utility",
 | 
			
		||||
            "start": 15995000,
 | 
			
		||||
            "end": 16005000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 18900000,
 | 
			
		||||
            "end": 19020000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 21000000,
 | 
			
		||||
            "end": 21450000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 21450000,
 | 
			
		||||
            "end": 21850000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 24890000,
 | 
			
		||||
            "end": 24990000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Shortwave Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 25670000,
 | 
			
		||||
            "end": 26100000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 28000000,
 | 
			
		||||
            "end": 29700000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 50000000,
 | 
			
		||||
            "end": 54000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "TV Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 54000000,
 | 
			
		||||
            "end": 72000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Flood Warning",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 72000000,
 | 
			
		||||
            "end": 74800000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "TV Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 76000000,
 | 
			
		||||
            "end": 88000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "FM Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 88000000,
 | 
			
		||||
            "end": 100000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "FM Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 100000000,
 | 
			
		||||
            "end": 108000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "ILS Localizer VOR",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 108000000,
 | 
			
		||||
            "end": 117975000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Amateur Station",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 144000000,
 | 
			
		||||
            "end": 146000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "General Communication",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 146000000,
 | 
			
		||||
            "end": 148000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Low Power Device",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 162037500,
 | 
			
		||||
            "end": 174000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "TV Broadcast",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 174000000,
 | 
			
		||||
            "end": 216000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Low Power Device",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 216000000,
 | 
			
		||||
            "end": 230000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Low Power Device",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 273000000,
 | 
			
		||||
            "end": 322000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Personal Radio",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 420000000,
 | 
			
		||||
            "end": 470000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Public Network",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 698000000,
 | 
			
		||||
            "end": 806000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Low Power Device",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 942000000,
 | 
			
		||||
            "end": 960000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Satellite Mobile Communication",
 | 
			
		||||
            "type": "fixed",
 | 
			
		||||
            "start": 15250000000,
 | 
			
		||||
            "end": 16605000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Mobile Communication",
 | 
			
		||||
            "type": "mobile",
 | 
			
		||||
            "start": 25000000000,
 | 
			
		||||
            "end": 37000000000
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
@@ -10,6 +10,7 @@
 | 
			
		||||
#include <libbladeRF.h>
 | 
			
		||||
#include <gui/smgui.h>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <utils/optionlist.h>
 | 
			
		||||
 | 
			
		||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
 | 
			
		||||
 | 
			
		||||
@@ -37,6 +38,10 @@ public:
 | 
			
		||||
    BladeRFSourceModule(std::string name) {
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        // Define clocks
 | 
			
		||||
        clocks.define("onboard", "On-Board", CLOCK_SELECT_ONBOARD);
 | 
			
		||||
        clocks.define("external", "External", CLOCK_SELECT_EXTERNAL);
 | 
			
		||||
 | 
			
		||||
        sampleRate = 1000000.0;
 | 
			
		||||
 | 
			
		||||
        handler.ctx = this;
 | 
			
		||||
@@ -267,6 +272,15 @@ public:
 | 
			
		||||
        }
 | 
			
		||||
        config.release(true);
 | 
			
		||||
 | 
			
		||||
        // Load clock source
 | 
			
		||||
        clkId = clocks.keyId("onboard");
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("clock")) {
 | 
			
		||||
            std::string clkStr = config.conf["devices"][selectedSerial]["clock"];
 | 
			
		||||
            if (clocks.keyExists(clkStr)) {
 | 
			
		||||
                clkId = clocks.keyId(clkStr);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Load gain mode
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("gainMode")) {
 | 
			
		||||
            std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
 | 
			
		||||
@@ -364,6 +378,7 @@ private:
 | 
			
		||||
        if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
 | 
			
		||||
 | 
			
		||||
        // Setup device parameters
 | 
			
		||||
        _this->setClockSource(_this->clocks[_this->clkId]);
 | 
			
		||||
        bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL);
 | 
			
		||||
        bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
 | 
			
		||||
        bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
 | 
			
		||||
@@ -486,6 +501,19 @@ private:
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("Clock Source");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Combo(CONCAT("##_balderf_clk_sel_", _this->name), &_this->clkId, _this->clocks.txt)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                _this->setClockSource(_this->clocks[_this->clkId]);
 | 
			
		||||
            }
 | 
			
		||||
            if (_this->selectedSerial != "") {
 | 
			
		||||
                config.acquire();
 | 
			
		||||
                config.conf["devices"][_this->selectedSerial]["clock"] = _this->clocks.key(_this->clkId);
 | 
			
		||||
                config.release(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // General config BS
 | 
			
		||||
        SmGui::LeftLabel("Gain control mode");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
@@ -537,6 +565,15 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void setClockSource(bladerf_clock_select clk) {
 | 
			
		||||
        if (selectedBladeType == BLADERF_TYPE_V1) {
 | 
			
		||||
            bladerf_set_smb_mode(openDev, (clk == CLOCK_SELECT_EXTERNAL) ? BLADERF_SMB_MODE_INPUT : BLADERF_SMB_MODE_DISABLED);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            bladerf_set_clock_select(openDev, clk);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void worker() {
 | 
			
		||||
        int16_t* buffer = new int16_t[bufferSize * 2];
 | 
			
		||||
        bladerf_metadata meta;
 | 
			
		||||
@@ -565,6 +602,7 @@ private:
 | 
			
		||||
    int devId = 0;
 | 
			
		||||
    int srId = 0;
 | 
			
		||||
    int bwId = 0;
 | 
			
		||||
    int clkId = 0;
 | 
			
		||||
    int chanId = 0;
 | 
			
		||||
    int gainMode = 0;
 | 
			
		||||
    bool streamingEnabled = false;
 | 
			
		||||
@@ -580,8 +618,8 @@ private:
 | 
			
		||||
    std::string sampleRatesTxt;
 | 
			
		||||
    std::vector<uint64_t> bandwidths;
 | 
			
		||||
    std::string bandwidthsTxt;
 | 
			
		||||
 | 
			
		||||
    std::string channelNamesTxt;
 | 
			
		||||
    OptionList<std::string, bladerf_clock_select> clocks;
 | 
			
		||||
 | 
			
		||||
    int bufferSize;
 | 
			
		||||
    struct bladerf_stream* rxStream;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								source_modules/kcsdr_source/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								source_modules/kcsdr_source/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
vendor/*
 | 
			
		||||
							
								
								
									
										10
									
								
								source_modules/kcsdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								source_modules/kcsdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(kcsdr_source)
 | 
			
		||||
 | 
			
		||||
file(GLOB SRC "src/*.cpp" "src/*.c")
 | 
			
		||||
 | 
			
		||||
include(${SDRPP_MODULE_CMAKE})
 | 
			
		||||
 | 
			
		||||
target_link_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10/x64/DLL")
 | 
			
		||||
target_include_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10")
 | 
			
		||||
target_link_libraries(kcsdr_source PRIVATE FTD3XX)
 | 
			
		||||
							
								
								
									
										209
									
								
								source_modules/kcsdr_source/src/kcsdr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								source_modules/kcsdr_source/src/kcsdr.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,209 @@
 | 
			
		||||
#include "kcsdr.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "../vendor/FTD3XXLibrary_1.3.0.10/FTD3XX.h"
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
 | 
			
		||||
#define KCSDR_PKT_EMPTY_LEN 0x0C
 | 
			
		||||
 | 
			
		||||
#define KCSDR_COMMAND_PIPE  0x02
 | 
			
		||||
#define KCSDR_RX_DATA_PIPE  0x83
 | 
			
		||||
#define KCSDR_TX_DATA_PIPE  0x03
 | 
			
		||||
 | 
			
		||||
struct kcsdr {
 | 
			
		||||
    FT_HANDLE ft;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#pragma pack(push, 1)
 | 
			
		||||
struct kcsdr_packet {
 | 
			
		||||
    uint8_t zeros0[4];
 | 
			
		||||
    uint8_t length;
 | 
			
		||||
    uint8_t zeros1[2];
 | 
			
		||||
    uint8_t hex_eighty;
 | 
			
		||||
    uint32_t command;
 | 
			
		||||
    uint8_t data[188];
 | 
			
		||||
};
 | 
			
		||||
typedef struct kcsdr_packet kcsdr_packet_t;
 | 
			
		||||
#pragma pack(pop)
 | 
			
		||||
 | 
			
		||||
enum kcsdr_command {
 | 
			
		||||
    CMD_NOT_USED_0x00   = 0x00,
 | 
			
		||||
    CMD_SET_PORT        = 0x01,
 | 
			
		||||
    CMD_SET_FREQUENCY   = 0x02,
 | 
			
		||||
    CMD_SET_ATTENUATION = 0x03,
 | 
			
		||||
    CMD_SET_AMPLIFIER   = 0x04,
 | 
			
		||||
    CMD_SET_BANDWIDTH   = 0x05,
 | 
			
		||||
    CMD_START           = 0x06,
 | 
			
		||||
    CMD_STOP            = 0x07,
 | 
			
		||||
    CMD_SET_EXT_AMP     = 0x08,
 | 
			
		||||
    CMD_START_REMOTE    = 0x09,
 | 
			
		||||
    CMD_STOP_REMOTE     = 0x0A
 | 
			
		||||
};
 | 
			
		||||
typedef enum kcsdr_command kcsdr_command_t;
 | 
			
		||||
 | 
			
		||||
int kcsdr_send_command(kcsdr_t* dev, kcsdr_direction_t dir, kcsdr_command_t cmd, const uint8_t* data, int len) {
 | 
			
		||||
    Sleep(50);
 | 
			
		||||
    
 | 
			
		||||
    // Create an empty packet
 | 
			
		||||
    kcsdr_packet_t pkt;
 | 
			
		||||
    memset(&pkt, 0, sizeof(kcsdr_packet_t));
 | 
			
		||||
 | 
			
		||||
    // Fill out the packet info
 | 
			
		||||
    pkt.length = len + KCSDR_PKT_EMPTY_LEN;
 | 
			
		||||
    pkt.hex_eighty = 0x80; // Whatever the fuck that is
 | 
			
		||||
    pkt.command = (uint32_t)cmd | (uint32_t)dir;
 | 
			
		||||
 | 
			
		||||
    // Copy the data if there is some
 | 
			
		||||
    if (len) { memcpy(pkt.data, data, len); }
 | 
			
		||||
 | 
			
		||||
    // Dump the bytes
 | 
			
		||||
    uint8_t* dump = (uint8_t*)&pkt;
 | 
			
		||||
    printf("Sending:");
 | 
			
		||||
    for (int i = 0; i < pkt.length; i++) {
 | 
			
		||||
        printf(" %02X", dump[i]);
 | 
			
		||||
    }
 | 
			
		||||
    printf("\n");
 | 
			
		||||
 | 
			
		||||
    // Send the command to endpoint 0
 | 
			
		||||
    int sent;
 | 
			
		||||
    FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_COMMAND_PIPE, (uint8_t*)&pkt, sizeof(kcsdr_packet_t), &sent, NULL);
 | 
			
		||||
    if (err != FT_OK) {
 | 
			
		||||
        return -err;
 | 
			
		||||
    }
 | 
			
		||||
    printf("Sent %d bytes (%d)\n", sent, err);
 | 
			
		||||
 | 
			
		||||
    Sleep(50);
 | 
			
		||||
 | 
			
		||||
    // Flush existing commands
 | 
			
		||||
    FT_FlushPipe(dev->ft, KCSDR_COMMAND_PIPE);
 | 
			
		||||
 | 
			
		||||
    return -(int)err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_list_devices(kcsdr_info_t** devices) {
 | 
			
		||||
    // Generate a list of FTDI devices
 | 
			
		||||
    int ftdiDevCount = 0;
 | 
			
		||||
    FT_STATUS err = FT_CreateDeviceInfoList(&ftdiDevCount);
 | 
			
		||||
    if (err != FT_OK) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If no device was found, return nothing
 | 
			
		||||
    if (!ftdiDevCount) {
 | 
			
		||||
        *devices = NULL;
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get said device list
 | 
			
		||||
    FT_DEVICE_LIST_INFO_NODE* list = malloc(ftdiDevCount * sizeof(FT_DEVICE_LIST_INFO_NODE));
 | 
			
		||||
    err = FT_GetDeviceInfoList(list, &ftdiDevCount);
 | 
			
		||||
    if (err != FT_OK) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Allocate the device info list
 | 
			
		||||
    *devices = malloc(ftdiDevCount * sizeof(kcsdr_info_t));
 | 
			
		||||
 | 
			
		||||
    // Find all KC908s
 | 
			
		||||
    int kcCount = 0;
 | 
			
		||||
    for (int i = 0; i < ftdiDevCount; i++) {
 | 
			
		||||
        strcpy((*devices)[kcCount++].serial, list[i].SerialNumber);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Free the FTDI list
 | 
			
		||||
    free(list);
 | 
			
		||||
 | 
			
		||||
    return kcCount;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void kcsdr_free_device_list(kcsdr_info_t* devices) {
 | 
			
		||||
    // Free the list
 | 
			
		||||
    if (devices) { free(devices); }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_open(kcsdr_t** dev, const char* serial) {
 | 
			
		||||
    // Attempt to open the device using the serial number
 | 
			
		||||
    FT_HANDLE ft;
 | 
			
		||||
    FT_STATUS err = FT_Create(serial, FT_OPEN_BY_SERIAL_NUMBER, &ft);
 | 
			
		||||
    if (err != FT_OK) {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the timeouts for the data pipes
 | 
			
		||||
    FT_SetPipeTimeout(ft, KCSDR_RX_DATA_PIPE, 1000);
 | 
			
		||||
    FT_SetPipeTimeout(ft, KCSDR_TX_DATA_PIPE, 1000);
 | 
			
		||||
 | 
			
		||||
    // Allocate the device object
 | 
			
		||||
    *dev = malloc(sizeof(kcsdr_t));
 | 
			
		||||
 | 
			
		||||
    // Fill out the device object
 | 
			
		||||
    (*dev)->ft = ft;
 | 
			
		||||
 | 
			
		||||
    // Put device into remote control mode
 | 
			
		||||
    return kcsdr_send_command(*dev, KCSDR_DIR_RX, CMD_START_REMOTE, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void kcsdr_close(kcsdr_t* dev) {
 | 
			
		||||
    // Put device back in normal mode
 | 
			
		||||
    kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_STOP_REMOTE, NULL, 0);
 | 
			
		||||
 | 
			
		||||
    // Close the device
 | 
			
		||||
    FT_Close(dev->ft);
 | 
			
		||||
 | 
			
		||||
    // Free the device object
 | 
			
		||||
    free(dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port) {
 | 
			
		||||
    // Send SET_PORT command
 | 
			
		||||
    return kcsdr_send_command(dev, dir, CMD_SET_PORT, &port, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq) {
 | 
			
		||||
    // Send SET_FREQUENCY command
 | 
			
		||||
    return kcsdr_send_command(dev, dir, CMD_SET_FREQUENCY, (uint8_t*)&freq, 8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att) {
 | 
			
		||||
    // Send SET_ATTENUATION command
 | 
			
		||||
    return kcsdr_send_command(dev, dir, CMD_SET_ATTENUATION, &att, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain) {
 | 
			
		||||
    // Send SET_AMPLIFIER command
 | 
			
		||||
    return kcsdr_send_command(dev, dir, CMD_SET_AMPLIFIER, &gain, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain) {
 | 
			
		||||
    // Send CMD_SET_EXT_AMP command
 | 
			
		||||
    return kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_SET_EXT_AMP, &gain, 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate) {
 | 
			
		||||
    // Set SET_BANDWIDTH command
 | 
			
		||||
    return kcsdr_send_command(dev, dir, CMD_SET_BANDWIDTH, (uint8_t*)&samplerate, 4);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir) {
 | 
			
		||||
    // Send START command
 | 
			
		||||
    return kcsdr_send_command(dev, dir, CMD_START, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir) {
 | 
			
		||||
    // Send STOP command
 | 
			
		||||
    return kcsdr_send_command(dev, dir, CMD_STOP, NULL, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count) {
 | 
			
		||||
    // Receive samples (TODO: Endpoint might be 0x81)
 | 
			
		||||
    int received;
 | 
			
		||||
    FT_STATUS err = FT_ReadPipeEx(dev->ft, KCSDR_RX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &received, NULL);
 | 
			
		||||
    return (err == FT_OK) ? received / (2*sizeof(uint16_t)) : -(int)err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count) {
 | 
			
		||||
    // Transmit samples
 | 
			
		||||
    int sent;
 | 
			
		||||
    FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_TX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &sent, NULL);
 | 
			
		||||
    return (err == FT_OK) ? sent / (2*sizeof(uint16_t)) : -(int)err;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										150
									
								
								source_modules/kcsdr_source/src/kcsdr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								source_modules/kcsdr_source/src/kcsdr.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,150 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define KCSDR_SERIAL_LEN    16
 | 
			
		||||
#define KCSDR_MAX_PORTS     6
 | 
			
		||||
 | 
			
		||||
// Detect C++
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
extern "C" {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * KCSDR Device.
 | 
			
		||||
*/
 | 
			
		||||
struct kcsdr;
 | 
			
		||||
typedef struct kcsdr kcsdr_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Device Information
 | 
			
		||||
*/
 | 
			
		||||
struct kcsdr_info {
 | 
			
		||||
    char serial[KCSDR_SERIAL_LEN+1];
 | 
			
		||||
};
 | 
			
		||||
typedef struct kcsdr_info kcsdr_info_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * RF Direction.
 | 
			
		||||
*/
 | 
			
		||||
enum kcsdr_direction {
 | 
			
		||||
    KCSDR_DIR_RX    = 0x00,
 | 
			
		||||
    KCSDR_DIR_TX    = 0x80
 | 
			
		||||
};
 | 
			
		||||
typedef enum kcsdr_direction kcsdr_direction_t;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get a list of KCSDR devices on the system.
 | 
			
		||||
 * @param devices Pointer to an array of device info.
 | 
			
		||||
 * @return Number of devices found or error code.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_list_devices(kcsdr_info_t** devices);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Free a device list returned by `kcsdr_list_devices()`.
 | 
			
		||||
 * @param devices Device list to free.
 | 
			
		||||
*/
 | 
			
		||||
void kcsdr_free_device_list(kcsdr_info_t* devices);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Open a KCSDR device.
 | 
			
		||||
 * @param dev Newly open device.
 | 
			
		||||
 * @param serial Serial number of the device to open as returned in the device list.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_open(kcsdr_t** dev, const char* serial);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Close a KCSDR device.
 | 
			
		||||
 * @param dev Device to be closed.
 | 
			
		||||
*/
 | 
			
		||||
void kcsdr_close(kcsdr_t* dev);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Select the RF port.
 | 
			
		||||
 * @param dev Device to control.
 | 
			
		||||
 * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
 | 
			
		||||
 * @param port RF port number to select.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the center frequency.
 | 
			
		||||
 * @param dev Device to control.
 | 
			
		||||
 * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
 | 
			
		||||
 * @param freq Frequency in Hz.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the attenuation.
 | 
			
		||||
 * @param dev Device to control.
 | 
			
		||||
 * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
 | 
			
		||||
 * @param samplerate Attenuation in dB.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the internal amplifier gain.
 | 
			
		||||
 * @param dev Device to control.
 | 
			
		||||
 * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
 | 
			
		||||
 * @param gain Gain in dB.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the external amplifier gain.
 | 
			
		||||
 * @param dev Device to control.
 | 
			
		||||
 * @param gain Gain in dB.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Set the samplerate.
 | 
			
		||||
 * @param dev Device to control.
 | 
			
		||||
 * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
 | 
			
		||||
 * @param samplerate Samplerate in Hz.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Start streaming samples.
 | 
			
		||||
 * @param dev Device to control.
 | 
			
		||||
 * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stop streaming samples.
 | 
			
		||||
 * @param dev Device to control.
 | 
			
		||||
 * @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
 | 
			
		||||
 * @return 0 on success, error code otherwise.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Receive a buffer of samples.
 | 
			
		||||
 * @param samples Sample buffer.
 | 
			
		||||
 * @param count Number of complex samples.
 | 
			
		||||
 * @return Number of samples received.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Transmit a buffer of samples.
 | 
			
		||||
 * @param samples Sample buffer.
 | 
			
		||||
 * @param count Number of complex samples.
 | 
			
		||||
 * @return Number of samples transmitted.
 | 
			
		||||
*/
 | 
			
		||||
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count);
 | 
			
		||||
 | 
			
		||||
// Detect C++
 | 
			
		||||
#ifdef __cplusplus
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										324
									
								
								source_modules/kcsdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								source_modules/kcsdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,324 @@
 | 
			
		||||
#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 "kcsdr.h"
 | 
			
		||||
#include <atomic>
 | 
			
		||||
 | 
			
		||||
SDRPP_MOD_INFO{
 | 
			
		||||
    /* Name:            */ "kcsdr_source",
 | 
			
		||||
    /* Description:     */ "KCSDR Source Module",
 | 
			
		||||
    /* Author:          */ "Ryzerth",
 | 
			
		||||
    /* Version:         */ 0, 1, 0,
 | 
			
		||||
    /* Max instances    */ -1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
 | 
			
		||||
 | 
			
		||||
class KCSDRSourceModule : public ModuleManager::Instance {
 | 
			
		||||
public:
 | 
			
		||||
    KCSDRSourceModule(std::string name) {
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        sampleRate = 2000000.0;
 | 
			
		||||
        samplerates.define(40e6, "40MHz", 40e6);
 | 
			
		||||
        samplerates.define(35e6, "35MHz", 35e6);
 | 
			
		||||
        samplerates.define(30e6, "30MHz", 30e6);
 | 
			
		||||
        samplerates.define(25e6, "25MHz", 25e6);
 | 
			
		||||
        samplerates.define(20e6, "20MHz", 20e6);
 | 
			
		||||
        samplerates.define(15e6, "15MHz", 15e6);
 | 
			
		||||
        samplerates.define(10e6, "10MHz", 10e6);
 | 
			
		||||
        samplerates.define(5e6, "5MHz", 5e6);
 | 
			
		||||
 | 
			
		||||
        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("KCSDR", &handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~KCSDRSourceModule() {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void postInit() {}
 | 
			
		||||
 | 
			
		||||
    void enable() {
 | 
			
		||||
        enabled = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void disable() {
 | 
			
		||||
        enabled = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isEnabled() {
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void refresh() {
 | 
			
		||||
        devices.clear();
 | 
			
		||||
        
 | 
			
		||||
        // Get device list
 | 
			
		||||
        kcsdr_info_t* list;
 | 
			
		||||
        int count = kcsdr_list_devices(&list);
 | 
			
		||||
        if (count < 0) {
 | 
			
		||||
            flog::error("Failed to list devices: {}", count);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Create list
 | 
			
		||||
        for (int i = 0; i < count; i++) {
 | 
			
		||||
            devices.define(list[i].serial, list[i].serial, list[i].serial);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Free the device list
 | 
			
		||||
        kcsdr_free_device_list(list);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
        // TODO
 | 
			
		||||
 | 
			
		||||
        // Update the samplerate
 | 
			
		||||
        core::setInputSampleRate(sampleRate);
 | 
			
		||||
 | 
			
		||||
        // Save serial number
 | 
			
		||||
        selectedSerial = serial;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuSelected(void* ctx) {
 | 
			
		||||
        KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
 | 
			
		||||
        core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
        flog::info("KCSDRSourceModule '{0}': Menu Select!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuDeselected(void* ctx) {
 | 
			
		||||
        KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
 | 
			
		||||
        flog::info("KCSDRSourceModule '{0}': Menu Deselect!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void start(void* ctx) {
 | 
			
		||||
        KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) { return; }
 | 
			
		||||
 | 
			
		||||
        // If no serial is given, do nothing
 | 
			
		||||
        if (_this->selectedSerial.empty()) { return; }
 | 
			
		||||
 | 
			
		||||
        // Open the device
 | 
			
		||||
        int err = kcsdr_open(&_this->openDev, _this->selectedSerial.c_str());
 | 
			
		||||
        if (err) {
 | 
			
		||||
            flog::error("Failed to open device: {}", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Configure the device
 | 
			
		||||
        kcsdr_set_port(_this->openDev, KCSDR_DIR_RX, 0);
 | 
			
		||||
        kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, _this->freq);
 | 
			
		||||
        kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
 | 
			
		||||
        kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
 | 
			
		||||
        kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
 | 
			
		||||
        kcsdr_set_samplerate(_this->openDev, KCSDR_DIR_RX, _this->sampleRate);
 | 
			
		||||
 | 
			
		||||
        // Start the stream
 | 
			
		||||
        kcsdr_start(_this->openDev, KCSDR_DIR_RX);
 | 
			
		||||
 | 
			
		||||
        // Start worker
 | 
			
		||||
        _this->run = true;
 | 
			
		||||
        _this->workerThread = std::thread(&KCSDRSourceModule::worker, _this);
 | 
			
		||||
        
 | 
			
		||||
        _this->running = true;
 | 
			
		||||
        flog::info("KCSDRSourceModule '{0}': Start!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void stop(void* ctx) {
 | 
			
		||||
        KCSDRSourceModule* _this = (KCSDRSourceModule*)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 streaming
 | 
			
		||||
        kcsdr_stop(_this->openDev, KCSDR_DIR_RX);
 | 
			
		||||
 | 
			
		||||
        // Close the device
 | 
			
		||||
        kcsdr_close(_this->openDev);
 | 
			
		||||
 | 
			
		||||
        flog::info("KCSDRSourceModule '{0}': Stop!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void tune(double freq, void* ctx) {
 | 
			
		||||
        KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) {
 | 
			
		||||
            kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, freq);
 | 
			
		||||
        }
 | 
			
		||||
        _this->freq = freq;
 | 
			
		||||
        flog::info("KCSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuHandler(void* ctx) {
 | 
			
		||||
        KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
 | 
			
		||||
        
 | 
			
		||||
        if (_this->running) { SmGui::BeginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        SmGui::ForceSync();
 | 
			
		||||
        if (SmGui::Combo(CONCAT("##_kcsdr_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("##_kcsdr_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##_kcsdr_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("##_kcsdr_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
 | 
			
		||||
        //     if (_this->running) {
 | 
			
		||||
        //         // TODO
 | 
			
		||||
        //     }
 | 
			
		||||
        //     // TODO: Save
 | 
			
		||||
        // }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("Attenuation");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::SliderInt(CONCAT("##_kcsdr_att_", _this->name), &_this->att, 0, 31)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
 | 
			
		||||
            }
 | 
			
		||||
            // TODO: Save
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("Gain");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::SliderInt(CONCAT("##_kcsdr_gain_", _this->name), &_this->gain, 0, 31)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
 | 
			
		||||
            }
 | 
			
		||||
            // TODO: Save
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("External Gain");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::SliderInt(CONCAT("##_kcsdr_ext_gain_", _this->name), &_this->extGain, 0, 31)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
 | 
			
		||||
            }
 | 
			
		||||
            // TODO: Save
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void worker() {
 | 
			
		||||
        // Compute the buffer size
 | 
			
		||||
        int bufferSize = 0x4000/4;//sampleRate / 200;
 | 
			
		||||
 | 
			
		||||
        // Allocate the sample buffer
 | 
			
		||||
        int16_t* samps = dsp::buffer::alloc<int16_t>(bufferSize*2);
 | 
			
		||||
 | 
			
		||||
        // Loop
 | 
			
		||||
        while (run) {
 | 
			
		||||
            // Read samples
 | 
			
		||||
            int count = kcsdr_rx(openDev, samps, bufferSize);
 | 
			
		||||
            if (!count) { continue; }
 | 
			
		||||
            if (count < 0) {
 | 
			
		||||
                flog::debug("Failed to read samples: {}", count);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Convert the samples to float
 | 
			
		||||
            volk_16i_s32f_convert_32f((float*)stream.writeBuf, samps, 8192.0f, count*2);
 | 
			
		||||
 | 
			
		||||
            // Send out the samples
 | 
			
		||||
            if (!stream.swap(count)) { break; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Free the sample buffer
 | 
			
		||||
        dsp::buffer::free(samps);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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<int, double> samplerates;
 | 
			
		||||
    int devId = 0;
 | 
			
		||||
    int srId = 0;
 | 
			
		||||
    int att = 0;
 | 
			
		||||
    int gain = 30;
 | 
			
		||||
    int extGain = 1;
 | 
			
		||||
    int portId = 0;
 | 
			
		||||
    std::string selectedSerial;
 | 
			
		||||
 | 
			
		||||
    kcsdr_t* openDev;
 | 
			
		||||
 | 
			
		||||
    std::thread workerThread;
 | 
			
		||||
    std::atomic<bool> run = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _INIT_() {
 | 
			
		||||
    // Nothing here
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
 | 
			
		||||
    return new KCSDRSourceModule(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
 | 
			
		||||
    delete (KCSDRSourceModule*)instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _END_() {
 | 
			
		||||
    // Nothing here
 | 
			
		||||
}
 | 
			
		||||
@@ -36,10 +36,10 @@ enum SampleType {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const size_t SAMPLE_TYPE_SIZE[] {
 | 
			
		||||
    sizeof(int8_t)*2,
 | 
			
		||||
    sizeof(int16_t)*2,
 | 
			
		||||
    sizeof(int32_t)*2,
 | 
			
		||||
    sizeof(float)*2,
 | 
			
		||||
    2*sizeof(int8_t),
 | 
			
		||||
    2*sizeof(int16_t),
 | 
			
		||||
    2*sizeof(int32_t),
 | 
			
		||||
    2*sizeof(float),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class NetworkSourceModule : public ModuleManager::Instance {
 | 
			
		||||
@@ -58,20 +58,6 @@ public:
 | 
			
		||||
        handler.tuneHandler = tune;
 | 
			
		||||
        handler.stream = &stream;
 | 
			
		||||
 | 
			
		||||
        // Define samplerates
 | 
			
		||||
        for (int i = 3000; i <= 192000; i <<= 1) {
 | 
			
		||||
            samplerates.define(i, getSrScaled(i), i);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 250000; i < 1000000; i += 250000) {
 | 
			
		||||
            samplerates.define(i, getSrScaled(i), i);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 1000000; i < 10000000; i += 500000) {
 | 
			
		||||
            samplerates.define(i, getSrScaled(i), i);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 10000000; i <= 100000000; i += 5000000) {
 | 
			
		||||
            samplerates.define(i, getSrScaled(i), i);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Define protocols
 | 
			
		||||
        // protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
 | 
			
		||||
        protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
 | 
			
		||||
@@ -86,8 +72,8 @@ public:
 | 
			
		||||
        // Load config
 | 
			
		||||
        config.acquire();
 | 
			
		||||
        if (config.conf[name].contains("samplerate")) {
 | 
			
		||||
            int sr = config.conf[name]["samplerate"];
 | 
			
		||||
            if (samplerates.keyExists(sr)) { samplerate = samplerates.value(samplerates.keyId(sr)); }
 | 
			
		||||
            samplerate = config.conf[name]["samplerate"];
 | 
			
		||||
            tempSamplerate = samplerate;
 | 
			
		||||
        }
 | 
			
		||||
        if (config.conf[name].contains("protocol")) {
 | 
			
		||||
            std::string protoStr = config.conf[name]["protocol"];
 | 
			
		||||
@@ -108,7 +94,6 @@ public:
 | 
			
		||||
        config.release();
 | 
			
		||||
 | 
			
		||||
        // Set menu IDs
 | 
			
		||||
        srId = samplerates.valueId(samplerate);
 | 
			
		||||
        protoId = protocols.valueId(proto);
 | 
			
		||||
        sampTypeId = sampleTypes.valueId(sampType);
 | 
			
		||||
 | 
			
		||||
@@ -228,35 +213,24 @@ private:
 | 
			
		||||
        if (_this->running) { SmGui::BeginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        // Hostname and port field
 | 
			
		||||
        if (ImGui::InputText(("##iq_exporter_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
 | 
			
		||||
        if (SmGui::InputText(("##network_source_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["host"] = _this->hostname;
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::SameLine();
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::InputInt(("##iq_exporter_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
 | 
			
		||||
        SmGui::SameLine();
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::InputInt(("##network_source_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
 | 
			
		||||
            _this->port = std::clamp<int>(_this->port, 1, 65535);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["port"] = _this->port;
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Samplerate selector
 | 
			
		||||
        ImGui::LeftLabel("Samplerate");
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::Combo(("##iq_exporter_sr_" + _this->name).c_str(), &_this->srId, _this->samplerates.txt)) {
 | 
			
		||||
            _this->samplerate = _this->samplerates.value(_this->srId);
 | 
			
		||||
            core::setInputSampleRate(_this->samplerate);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["samplerate"] = _this->samplerates.key(_this->srId);
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Mode protocol selector
 | 
			
		||||
        ImGui::LeftLabel("Protocol");
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::Combo(("##iq_exporter_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
 | 
			
		||||
        SmGui::LeftLabel("Protocol");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Combo(("##network_source_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
 | 
			
		||||
            _this->proto = _this->protocols.value(_this->protoId);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
 | 
			
		||||
@@ -264,15 +238,38 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Sample type selector
 | 
			
		||||
        ImGui::LeftLabel("Sample type");
 | 
			
		||||
        ImGui::FillWidth();
 | 
			
		||||
        if (ImGui::Combo(("##iq_exporter_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
 | 
			
		||||
        SmGui::LeftLabel("Sample type");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Combo(("##network_source_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
 | 
			
		||||
            _this->sampType = _this->sampleTypes.value(_this->sampTypeId);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Samplerate selector
 | 
			
		||||
        SmGui::LeftLabel("Samplerate");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::InputInt(("##network_source_sr_" + _this->name).c_str(), &_this->tempSamplerate)) {
 | 
			
		||||
            // Prevent silly values from silly users
 | 
			
		||||
            _this->tempSamplerate = std::max<int>(_this->tempSamplerate, 1000);
 | 
			
		||||
        }
 | 
			
		||||
        bool applyEn = (!_this->running && _this->tempSamplerate != _this->samplerate);
 | 
			
		||||
        if (!applyEn) { SmGui::BeginDisabled(); }
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Button(("Apply##network_source_apply_" + _this->name).c_str())) {
 | 
			
		||||
            _this->samplerate = _this->tempSamplerate;
 | 
			
		||||
            core::setInputSampleRate(_this->samplerate);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf[_this->name]["samplerate"] = _this->samplerate;
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
        if (!applyEn) { SmGui::EndDisabled(); }
 | 
			
		||||
 | 
			
		||||
        if (_this->tempSamplerate != _this->samplerate) {
 | 
			
		||||
            SmGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Warning: Samplerate not applied yet");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_this->running) { SmGui::EndDisabled(); }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -280,14 +277,17 @@ private:
 | 
			
		||||
        // Compute sizes
 | 
			
		||||
        int blockSize = samplerate / 200;
 | 
			
		||||
        int sampleSize = SAMPLE_TYPE_SIZE[sampType];
 | 
			
		||||
        int frameSize = blockSize*sampleSize;
 | 
			
		||||
 | 
			
		||||
        // Chose amount of bytes to attempt to read
 | 
			
		||||
        bool forceSize = (proto != PROTOCOL_UDP);
 | 
			
		||||
        int frameSize = sampleSize * (forceSize ? blockSize : STREAM_BUFFER_SIZE);
 | 
			
		||||
 | 
			
		||||
        // Allocate receive buffer
 | 
			
		||||
        uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
 | 
			
		||||
 | 
			
		||||
        while (true) {
 | 
			
		||||
            // Read samples from socket
 | 
			
		||||
            int bytes = sock->recv(buffer, frameSize, true);
 | 
			
		||||
            int bytes = sock->recv(buffer, frameSize, forceSize);
 | 
			
		||||
            if (bytes <= 0) { break; }
 | 
			
		||||
 | 
			
		||||
            // Convert to CF32 (note: problem if partial sample)
 | 
			
		||||
@@ -325,7 +325,7 @@ private:
 | 
			
		||||
    double freq;
 | 
			
		||||
    
 | 
			
		||||
    int samplerate = 1000000;
 | 
			
		||||
    int srId;
 | 
			
		||||
    int tempSamplerate = 1000000;
 | 
			
		||||
    Protocol proto = PROTOCOL_UDP;
 | 
			
		||||
    int protoId;
 | 
			
		||||
    SampleType sampType = SAMPLE_TYPE_INT16;
 | 
			
		||||
@@ -333,7 +333,6 @@ private:
 | 
			
		||||
    char hostname[1024] = "localhost";
 | 
			
		||||
    int port = 1234;
 | 
			
		||||
 | 
			
		||||
    OptionList<int, int> samplerates;
 | 
			
		||||
    OptionList<std::string, Protocol> protocols;
 | 
			
		||||
    OptionList<std::string, SampleType> sampleTypes;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,12 @@ SDRPP_MOD_INFO{
 | 
			
		||||
 | 
			
		||||
ConfigManager config;
 | 
			
		||||
 | 
			
		||||
const std::vector<const char*> deviceWhiteList = {
 | 
			
		||||
    "PlutoSDR",
 | 
			
		||||
    "ANTSDR",
 | 
			
		||||
    "LibreSDR"
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PlutoSDRSourceModule : public ModuleManager::Instance {
 | 
			
		||||
public:
 | 
			
		||||
    PlutoSDRSourceModule(std::string name) {
 | 
			
		||||
@@ -130,7 +136,14 @@ private:
 | 
			
		||||
            std::string duri = iio_context_info_get_uri(info);
 | 
			
		||||
 | 
			
		||||
            // If the device is not a plutosdr, don't include it
 | 
			
		||||
            if (desc.find("PlutoSDR") == std::string::npos) {
 | 
			
		||||
            bool isPluto = false;
 | 
			
		||||
            for (const auto type : deviceWhiteList) {
 | 
			
		||||
                if (desc.find(type) != std::string::npos) {
 | 
			
		||||
                    isPluto = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (!isPluto) {
 | 
			
		||||
                flog::warn("Ignored IIO device: [{}] {}", duri, desc);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,17 @@ file(GLOB SRC "src/*.cpp")
 | 
			
		||||
 | 
			
		||||
include(${SDRPP_MODULE_CMAKE})
 | 
			
		||||
 | 
			
		||||
target_include_directories(rfnm_source PRIVATE "C:/Users/ryzerth/Documents/Code/librfnm/include/librfnm")
 | 
			
		||||
target_link_directories(rfnm_source PRIVATE "C:/Users/ryzerth/Documents/Code/librfnm/build/Release")
 | 
			
		||||
target_link_libraries(rfnm_source PRIVATE librfnm)
 | 
			
		||||
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 ()
 | 
			
		||||
@@ -3,9 +3,10 @@
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <gui/smgui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <librfnm.h>
 | 
			
		||||
#include <librfnm/librfnm.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
#include <utils/optionlist.h>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
 | 
			
		||||
SDRPP_MOD_INFO{
 | 
			
		||||
    /* Name:            */ "rfnm_source",
 | 
			
		||||
@@ -90,8 +91,131 @@ private:
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // // Open the device
 | 
			
		||||
        // librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
 | 
			
		||||
        // 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();
 | 
			
		||||
@@ -103,27 +227,8 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get gain range
 | 
			
		||||
        gainMin = -30;//dev->librfnm_s->rx.ch[0].gain_range.min;
 | 
			
		||||
        gainMax = 60;//dev->librfnm_s->rx.ch[0].gain_range.max;
 | 
			
		||||
 | 
			
		||||
        // // Close device
 | 
			
		||||
        // delete dev;
 | 
			
		||||
 | 
			
		||||
        // Define samplerates
 | 
			
		||||
        samplerates.clear();
 | 
			
		||||
        samplerates.define(61440000, "61.44 MHz", 2);
 | 
			
		||||
        samplerates.define(122880000, "122.88 MHz", 1);
 | 
			
		||||
 | 
			
		||||
        // TODO: Load options
 | 
			
		||||
        srId = samplerates.keyId(61440000);
 | 
			
		||||
        bwId = bandwidths.nameId("Auto");
 | 
			
		||||
        gain = 0;
 | 
			
		||||
 | 
			
		||||
        // Update samplerate
 | 
			
		||||
        sampleRate = samplerates.key(srId);
 | 
			
		||||
 | 
			
		||||
        // Save serial number
 | 
			
		||||
        selectedSerial = serial;
 | 
			
		||||
        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) {
 | 
			
		||||
@@ -144,19 +249,6 @@ private:
 | 
			
		||||
        // Open the device
 | 
			
		||||
        _this->openDev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
 | 
			
		||||
 | 
			
		||||
        // Configure the device
 | 
			
		||||
        _this->openDev->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
 | 
			
		||||
        _this->openDev->librfnm_s->rx.ch[0].samp_freq_div_n = _this->samplerates[_this->srId];
 | 
			
		||||
        _this->openDev->librfnm_s->rx.ch[0].freq = _this->freq;
 | 
			
		||||
        _this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
 | 
			
		||||
        _this->openDev->librfnm_s->rx.ch[0].rfic_lpf_bw = 100;
 | 
			
		||||
        _this->openDev->librfnm_s->rx.ch[0].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
 | 
			
		||||
        _this->openDev->librfnm_s->rx.ch[0].path = _this->openDev->librfnm_s->rx.ch[0].path_preferred;
 | 
			
		||||
        rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
 | 
			
		||||
        if (fail != rfnm_api_failcode::RFNM_API_OK) {
 | 
			
		||||
            flog::error("Failed to configure device: {}", (int)fail);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Configure the stream
 | 
			
		||||
        _this->bufferSize = -1;
 | 
			
		||||
        _this->openDev->rx_stream(librfnm_stream_format::LIBRFNM_STREAM_FORMAT_CS16, &_this->bufferSize);
 | 
			
		||||
@@ -171,7 +263,24 @@ private:
 | 
			
		||||
            _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;
 | 
			
		||||
@@ -184,13 +293,20 @@ private:
 | 
			
		||||
        _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->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
 | 
			
		||||
        _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
 | 
			
		||||
        _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;
 | 
			
		||||
@@ -206,8 +322,8 @@ private:
 | 
			
		||||
    static void tune(double freq, void* ctx) {
 | 
			
		||||
        RFNMSourceModule* _this = (RFNMSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) {
 | 
			
		||||
            _this->openDev->librfnm_s->rx.ch[0].freq = freq;
 | 
			
		||||
            rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
 | 
			
		||||
            _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);
 | 
			
		||||
            }
 | 
			
		||||
@@ -244,11 +360,48 @@ private:
 | 
			
		||||
            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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -256,16 +409,16 @@ private:
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::SliderInt(CONCAT("##_rfnm_gain_", _this->name), &_this->gain, _this->gainMin, _this->gainMax)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                _this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
 | 
			
		||||
                rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
 | 
			
		||||
                _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->librfnm_s->rx.ch[0].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(LIBRFNM_APPLY_CH0_RX);
 | 
			
		||||
                _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
 | 
			
		||||
        }
 | 
			
		||||
@@ -274,12 +427,18 @@ private:
 | 
			
		||||
    void worker() {
 | 
			
		||||
        librfnm_rx_buf* lrxbuf;
 | 
			
		||||
        int sampCount = bufferSize/4;
 | 
			
		||||
        uint8_t ch = (1 << currentPath.chId);
 | 
			
		||||
 | 
			
		||||
        // TODO: Define number of buffers per swap to maintain 200 fps
 | 
			
		||||
        // 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; }
 | 
			
		||||
 | 
			
		||||
        while (true) {
 | 
			
		||||
        int count = 0;
 | 
			
		||||
        while (run) {
 | 
			
		||||
            // Receive a buffer
 | 
			
		||||
            auto fail = openDev->rx_dqbuf(&lrxbuf, LIBRFNM_CH0, 1000);
 | 
			
		||||
            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;
 | 
			
		||||
@@ -287,13 +446,17 @@ private:
 | 
			
		||||
            else if (fail) { break; }
 | 
			
		||||
 | 
			
		||||
            // Convert buffer to CF32
 | 
			
		||||
            volk_16i_s32f_convert_32f((float*)stream.writeBuf, (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
 | 
			
		||||
            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 (!stream.swap(sampCount)) { break; }
 | 
			
		||||
            if (count >= bufCount) {
 | 
			
		||||
                if (!stream.swap(count*sampCount)) { break; }
 | 
			
		||||
                count = 0;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        flog::debug("Worker exiting");
 | 
			
		||||
@@ -308,12 +471,15 @@ private:
 | 
			
		||||
    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;
 | 
			
		||||
@@ -321,8 +487,11 @@ private:
 | 
			
		||||
    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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -452,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
 | 
			
		||||
 
 | 
			
		||||
@@ -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