mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-11-04 02:39:11 +01:00 
			
		
		
		
	Compare commits
	
		
			46 Commits
		
	
	
		
			debug
			...
			fobos_test
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					17eccf5156 | ||
| 
						 | 
					acb1be121c | ||
| 
						 | 
					0fa89614bb | ||
| 
						 | 
					256affd918 | ||
| 
						 | 
					e80cdbf248 | ||
| 
						 | 
					75e66226c3 | ||
| 
						 | 
					c2f0e756a5 | ||
| 
						 | 
					79dd5bdcbb | ||
| 
						 | 
					6dce28345c | ||
| 
						 | 
					fe9ac6c9a1 | ||
| 
						 | 
					bfdfa2b30b | ||
| 
						 | 
					46e98b9b03 | ||
| 
						 | 
					e674a73771 | ||
| 
						 | 
					118e1fbff0 | ||
| 
						 | 
					bcadb36232 | ||
| 
						 | 
					554ba2f596 | ||
| 
						 | 
					949fde022d | ||
| 
						 | 
					123e34d250 | ||
| 
						 | 
					f1c7010437 | ||
| 
						 | 
					33a7795de1 | ||
| 
						 | 
					fe7299c18a | ||
| 
						 | 
					8a9e0abcc2 | ||
| 
						 | 
					13abe4860b | ||
| 
						 | 
					981592fa19 | ||
| 
						 | 
					582750f79b | ||
| 
						 | 
					36f2a083ce | ||
| 
						 | 
					d753135a61 | ||
| 
						 | 
					07744e5bae | ||
| 
						 | 
					9ec78da7ac | ||
| 
						 | 
					0066994899 | ||
| 
						 | 
					93ab51bf2f | ||
| 
						 | 
					f9d7d20073 | ||
| 
						 | 
					e81db5d85c | ||
| 
						 | 
					5c3a66642b | ||
| 
						 | 
					36492e799a | ||
| 
						 | 
					0110dfbef6 | ||
| 
						 | 
					0b5a2ff786 | ||
| 
						 | 
					ce0f1f05ae | ||
| 
						 | 
					46a5ff8ac5 | ||
| 
						 | 
					03559b928b | ||
| 
						 | 
					206ce6e8c3 | ||
| 
						 | 
					d7a1f46af0 | ||
| 
						 | 
					89e6e4f7ad | ||
| 
						 | 
					6ced9b15c3 | ||
| 
						 | 
					0de189a7b7 | ||
| 
						 | 
					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,7 +15,9 @@ 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_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
 | 
			
		||||
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
 | 
			
		||||
@@ -42,12 +44,14 @@ option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: port
 | 
			
		||||
 | 
			
		||||
# Decoders
 | 
			
		||||
option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF)
 | 
			
		||||
option(OPT_BUILD_DAB_DECODER "Build the DAB/DAB+ decoder (no dependencies required)" OFF)
 | 
			
		||||
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
 | 
			
		||||
option(OPT_BUILD_KG_SSTV_DECODER "Build the KG SSTV (KG-STV) decoder module (no dependencies required)" OFF)
 | 
			
		||||
option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (Dependencies: codec2)" OFF)
 | 
			
		||||
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
 | 
			
		||||
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON)
 | 
			
		||||
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
 | 
			
		||||
option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF)
 | 
			
		||||
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
 | 
			
		||||
 | 
			
		||||
# Misc
 | 
			
		||||
@@ -63,6 +67,7 @@ option(OPT_BUILD_SCHEDULER "Build the scheduler" OFF)
 | 
			
		||||
# Other options
 | 
			
		||||
option(USE_INTERNAL_LIBCORRECT "Use an internal version of libcorrect" ON)
 | 
			
		||||
option(USE_BUNDLE_DEFAULTS "Set the default resource and module directories to the right ones for a MacOS .app" OFF)
 | 
			
		||||
option(COPY_MSVC_REDISTRIBUTABLES "Copy over the Visual C++ Redistributable" OFF)
 | 
			
		||||
 | 
			
		||||
# Module cmake path
 | 
			
		||||
set(SDRPP_MODULE_CMAKE "${CMAKE_SOURCE_DIR}/sdrpp_module.cmake")
 | 
			
		||||
@@ -139,10 +144,18 @@ if (OPT_BUILD_FILE_SOURCE)
 | 
			
		||||
add_subdirectory("source_modules/file_source")
 | 
			
		||||
endif (OPT_BUILD_FILE_SOURCE)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_FOBOSSDR_SOURCE)
 | 
			
		||||
add_subdirectory("source_modules/fobossdr_source")
 | 
			
		||||
endif (OPT_BUILD_FOBOSSDR_SOURCE)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_HACKRF_SOURCE)
 | 
			
		||||
add_subdirectory("source_modules/hackrf_source")
 | 
			
		||||
endif (OPT_BUILD_HACKRF_SOURCE)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_HAROGIC_SOURCE)
 | 
			
		||||
add_subdirectory("source_modules/harogic_source")
 | 
			
		||||
endif (OPT_BUILD_HAROGIC_SOURCE)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_HERMES_SOURCE)
 | 
			
		||||
add_subdirectory("source_modules/hermes_source")
 | 
			
		||||
endif (OPT_BUILD_HERMES_SOURCE)
 | 
			
		||||
@@ -235,6 +248,10 @@ if (OPT_BUILD_ATV_DECODER)
 | 
			
		||||
add_subdirectory("decoder_modules/atv_decoder")
 | 
			
		||||
endif (OPT_BUILD_ATV_DECODER)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_DAB_DECODER)
 | 
			
		||||
add_subdirectory("decoder_modules/dab_decoder")
 | 
			
		||||
endif (OPT_BUILD_DAB_DECODER)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_FALCON9_DECODER)
 | 
			
		||||
add_subdirectory("decoder_modules/falcon9_decoder")
 | 
			
		||||
endif (OPT_BUILD_FALCON9_DECODER)
 | 
			
		||||
@@ -259,6 +276,10 @@ if (OPT_BUILD_RADIO)
 | 
			
		||||
add_subdirectory("decoder_modules/radio")
 | 
			
		||||
endif (OPT_BUILD_RADIO)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_RYFI_DECODER)
 | 
			
		||||
add_subdirectory("decoder_modules/ryfi_decoder")
 | 
			
		||||
endif (OPT_BUILD_RYFI_DECODER)
 | 
			
		||||
 | 
			
		||||
if (OPT_BUILD_WEATHER_SAT_DECODER)
 | 
			
		||||
add_subdirectory("decoder_modules/weather_sat_decoder")
 | 
			
		||||
endif (OPT_BUILD_WEATHER_SAT_DECODER)
 | 
			
		||||
@@ -312,6 +333,21 @@ target_compile_options(sdrpp PRIVATE ${SDRPP_COMPILER_FLAGS})
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
 | 
			
		||||
    add_custom_target(do_always_volk ALL xcopy /s \"C:/Program Files/PothosSDR/bin\\volk.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
 | 
			
		||||
 | 
			
		||||
    if (COPY_MSVC_REDISTRIBUTABLES)
 | 
			
		||||
        # Get the list of Visual C++ runtime DLLs
 | 
			
		||||
        set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP True)
 | 
			
		||||
        include(InstallRequiredSystemLibraries)
 | 
			
		||||
 | 
			
		||||
        # Create a space sperated list
 | 
			
		||||
        set(REDIST_DLLS_STR "")
 | 
			
		||||
        foreach(DLL IN LISTS CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
 | 
			
		||||
            set(REDIST_DLLS_STR COMMAND xcopy /F \"${DLL}\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y ${REDIST_DLLS_STR})
 | 
			
		||||
        endforeach()
 | 
			
		||||
        
 | 
			
		||||
        # Create target
 | 
			
		||||
        add_custom_target(do_always_msvc ALL ${REDIST_DLLS_STR})
 | 
			
		||||
    endif ()
 | 
			
		||||
endif ()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -173,16 +173,22 @@ int sdrpp_main(int argc, char* argv[]) {
 | 
			
		||||
    defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["File Source"]["module"] = "file_source";
 | 
			
		||||
    defConfig["moduleInstances"]["File Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["FobosSDR Source"]["module"] = "fobossdr_source";
 | 
			
		||||
    defConfig["moduleInstances"]["FobosSDR Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["HackRF Source"]["module"] = "hackrf_source";
 | 
			
		||||
    defConfig["moduleInstances"]["HackRF Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["Harogic Source"]["module"] = "harogic_source";
 | 
			
		||||
    defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["Hermes Source"]["module"] = "hermes_source";
 | 
			
		||||
    defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
 | 
			
		||||
    defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
 | 
			
		||||
    defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
 | 
			
		||||
    defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
 | 
			
		||||
    defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["RFNM Source"]["module"] = "rfnm_source";
 | 
			
		||||
    defConfig["moduleInstances"]["RFNM Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
 | 
			
		||||
    defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";
 | 
			
		||||
@@ -193,8 +199,12 @@ int sdrpp_main(int argc, char* argv[]) {
 | 
			
		||||
    defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["SDR++ Server Source"]["module"] = "sdrpp_server_source";
 | 
			
		||||
    defConfig["moduleInstances"]["SDR++ Server Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["Spectran HTTP Source"]["module"] = "spectran_http_source";
 | 
			
		||||
    defConfig["moduleInstances"]["Spectran HTTP Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["SpyServer Source"]["module"] = "spyserver_source";
 | 
			
		||||
    defConfig["moduleInstances"]["SpyServer Source"]["enabled"] = true;
 | 
			
		||||
    defConfig["moduleInstances"]["USRP Source"]["module"] = "usrp_source";
 | 
			
		||||
    defConfig["moduleInstances"]["USRP Source"]["enabled"] = true;
 | 
			
		||||
 | 
			
		||||
    defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
 | 
			
		||||
    defConfig["moduleInstances"]["Network Sink"] = "network_sink";
 | 
			
		||||
 
 | 
			
		||||
@@ -37,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>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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,13 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/fobossdr_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfspace_source/rfspace_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_sdr_source/rtl_sdr_source.dylib
 | 
			
		||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_tcp_source/rtl_tcp_source.dylib
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,9 @@ cp 'C:/Program Files/PothosSDR/bin/bladeRF.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/file_source/Release/file_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/fobossdr_source/Release/fobossdr_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp 'C:/Program Files/RigExpert/Fobos/bin/fobos.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/hackrf_source/Release/hackrf_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +42,11 @@ cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_w
 | 
			
		||||
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
 | 
			
		||||
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/rfnm_source/Release/rfnm_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
cp 'C:/Program Files/RFNM/bin/rfnm.dll' sdrpp_windows_x64/
 | 
			
		||||
cp 'C:/Program Files/RFNM/bin/spdlog.dll' sdrpp_windows_x64/
 | 
			
		||||
cp 'C:/Program Files/RFNM/bin/fmt.dll' sdrpp_windows_x64/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/rfspace_source/Release/rfspace_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
 | 
			
		||||
cp $build_dir/source_modules/rtl_sdr_source/Release/rtl_sdr_source.dll sdrpp_windows_x64/modules/
 | 
			
		||||
 
 | 
			
		||||
@@ -249,7 +249,6 @@ private:
 | 
			
		||||
        }
 | 
			
		||||
        ImGui::Columns(1, CONCAT("EndRecorderModeColumns##_", _this->name), false);
 | 
			
		||||
        ImGui::EndGroup();
 | 
			
		||||
        if (_this->recording) { style::endDisabled(); }
 | 
			
		||||
 | 
			
		||||
        // Recording path
 | 
			
		||||
        if (_this->folderSelect.render("##_recorder_fold_" + _this->name)) {
 | 
			
		||||
@@ -284,8 +283,11 @@ private:
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_this->recording) { style::endDisabled(); }
 | 
			
		||||
 | 
			
		||||
        // Show additional audio options
 | 
			
		||||
        if (_this->recMode == RECORDER_MODE_AUDIO) {
 | 
			
		||||
            if (_this->recording) { style::beginDisabled(); }
 | 
			
		||||
            ImGui::LeftLabel("Stream");
 | 
			
		||||
            ImGui::FillWidth();
 | 
			
		||||
            if (ImGui::Combo(CONCAT("##_recorder_stream_", _this->name), &_this->streamId, _this->audioStreams.txt)) {
 | 
			
		||||
@@ -294,6 +296,7 @@ private:
 | 
			
		||||
                config.conf[_this->name]["audioStream"] = _this->audioStreams.key(_this->streamId);
 | 
			
		||||
                config.release(true);
 | 
			
		||||
            }
 | 
			
		||||
            if (_this->recording) { style::endDisabled(); }
 | 
			
		||||
 | 
			
		||||
            _this->updateAudioMeter(_this->audioLvl);
 | 
			
		||||
            ImGui::FillWidth();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								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,15 @@ Modules in beta are still included in releases for the most part but not enabled
 | 
			
		||||
| audio_source         | Working    | rtaudio           | OPT_BUILD_AUDIO_SOURCE         | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| bladerf_source       | Working    | libbladeRF        | OPT_BUILD_BLADERF_SOURCE       | ⛔              | ✅ (not Debian Buster) | ✅                         |
 | 
			
		||||
| file_source          | Working    | -                 | OPT_BUILD_FILE_SOURCE          | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| fobossdr_source      | Beta       | libfobos          | OPT_BUILD_FOBOSSDR_SOURCE      | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| hackrf_source        | Working    | libhackrf         | OPT_BUILD_HACKRF_SOURCE        | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| harogic_source       | Beta       | htra_api          | OPT_BUILD_HAROGIC_SOURCE       | ⛔              | ⛔                     | ✅                         |
 | 
			
		||||
| hermes_source        | Beta       | -                 | OPT_BUILD_HERMES_SOURCE        | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| limesdr_source       | Working    | liblimesuite      | OPT_BUILD_LIMESDR_SOURCE       | ⛔              | ✅                     | ✅                         |
 | 
			
		||||
| network_source       | Unfinished | -                 | OPT_BUILD_NETWORK_SOURCE       | ✅              | ✅                     | ⛔                         |
 | 
			
		||||
| perseus_source       | Beta       | libperseus-sdr    | OPT_BUILD_PERSEUS_SOURCE       | ⛔              | ✅                     | ✅                         |
 | 
			
		||||
| plutosdr_source      | Working    | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE      | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| rfnm_source          | Beta       | librfnm           | OPT_BUILD_RFNM_SOURCE          | ⛔              | ⛔                     | ⛔                         |
 | 
			
		||||
| 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 +340,9 @@ Modules in beta are still included in releases for the most part but not enabled
 | 
			
		||||
| sdrpp_server_source  | Working    | -                 | OPT_BUILD_SDRPP_SERVER_SOURCE  | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| soapy_source         | Deprecated | soapysdr          | OPT_BUILD_SOAPY_SOURCE         | ⛔              | ⛔                     | ⛔                         |
 | 
			
		||||
| spectran_source      | Unfinished | RTSA Suite        | OPT_BUILD_SPECTRAN_SOURCE      | ⛔              | ⛔                     | ⛔                         |
 | 
			
		||||
| spectran_http_source | Beta       | -                 | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅              | ✅                     | ⛔                         |
 | 
			
		||||
| spectran_http_source | Beta       | -                 | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| spyserver_source     | Working    | -                 | OPT_BUILD_SPYSERVER_SOURCE     | ✅              | ✅                     | ✅                         |
 | 
			
		||||
| usrp_source          | Beta       | libuhd            | OPT_BUILD_USRP_SOURCE          | ⛔              | ⛔                     | ⛔                         |
 | 
			
		||||
| usrp_source          | Beta       | libuhd            | OPT_BUILD_USRP_SOURCE          | ⛔              | ⛔                     | ✅                         |
 | 
			
		||||
 | 
			
		||||
## Sinks
 | 
			
		||||
 | 
			
		||||
@@ -358,6 +359,7 @@ Modules in beta are still included in releases for the most part but not enabled
 | 
			
		||||
| Name                | Stage      | Dependencies | Option                        | Built by default| Built in Release | Enabled in SDR++ by default |
 | 
			
		||||
|---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:|
 | 
			
		||||
| atv_decoder         | Unfinished | -            | OPT_BUILD_ATV_DECODER         | ⛔              | ⛔              | ⛔                         |
 | 
			
		||||
| dab_decoder         | Unfinished | -            | OPT_BUILD_DAB_DECODER         | ⛔              | ⛔              | ⛔                         |
 | 
			
		||||
| falcon9_decoder     | Unfinished | ffplay       | OPT_BUILD_FALCON9_DECODER     | ⛔              | ⛔              | ⛔                         |
 | 
			
		||||
| kgsstv_decoder      | Unfinished | -            | OPT_BUILD_KGSSTV_DECODER      | ⛔              | ⛔              | ⛔                         |
 | 
			
		||||
| m17_decoder         | Working    | -            | OPT_BUILD_M17_DECODER         | ⛔              | ✅              | ⛔                         |
 | 
			
		||||
 
 | 
			
		||||
@@ -2,8 +2,8 @@
 | 
			
		||||
    "name": "France",
 | 
			
		||||
    "country_name": "France",
 | 
			
		||||
    "country_code": "FR",
 | 
			
		||||
    "author_name": "Fred F4EED",
 | 
			
		||||
    "author_url": "http://f4eed.wordpress.com",
 | 
			
		||||
    "author_name": "Fred F4EED, Armand31",
 | 
			
		||||
    "author_url": "http://f4eed.wordpress.com, https://github.com/Armand31",
 | 
			
		||||
    "bands": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "137KHz - Radioamateur",
 | 
			
		||||
@@ -355,7 +355,7 @@
 | 
			
		||||
            "end": 54000000
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Bande FM - Radiodif.",
 | 
			
		||||
            "name": "Radiodiffusion - Bande FM",
 | 
			
		||||
            "type": "broadcast",
 | 
			
		||||
            "start": 80000000,
 | 
			
		||||
            "end": 108000000
 | 
			
		||||
@@ -396,6 +396,12 @@
 | 
			
		||||
            "start": 162362500,
 | 
			
		||||
            "end": 162587500
 | 
			
		||||
        },
 | 
			
		||||
	{
 | 
			
		||||
	    "name": "Radiodiffusion - Bande DAB",
 | 
			
		||||
	    "type": "broadcast",
 | 
			
		||||
	    "start": 174000000,
 | 
			
		||||
	    "end": 223000000
 | 
			
		||||
	},
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Military Aviation",
 | 
			
		||||
            "type": "military",
 | 
			
		||||
@@ -408,6 +414,12 @@
 | 
			
		||||
            "start": 240000000,
 | 
			
		||||
            "end": 270000000
 | 
			
		||||
        },
 | 
			
		||||
	{
 | 
			
		||||
	    "name": "Police (TETRAPOL)",
 | 
			
		||||
	    "type": "military",
 | 
			
		||||
 	    "start": 380000000,
 | 
			
		||||
	    "end": 400000000
 | 
			
		||||
	},
 | 
			
		||||
        {
 | 
			
		||||
            "name": "70cm - Radioamateur",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
@@ -420,12 +432,24 @@
 | 
			
		||||
            "start": 446000000,
 | 
			
		||||
            "end": 446200000
 | 
			
		||||
        },
 | 
			
		||||
	{
 | 
			
		||||
	    "name": "TNT (DVB-T)",
 | 
			
		||||
	    "type": "broadcast",
 | 
			
		||||
	    "start": 470000000,
 | 
			
		||||
	    "end": 694000000
 | 
			
		||||
	},
 | 
			
		||||
        {
 | 
			
		||||
            "name": "23cm - Radioamateur",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
            "start": 1240000000,
 | 
			
		||||
            "end": 1300000000
 | 
			
		||||
        },
 | 
			
		||||
	{
 | 
			
		||||
	    "name": "Radiodiffusion - Bande DAB",
 | 
			
		||||
	    "type": "broadcast",
 | 
			
		||||
	    "start": 1452000000,
 | 
			
		||||
	    "end": 1492000000
 | 
			
		||||
	},
 | 
			
		||||
        {
 | 
			
		||||
            "name": "13cm - Radioamateur",
 | 
			
		||||
            "type": "amateur",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										285
									
								
								root/res/bandplans/turkey.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								root/res/bandplans/turkey.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,285 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "Turkey",
 | 
			
		||||
  "country_name": "Turkey",
 | 
			
		||||
  "country_code": "TR",
 | 
			
		||||
  "author_name": "Yunus TA2PEA",
 | 
			
		||||
  "author_url": "https://github.com/ycanerol",
 | 
			
		||||
  "bands": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "LW",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 135700,
 | 
			
		||||
      "end": 137800
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "630m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 472000,
 | 
			
		||||
      "end": 479000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "160m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 1810000,
 | 
			
		||||
      "end": 1850000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "80m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 3500000,
 | 
			
		||||
      "end": 3800000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "60m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 5351500,
 | 
			
		||||
      "end": 5366500
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "40m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 7000000,
 | 
			
		||||
      "end": 7200000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "30m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 10100000,
 | 
			
		||||
      "end": 10150000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "20m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 14000000,
 | 
			
		||||
      "end": 14350000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "17m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 18068000,
 | 
			
		||||
      "end": 18168000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "15m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 21000000,
 | 
			
		||||
      "end": 21450000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "12m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 24890000,
 | 
			
		||||
      "end": 24990000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "CB",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 26565000,
 | 
			
		||||
      "end": 27405000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Pagers",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 27750000,
 | 
			
		||||
      "end": 28000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "10m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 28000000,
 | 
			
		||||
      "end": 29700000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "6m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 50030000,
 | 
			
		||||
      "end": 51000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "FM",
 | 
			
		||||
      "type": "broadcast",
 | 
			
		||||
      "start": 87500000,
 | 
			
		||||
      "end": 108000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Airband VOR/ILS",
 | 
			
		||||
      "type": "aviation",
 | 
			
		||||
      "start": 108000000,
 | 
			
		||||
      "end": 117975000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Airband Voice",
 | 
			
		||||
      "type": "aviation",
 | 
			
		||||
      "start": 117975000,
 | 
			
		||||
      "end": 137000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "2m",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 144000000,
 | 
			
		||||
      "end": 146000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Sayac Okuma",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 169400000,
 | 
			
		||||
      "end": 169475000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Pagers",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 167000000,
 | 
			
		||||
      "end": 167100000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Public announcement systems",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 173882500,
 | 
			
		||||
      "end": 174000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "DVB-T",
 | 
			
		||||
      "type": "broadcast",
 | 
			
		||||
      "start": 174000000,
 | 
			
		||||
      "end": 216000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "T-DAB",
 | 
			
		||||
      "type": "broadcast",
 | 
			
		||||
      "start": 216000000,
 | 
			
		||||
      "end": 233000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "ILS-Glide Path",
 | 
			
		||||
      "type": "aviation",
 | 
			
		||||
      "start": 328600000,
 | 
			
		||||
      "end": 335400000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Public Safety/Emergency",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 380000000,
 | 
			
		||||
      "end": 385000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Public Safety/Emergency",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 390000000,
 | 
			
		||||
      "end": 395000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "70cm",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 430200000,
 | 
			
		||||
      "end": 430700000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "70cm-RepeaterRX",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 431550000,
 | 
			
		||||
      "end": 431825000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "70cm",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 432000000,
 | 
			
		||||
      "end": 432975000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "70cm",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 433400000,
 | 
			
		||||
      "end": 434000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "70cm",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 435000000,
 | 
			
		||||
      "end": 438000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "70cm-RepeaterTX",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 439150000,
 | 
			
		||||
      "end": 439425000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "Public announcement systems",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 445250000,
 | 
			
		||||
      "end": 445462500
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "PMR446",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 446006250,
 | 
			
		||||
      "end": 446196875
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "RFID",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 865000000,
 | 
			
		||||
      "end": 868000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "RFID",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 916100000,
 | 
			
		||||
      "end": 918900000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "23cm",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 1240000000,
 | 
			
		||||
      "end": 1300000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "DECT",
 | 
			
		||||
      "type": "other",
 | 
			
		||||
      "start": 1880000000,
 | 
			
		||||
      "end": 1900000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "5GHz",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 5650000000,
 | 
			
		||||
      "end": 5670000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "5GHz",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 5820000000,
 | 
			
		||||
      "end": 5850000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "3cm",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 104500000000,
 | 
			
		||||
      "end": 104520000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "24GHz",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 24000000000,
 | 
			
		||||
      "end": 24050000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "47GHz",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 47000000000,
 | 
			
		||||
      "end": 47200000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "75GHz",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 75500000000,
 | 
			
		||||
      "end": 7600000000
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "134GHz",
 | 
			
		||||
      "type": "amateur",
 | 
			
		||||
      "start": 134000000000,
 | 
			
		||||
      "end": 142000000000
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -217,14 +217,19 @@ private:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void startServer() {
 | 
			
		||||
        if (modeId == SINK_MODE_TCP) {
 | 
			
		||||
            listener = net::listen(hostname, port);
 | 
			
		||||
            if (listener) {
 | 
			
		||||
                listener->acceptAsync(clientHandler, this);
 | 
			
		||||
        try {
 | 
			
		||||
            if (modeId == SINK_MODE_TCP) {
 | 
			
		||||
                listener = net::listen(hostname, port);
 | 
			
		||||
                if (listener) {
 | 
			
		||||
                    listener->acceptAsync(clientHandler, this);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                conn = net::openUDP("0.0.0.0", port, hostname, port, false);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            conn = net::openUDP("0.0.0.0", port, hostname, port, false);
 | 
			
		||||
        catch (const std::exception& e) {
 | 
			
		||||
            flog::error("Failed to open socket: {}", e.what());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								source_modules/badgesdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								source_modules/badgesdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(badgesdr_source)
 | 
			
		||||
 | 
			
		||||
file(GLOB SRC "src/*.cpp")
 | 
			
		||||
 | 
			
		||||
include(${SDRPP_MODULE_CMAKE})
 | 
			
		||||
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    find_package(libusb CONFIG REQUIRED)
 | 
			
		||||
    target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS})
 | 
			
		||||
    target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES})
 | 
			
		||||
elseif (ANDROID)
 | 
			
		||||
    target_link_libraries(badgesdr_source PUBLIC
 | 
			
		||||
        /sdr-kit/${ANDROID_ABI}/lib/libusb1.0.so
 | 
			
		||||
        /sdr-kit/${ANDROID_ABI}/lib/librtlsdr.so
 | 
			
		||||
    )
 | 
			
		||||
else (MSVC)
 | 
			
		||||
    find_package(PkgConfig)
 | 
			
		||||
 | 
			
		||||
    pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
 | 
			
		||||
 | 
			
		||||
    target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS})
 | 
			
		||||
    target_link_directories(badgesdr_source PRIVATE ${LIBUSB_LIBRARY_DIRS})
 | 
			
		||||
    target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES})
 | 
			
		||||
endif ()
 | 
			
		||||
							
								
								
									
										309
									
								
								source_modules/badgesdr_source/src/badgesdr.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										309
									
								
								source_modules/badgesdr_source/src/badgesdr.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,309 @@
 | 
			
		||||
#include "badgesdr.h"
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <utils/flog.h>
 | 
			
		||||
 | 
			
		||||
#define R820T_I2C_ADDR  0x1A
 | 
			
		||||
 | 
			
		||||
namespace BadgeSDR {
 | 
			
		||||
    enum Commands {
 | 
			
		||||
        CMD_I2C_RW,
 | 
			
		||||
        CMD_I2C_STATUS,
 | 
			
		||||
        CMD_ADC_START,
 | 
			
		||||
        CMD_ADC_STOP,
 | 
			
		||||
        CMD_ADC_GET_SAMP_COUNT
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    libusb_context* ctx = NULL;
 | 
			
		||||
 | 
			
		||||
    bool DeviceInfo::operator==(const DeviceInfo& b) const {
 | 
			
		||||
        return serialNumber == b.serialNumber;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::write_reg(uint8_t reg, uint8_t value, void* ctx) {
 | 
			
		||||
        Device* dev = (Device*)ctx;
 | 
			
		||||
        dev->writeR820TReg(reg, value);
 | 
			
		||||
        dev->writeR820TReg(0x1F, 0); // TODO: Figure out why this is needed
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::read_reg(uint8_t* data, int len, void* ctx) {
 | 
			
		||||
        Device* dev = (Device*)ctx;
 | 
			
		||||
        dev->readI2C(R820T_I2C_ADDR, data, len);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Device::Device(libusb_device_handle* dev) {
 | 
			
		||||
        // Save device handle
 | 
			
		||||
        this->dev = dev;
 | 
			
		||||
 | 
			
		||||
        // Init tuner
 | 
			
		||||
        r820t = {
 | 
			
		||||
            16000000, // xtal_freq => 16MHz
 | 
			
		||||
            3000000, // Set at boot to airspy_m0_m4_conf_t conf0 -> r820t_if_freq
 | 
			
		||||
            100000000, /* Default Freq 100Mhz */
 | 
			
		||||
            {
 | 
			
		||||
                /* 05 */ 0x9F, // LNA manual gain mode, init to 0
 | 
			
		||||
                /* 06 */ 0x80,
 | 
			
		||||
                /* 07 */ 0x60,
 | 
			
		||||
                /* 08 */ 0x80, // Image Gain Adjustment
 | 
			
		||||
                /* 09 */ 0x40, // Image Phase Adjustment
 | 
			
		||||
                /* 0A */ 0xA8, // Channel filter [0..3]: 0 = widest, f = narrowest - Optimal. Don't touch!
 | 
			
		||||
                /* 0B */ 0x0F, // High pass filter - Optimal. Don't touch!
 | 
			
		||||
                /* 0C */ 0x4F, // VGA control by code, init at 0
 | 
			
		||||
                /* 0D */ 0x63, // LNA AGC settings: [0..3]: Lower threshold; [4..7]: High threshold
 | 
			
		||||
                /* 0E */ 0x75,
 | 
			
		||||
                /* 0F */ 0xE8, // Filter Widest, LDO_5V OFF, clk out ON,
 | 
			
		||||
                /* 10 */ 0x7C,
 | 
			
		||||
                /* 11 */ 0x42,
 | 
			
		||||
                /* 12 */ 0x06,
 | 
			
		||||
                /* 13 */ 0x00,
 | 
			
		||||
                /* 14 */ 0x0F,
 | 
			
		||||
                /* 15 */ 0x00,
 | 
			
		||||
                /* 16 */ 0xC0,
 | 
			
		||||
                /* 17 */ 0xA0,
 | 
			
		||||
                /* 18 */ 0x48,
 | 
			
		||||
                /* 19 */ 0xCC,
 | 
			
		||||
                /* 1A */ 0x60,
 | 
			
		||||
                /* 1B */ 0x00,
 | 
			
		||||
                /* 1C */ 0x54,
 | 
			
		||||
                /* 1D */ 0xAE,
 | 
			
		||||
                /* 1E */ 0x0A,
 | 
			
		||||
                /* 1F */ 0xC0
 | 
			
		||||
            },
 | 
			
		||||
            0 /* uint16_t padding */
 | 
			
		||||
        };
 | 
			
		||||
        r820t_init(&r820t, 3000000, write_reg, read_reg, this);
 | 
			
		||||
        r820t_set_mixer_gain(&r820t, 15);
 | 
			
		||||
        r820t_set_vga_gain(&r820t, 15);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Device::~Device() {
 | 
			
		||||
        // Release the bulk interface
 | 
			
		||||
        libusb_release_interface(dev, 0);
 | 
			
		||||
 | 
			
		||||
        // Close device
 | 
			
		||||
        libusb_close(dev);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::setFrequency(double freq) {
 | 
			
		||||
        r820t_set_freq(&r820t, freq - 2125000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::setLNAGain(int gain) {
 | 
			
		||||
        r820t_set_lna_gain(&r820t, gain);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::setMixerGain(int gain) {
 | 
			
		||||
        r820t_set_mixer_gain(&r820t, gain);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::setVGAGain(int gain) {
 | 
			
		||||
        r820t_set_vga_gain(&r820t, gain);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void Device::start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx, int minBufferSize) {
 | 
			
		||||
        // Do nothing if already running
 | 
			
		||||
        if (run) { return; }
 | 
			
		||||
 | 
			
		||||
        // Save handler
 | 
			
		||||
        this->callback = callback;
 | 
			
		||||
        this->ctx = ctx;
 | 
			
		||||
 | 
			
		||||
        // Compute buffer size
 | 
			
		||||
        int bufCount = minBufferSize / 64;
 | 
			
		||||
        if (minBufferSize % 64) { bufCount++; }
 | 
			
		||||
        bufferSize = bufCount * 64;
 | 
			
		||||
 | 
			
		||||
        // Mark as running
 | 
			
		||||
        run = true;
 | 
			
		||||
 | 
			
		||||
        // Start the ADC
 | 
			
		||||
        startADC();
 | 
			
		||||
 | 
			
		||||
        // Start worker thread
 | 
			
		||||
        workerThread = std::thread(&Device::worker, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::stop() {
 | 
			
		||||
        // Do nothing if already stopped
 | 
			
		||||
        if (!run) { return; }
 | 
			
		||||
 | 
			
		||||
        // Mark as stopped
 | 
			
		||||
        run = false;
 | 
			
		||||
 | 
			
		||||
        // Wait for the worker to exit
 | 
			
		||||
        if (workerThread.joinable()) { workerThread.join(); }
 | 
			
		||||
 | 
			
		||||
        // Stop the ADC
 | 
			
		||||
        stopADC();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Device::getI2CStatus() {
 | 
			
		||||
        int status;
 | 
			
		||||
        int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
 | 
			
		||||
        if (ret <= 0 || status < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        return status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Device::readI2C(uint8_t addr, uint8_t* data, int len) {
 | 
			
		||||
        // Do read
 | 
			
		||||
        int bytes = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_RW, 0, addr, data, len, 1000);
 | 
			
		||||
        if (bytes < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get status (TODO: Use function)
 | 
			
		||||
        int status;
 | 
			
		||||
        int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
 | 
			
		||||
        if (ret <= 0 || status < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return bytes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Device::writeI2C(uint8_t addr, const uint8_t* data, int len) {
 | 
			
		||||
        // Do write
 | 
			
		||||
        int bytes = libusb_control_transfer(dev, (2 << 5), CMD_I2C_RW, 0, addr, (uint8_t*)data, len, 1000);
 | 
			
		||||
        if (bytes < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get status (TODO: Use function)
 | 
			
		||||
        int status;
 | 
			
		||||
        int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
 | 
			
		||||
        if (ret <= 0 || status < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return bytes;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t bitrev(uint8_t val) {
 | 
			
		||||
        return ((val & 0x01) << 7) | ((val & 0x02) << 5) | ((val & 0x04) << 3) | ((val & 0x08) << 1) | ((val & 0x10) >> 1) | ((val & 0x20) >> 3) | ((val & 0x40) >> 5) | ((val & 0x80) >> 7);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    uint8_t Device::readR820TReg(uint8_t reg) {
 | 
			
		||||
        // Read registers up to it (can't read single)
 | 
			
		||||
        uint8_t regs[0x20];
 | 
			
		||||
        readI2C(R820T_I2C_ADDR, regs, reg+1);
 | 
			
		||||
 | 
			
		||||
        // Invert bit order
 | 
			
		||||
        return bitrev(regs[reg]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::writeR820TReg(uint8_t reg, uint8_t val) {
 | 
			
		||||
        // Write register id then value
 | 
			
		||||
        uint8_t cmd[2] = { reg, val };
 | 
			
		||||
        writeI2C(R820T_I2C_ADDR, cmd, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Device::startADC() {
 | 
			
		||||
        return libusb_control_transfer(dev, (2 << 5), CMD_ADC_START, 0, 0, NULL, 0, 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int Device::stopADC() {
 | 
			
		||||
        return libusb_control_transfer(dev, (2 << 5), CMD_ADC_STOP, 0, 0, NULL, 0, 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Device::worker() {
 | 
			
		||||
        // Allocate sample buffer
 | 
			
		||||
        uint8_t* buffer = new uint8_t[bufferSize];
 | 
			
		||||
        int sampleCount = 0;
 | 
			
		||||
 | 
			
		||||
        while (run) {
 | 
			
		||||
            // Receive data with bulk transfer
 | 
			
		||||
            int recvLen = 0;
 | 
			
		||||
            int val = libusb_bulk_transfer(dev, LIBUSB_ENDPOINT_IN | 1, &buffer[sampleCount], bufferSize - sampleCount, &recvLen, 1000);
 | 
			
		||||
            
 | 
			
		||||
            // If timed out, try again. Otherwise, if an error occur, stop the thread
 | 
			
		||||
            if (val == LIBUSB_ERROR_TIMEOUT) {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            else if (val) {
 | 
			
		||||
                flog::error("USB Error: {}", val);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Increment sample count
 | 
			
		||||
            if (recvLen) { sampleCount += recvLen; }
 | 
			
		||||
 | 
			
		||||
            // If the buffer is full, call handler and reset sample count
 | 
			
		||||
            if (sampleCount >= bufferSize) {
 | 
			
		||||
                callback(buffer, sampleCount, ctx);
 | 
			
		||||
                sampleCount = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Free buffer
 | 
			
		||||
        delete[] buffer;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<DeviceInfo> list() {
 | 
			
		||||
        // Init libusb if done yet
 | 
			
		||||
        if (!ctx) {
 | 
			
		||||
            libusb_init(&ctx);
 | 
			
		||||
            libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_WARNING);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // List devices
 | 
			
		||||
        std::vector<DeviceInfo> devList;
 | 
			
		||||
        libusb_device** devices;
 | 
			
		||||
        int devCount = libusb_get_device_list(ctx, &devices);
 | 
			
		||||
        for (int i = 0; i < devCount; i++) {
 | 
			
		||||
            // Get device info
 | 
			
		||||
            DeviceInfo devInfo;
 | 
			
		||||
            devInfo.dev = devices[i];
 | 
			
		||||
            libusb_device_descriptor desc;
 | 
			
		||||
            libusb_get_device_descriptor(devInfo.dev, &desc);
 | 
			
		||||
 | 
			
		||||
            // Check the VID/PID and give up if not the right ones
 | 
			
		||||
            if (desc.idVendor != 0xCAFE || desc.idProduct != 0x4010) {
 | 
			
		||||
                libusb_unref_device(devInfo.dev);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Open devices
 | 
			
		||||
            libusb_device_handle* openDev;
 | 
			
		||||
            int err = libusb_open(devInfo.dev, &openDev);
 | 
			
		||||
            if (err) {
 | 
			
		||||
                libusb_unref_device(devInfo.dev);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Get serial number
 | 
			
		||||
            char serial[128];
 | 
			
		||||
            libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, (uint8_t*)serial, sizeof(serial));
 | 
			
		||||
            devInfo.serialNumber = serial;
 | 
			
		||||
 | 
			
		||||
            // TODO: The libusb device should be unreffed but would need to know when the list disappears
 | 
			
		||||
 | 
			
		||||
            // Close device
 | 
			
		||||
            libusb_close(openDev);
 | 
			
		||||
 | 
			
		||||
            // Add to list
 | 
			
		||||
            devList.push_back(devInfo);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Return devices
 | 
			
		||||
        return devList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Device> open(const DeviceInfo& dev) {
 | 
			
		||||
        // Open device
 | 
			
		||||
        libusb_device_handle* openDev;
 | 
			
		||||
        int err = libusb_open(dev.dev, &openDev);
 | 
			
		||||
        if (err) {
 | 
			
		||||
            throw std::runtime_error("Failed to open device");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Claim the bulk transfer interface
 | 
			
		||||
        if (libusb_claim_interface(openDev, 0)) {
 | 
			
		||||
            throw std::runtime_error("Failed to claim bulk interface");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Create device
 | 
			
		||||
        return std::make_shared<Device>(openDev);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										53
									
								
								source_modules/badgesdr_source/src/badgesdr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								source_modules/badgesdr_source/src/badgesdr.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <libusb.h>
 | 
			
		||||
#include "r820t.h"
 | 
			
		||||
 | 
			
		||||
namespace BadgeSDR {
 | 
			
		||||
    struct DeviceInfo {
 | 
			
		||||
        std::string serialNumber;
 | 
			
		||||
        libusb_device* dev;
 | 
			
		||||
        bool operator==(const DeviceInfo& b) const;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    class Device {
 | 
			
		||||
    public:
 | 
			
		||||
        Device(libusb_device_handle* dev);
 | 
			
		||||
        ~Device();
 | 
			
		||||
 | 
			
		||||
        void setFrequency(double freq);
 | 
			
		||||
        void setLNAGain(int gain);
 | 
			
		||||
        void setMixerGain(int gain);
 | 
			
		||||
        void setVGAGain(int gain);
 | 
			
		||||
        
 | 
			
		||||
        void start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx = NULL, int minBufferSize = 2500);
 | 
			
		||||
        void stop();
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        int getI2CStatus();
 | 
			
		||||
        int readI2C(uint8_t addr, uint8_t* data, int len);
 | 
			
		||||
        int writeI2C(uint8_t addr, const uint8_t* data, int len);
 | 
			
		||||
        uint8_t readR820TReg(uint8_t reg);
 | 
			
		||||
        void writeR820TReg(uint8_t reg, uint8_t val);
 | 
			
		||||
        int startADC();
 | 
			
		||||
        int stopADC();
 | 
			
		||||
        void worker();
 | 
			
		||||
 | 
			
		||||
        libusb_device_handle* dev;
 | 
			
		||||
        std::thread workerThread;
 | 
			
		||||
        bool run = false;
 | 
			
		||||
        int bufferSize = 0; // Must be multiple of 64 for best performance
 | 
			
		||||
        void* ctx = NULL;
 | 
			
		||||
        void (*callback)(const uint8_t* samples, int count, void* ctx);
 | 
			
		||||
 | 
			
		||||
        static void write_reg(uint8_t reg, uint8_t value, void* ctx);
 | 
			
		||||
        static void read_reg(uint8_t* data, int len, void* ctx);
 | 
			
		||||
        r820t_priv_t r820t;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::vector<DeviceInfo> list();
 | 
			
		||||
    std::shared_ptr<Device> open(const DeviceInfo& dev);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										272
									
								
								source_modules/badgesdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								source_modules/badgesdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,272 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <gui/smgui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
#include <utils/optionlist.h>
 | 
			
		||||
#include <dsp/channel/rx_vfo.h>
 | 
			
		||||
#include <dsp/correction/dc_blocker.h>
 | 
			
		||||
#include "badgesdr.h"
 | 
			
		||||
 | 
			
		||||
SDRPP_MOD_INFO{
 | 
			
		||||
    /* Name:            */ "badgesdr_source",
 | 
			
		||||
    /* Description:     */ "BadgeSDR Source Module",
 | 
			
		||||
    /* Author:          */ "Ryzerth",
 | 
			
		||||
    /* Version:         */ 0, 1, 0,
 | 
			
		||||
    /* Max instances    */ -1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
 | 
			
		||||
 | 
			
		||||
class BadgeSDRSourceModule : public ModuleManager::Instance {
 | 
			
		||||
public:
 | 
			
		||||
    BadgeSDRSourceModule(std::string name) {
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        sampleRate = 250000.0;
 | 
			
		||||
 | 
			
		||||
        // Initialize DSP
 | 
			
		||||
        dcBlock.init(&input, 0.001);
 | 
			
		||||
        ddc.init(&dcBlock.out, 500000, 250000, 250000, 125000);
 | 
			
		||||
 | 
			
		||||
        handler.ctx = this;
 | 
			
		||||
        handler.selectHandler = menuSelected;
 | 
			
		||||
        handler.deselectHandler = menuDeselected;
 | 
			
		||||
        handler.menuHandler = menuHandler;
 | 
			
		||||
        handler.startHandler = start;
 | 
			
		||||
        handler.stopHandler = stop;
 | 
			
		||||
        handler.tuneHandler = tune;
 | 
			
		||||
        handler.stream = &ddc.out;
 | 
			
		||||
 | 
			
		||||
        // Refresh devices
 | 
			
		||||
        refresh();
 | 
			
		||||
 | 
			
		||||
        // Select first (TODO: Select from config)
 | 
			
		||||
        select("");
 | 
			
		||||
 | 
			
		||||
        sigpath::sourceManager.registerSource("BadgeSDR", &handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~BadgeSDRSourceModule() {
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void postInit() {}
 | 
			
		||||
 | 
			
		||||
    void enable() {
 | 
			
		||||
        enabled = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void disable() {
 | 
			
		||||
        enabled = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isEnabled() {
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void refresh() {
 | 
			
		||||
        devices.clear();
 | 
			
		||||
        auto list = BadgeSDR::list();
 | 
			
		||||
        for (const auto& info : list) {
 | 
			
		||||
            // Format device name
 | 
			
		||||
            std::string devName = "BadgeSDR ";
 | 
			
		||||
            devName += " [";
 | 
			
		||||
            devName += info.serialNumber;
 | 
			
		||||
            devName += ']';
 | 
			
		||||
 | 
			
		||||
            // Save device
 | 
			
		||||
            devices.define(info.serialNumber, devName, info);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void select(const std::string& serial) {
 | 
			
		||||
        // If there are no devices, give up
 | 
			
		||||
        if (devices.empty()) {
 | 
			
		||||
            selectedSerial.clear();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If the serial was not found, select the first available serial
 | 
			
		||||
        if (!devices.keyExists(serial)) {
 | 
			
		||||
            select(devices.key(0));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Save serial number
 | 
			
		||||
        selectedSerial = serial;
 | 
			
		||||
        selectedDev = devices.value(devices.keyId(serial));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuSelected(void* ctx) {
 | 
			
		||||
        BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
 | 
			
		||||
        core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
        flog::info("BadgeSDRSourceModule '{0}': Menu Select!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuDeselected(void* ctx) {
 | 
			
		||||
        BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
 | 
			
		||||
        flog::info("BadgeSDRSourceModule '{0}': Menu Deselect!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void start(void* ctx) {
 | 
			
		||||
        BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) { return; }
 | 
			
		||||
 | 
			
		||||
        // Open the device
 | 
			
		||||
        _this->openDev = BadgeSDR::open(_this->selectedDev);
 | 
			
		||||
 | 
			
		||||
        // Configure the device
 | 
			
		||||
        _this->openDev->setFrequency(_this->freq);
 | 
			
		||||
        _this->openDev->setLNAGain(_this->lnaGain);
 | 
			
		||||
        _this->openDev->setMixerGain(_this->mixerGain);
 | 
			
		||||
        _this->openDev->setVGAGain(_this->vgaGain);
 | 
			
		||||
 | 
			
		||||
        // Start DSP
 | 
			
		||||
        _this->dcBlock.start();
 | 
			
		||||
        _this->ddc.start();
 | 
			
		||||
 | 
			
		||||
        // Start device
 | 
			
		||||
        _this->openDev->start(callback, _this, 500000/200);
 | 
			
		||||
        
 | 
			
		||||
        _this->running = true;
 | 
			
		||||
        flog::info("BadgeSDRSourceModule '{0}': Start!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void stop(void* ctx) {
 | 
			
		||||
        BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
 | 
			
		||||
        if (!_this->running) { return; }
 | 
			
		||||
        _this->running = false;
 | 
			
		||||
        
 | 
			
		||||
        // Stop worker
 | 
			
		||||
        _this->openDev->stop();
 | 
			
		||||
 | 
			
		||||
        // Stop DSP
 | 
			
		||||
        _this->dcBlock.stop();
 | 
			
		||||
        _this->ddc.stop();
 | 
			
		||||
 | 
			
		||||
        // Close device
 | 
			
		||||
        _this->openDev.reset();
 | 
			
		||||
 | 
			
		||||
        flog::info("BadgeSDRSourceModule '{0}': Stop!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void tune(double freq, void* ctx) {
 | 
			
		||||
        BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) {
 | 
			
		||||
            _this->openDev->setFrequency(freq);
 | 
			
		||||
        }
 | 
			
		||||
        _this->freq = freq;
 | 
			
		||||
        flog::info("BadgeSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuHandler(void* ctx) {
 | 
			
		||||
        BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
 | 
			
		||||
        
 | 
			
		||||
        if (_this->running) { SmGui::BeginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        SmGui::ForceSync();
 | 
			
		||||
        if (SmGui::Combo(CONCAT("##_badgesdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
 | 
			
		||||
            _this->select(_this->devices.key(_this->devId));
 | 
			
		||||
            core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
            // TODO: Save
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        SmGui::ForceSync();
 | 
			
		||||
        if (SmGui::Button(CONCAT("Refresh##_badgesdr_refr_", _this->name))) {
 | 
			
		||||
            _this->refresh();
 | 
			
		||||
            _this->select(_this->selectedSerial);
 | 
			
		||||
            core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_this->running) { SmGui::EndDisabled(); }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("LNA Gain");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::SliderInt(CONCAT("##_badgesdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                _this->openDev->setLNAGain(_this->lnaGain);
 | 
			
		||||
            }
 | 
			
		||||
            // TODO: Save
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("Mixer Gain");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::SliderInt(CONCAT("##_badgesdr_mixer_gain_", _this->name), &_this->mixerGain, 0, 15)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                _this->openDev->setMixerGain(_this->mixerGain);
 | 
			
		||||
            }
 | 
			
		||||
            // TODO: Save
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("VGA Gain");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::SliderInt(CONCAT("##_badgesdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                _this->openDev->setVGAGain(_this->vgaGain);
 | 
			
		||||
            }
 | 
			
		||||
            // TODO: Save
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void callback(const uint8_t* samples, int count, void* ctx) {
 | 
			
		||||
        BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
 | 
			
		||||
 | 
			
		||||
        // Convert samples to float
 | 
			
		||||
        dsp::complex_t* out = _this->input.writeBuf;
 | 
			
		||||
        int min = 255, max = 0;
 | 
			
		||||
        for (int i = 0; i < count; i++) {
 | 
			
		||||
            if (samples[i] < min) { min = samples[i]; }
 | 
			
		||||
            if (samples[i] > max) { max = samples[i]; }
 | 
			
		||||
 | 
			
		||||
            out[i].re = ((float)samples[i] - 127.5f) * (1.0f/127.0f);
 | 
			
		||||
            out[i].im = 1.0f;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Send out samples
 | 
			
		||||
        _this->input.swap(count);
 | 
			
		||||
 | 
			
		||||
        flog::debug("Amplitudes: {} -> {}", min, max);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string name;
 | 
			
		||||
    bool enabled = true;
 | 
			
		||||
    double sampleRate;
 | 
			
		||||
    SourceManager::SourceHandler handler;
 | 
			
		||||
    bool running = false;
 | 
			
		||||
    double freq;
 | 
			
		||||
 | 
			
		||||
    OptionList<std::string, BadgeSDR::DeviceInfo> devices;
 | 
			
		||||
 | 
			
		||||
    int devId = 0;
 | 
			
		||||
    int lnaGain = 0;
 | 
			
		||||
    int mixerGain = 0;
 | 
			
		||||
    int vgaGain = 0;
 | 
			
		||||
    std::string selectedSerial;
 | 
			
		||||
    BadgeSDR::DeviceInfo selectedDev;
 | 
			
		||||
    std::shared_ptr<BadgeSDR::Device> openDev;
 | 
			
		||||
 | 
			
		||||
    dsp::stream<dsp::complex_t> input;
 | 
			
		||||
    dsp::correction::DCBlocker<dsp::complex_t> dcBlock;
 | 
			
		||||
    dsp::channel::RxVFO ddc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _INIT_() {
 | 
			
		||||
    // Nothing here
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
 | 
			
		||||
    return new BadgeSDRSourceModule(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
 | 
			
		||||
    delete (BadgeSDRSourceModule*)instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _END_() {
 | 
			
		||||
    // Nothing here
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										622
									
								
								source_modules/badgesdr_source/src/r820t.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										622
									
								
								source_modules/badgesdr_source/src/r820t.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,622 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Rafael Micro R820T driver for AIRSPY
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright 2013 Youssef Touil <youssef@airspy.com>
 | 
			
		||||
 * Copyright 2014-2016 Benjamin Vernoux <bvernoux@airspy.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation, either version 2 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "r820t.h"
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
static int r820t_read_cache_reg(r820t_priv_t *priv, int reg);
 | 
			
		||||
 | 
			
		||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
 | 
			
		||||
 | 
			
		||||
/* Tuner frequency ranges */
 | 
			
		||||
struct r820t_freq_range
 | 
			
		||||
{
 | 
			
		||||
  uint8_t open_d;
 | 
			
		||||
  uint8_t rf_mux_ploy;
 | 
			
		||||
  uint8_t tf_c;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define R820T_READ_MAX_DATA 32
 | 
			
		||||
#define R820T_INIT_NB_REGS (32-5)
 | 
			
		||||
uint8_t r820t_read_data[R820T_READ_MAX_DATA]; /* Buffer for data read from I2C */
 | 
			
		||||
uint8_t r820t_state_standby = 1; /* 1=standby/power off 0=r820t initialized/power on */
 | 
			
		||||
 | 
			
		||||
/* Tuner frequency ranges
 | 
			
		||||
"Copyright (C) 2013 Mauro Carvalho Chehab"
 | 
			
		||||
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
 | 
			
		||||
part of freq_ranges()
 | 
			
		||||
*/
 | 
			
		||||
const struct r820t_freq_range freq_ranges[] =
 | 
			
		||||
{
 | 
			
		||||
  {
 | 
			
		||||
  /* 0 MHz */
 | 
			
		||||
  /* .open_d = */     0x08, /* low */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0xdf, /* R27[7:0]  band2,band0 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 50 MHz */
 | 
			
		||||
  /* .open_d = */     0x08, /* low */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0xbe, /* R27[7:0]  band4,band1  */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 55 MHz */
 | 
			
		||||
  /* .open_d = */     0x08, /* low */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x8b, /* R27[7:0]  band7,band4 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 60 MHz */
 | 
			
		||||
  /* .open_d = */     0x08, /* low */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x7b, /* R27[7:0]  band8,band4 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 65 MHz */
 | 
			
		||||
  /* .open_d = */     0x08, /* low */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x69, /* R27[7:0]  band9,band6 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 70 MHz */
 | 
			
		||||
  /* .open_d = */     0x08, /* low */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x58, /* R27[7:0]  band10,band7 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 75 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x44, /* R27[7:0]  band11,band11 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 80 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x44, /* R27[7:0]  band11,band11 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 90 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x34, /* R27[7:0]  band12,band11 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 100 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x34, /* R27[7:0]  band12,band11 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 110 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x24, /* R27[7:0]  band13,band11 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 120 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x24, /* R27[7:0]  band13,band11 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 140 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x14, /* R27[7:0]  band14,band11 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 180 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x13, /* R27[7:0]  band14,band12 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 220 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x13, /* R27[7:0]  band14,band12 */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 250 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x11, /* R27[7:0]  highest,highest */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 280 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x02, /* R26[7:6]=0 (LPF)  R26[1:0]=2 (low) */
 | 
			
		||||
  /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 310 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x41, /* R26[7:6]=1 (bypass)  R26[1:0]=1 (middle) */
 | 
			
		||||
  /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 450 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x41, /* R26[7:6]=1 (bypass)  R26[1:0]=1 (middle) */
 | 
			
		||||
  /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 588 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x40, /* R26[7:6]=1 (bypass)  R26[1:0]=0 (highest) */
 | 
			
		||||
  /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */
 | 
			
		||||
  }, {
 | 
			
		||||
  /* 650 MHz */
 | 
			
		||||
  /* .open_d = */     0x00, /* high */
 | 
			
		||||
  /* .rf_mux_ploy = */  0x40, /* R26[7:6]=1 (bypass)  R26[1:0]=0 (highest) */
 | 
			
		||||
  /* .tf_c = */     0x00, /* R27[7:0]  highest,highest */
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define FREQ_TO_IDX_SIZE (600)
 | 
			
		||||
const uint8_t freq_to_idx[FREQ_TO_IDX_SIZE]=
 | 
			
		||||
{
 | 
			
		||||
  /* 50 */ 1,/* 51 */ 1,/* 52 */ 1,/* 53 */ 1,/* 54 */ 1,
 | 
			
		||||
  /* 55 */ 2,/* 56 */ 2,/* 57 */ 2,/* 58 */ 2,/* 59 */ 2,
 | 
			
		||||
  /* 60 */ 3,/* 61 */ 3,/* 62 */ 3,/* 63 */ 3,/* 64 */ 3,
 | 
			
		||||
  /* 65 */ 4,/* 66 */ 4,/* 67 */ 4,/* 68 */ 4,/* 69 */ 4,
 | 
			
		||||
  /* 70 */ 5,/* 71 */ 5,/* 72 */ 5,/* 73 */ 5,/* 74 */ 5,
 | 
			
		||||
  /* 75 */ 6,/* 76 */ 6,/* 77 */ 6,/* 78 */ 6,/* 79 */ 6,
 | 
			
		||||
  /* 80 */ 7,/* 81 */ 7,/* 82 */ 7,/* 83 */ 7,/* 84 */ 7,/* 85 */ 7,/* 86 */ 7,/* 87 */ 7,/* 88 */ 7,/* 89 */ 7,
 | 
			
		||||
  /* 90 */ 8,/* 91 */ 8,/* 92 */ 8,/* 93 */ 8,/* 94 */ 8,/* 95 */ 8,/* 96 */ 8,/* 97 */ 8,/* 98 */ 8,/* 99 */ 8,
 | 
			
		||||
  /* 100 */ 9,/* 101 */ 9,/* 102 */ 9,/* 103 */ 9,/* 104 */ 9,/* 105 */ 9,/* 106 */ 9,/* 107 */ 9,/* 108 */ 9,/* 109 */ 9,
 | 
			
		||||
  /* 110 */ 10,/* 111 */ 10,/* 112 */ 10,/* 113 */ 10,/* 114 */ 10,/* 115 */ 10,/* 116 */ 10,/* 117 */ 10,/* 118 */ 10,/* 119 */ 10,
 | 
			
		||||
  /* 120 */ 11,/* 121 */ 11,/* 122 */ 11,/* 123 */ 11,/* 124 */ 11,/* 125 */ 11,/* 126 */ 11,/* 127 */ 11,/* 128 */ 11,/* 129 */ 11,
 | 
			
		||||
  /* 130 */ 11,/* 131 */ 11,/* 132 */ 11,/* 133 */ 11,/* 134 */ 11,/* 135 */ 11,/* 136 */ 11,/* 137 */ 11,/* 138 */ 11,/* 139 */ 11,
 | 
			
		||||
  /* 140 */ 12,/* 141 */ 12,/* 142 */ 12,/* 143 */ 12,/* 144 */ 12,/* 145 */ 12,/* 146 */ 12,/* 147 */ 12,/* 148 */ 12,/* 149 */ 12,
 | 
			
		||||
  /* 150 */ 12,/* 151 */ 12,/* 152 */ 12,/* 153 */ 12,/* 154 */ 12,/* 155 */ 12,/* 156 */ 12,/* 157 */ 12,/* 158 */ 12,/* 159 */ 12,
 | 
			
		||||
  /* 160 */ 12,/* 161 */ 12,/* 162 */ 12,/* 163 */ 12,/* 164 */ 12,/* 165 */ 12,/* 166 */ 12,/* 167 */ 12,/* 168 */ 12,/* 169 */ 12,
 | 
			
		||||
  /* 170 */ 12,/* 171 */ 12,/* 172 */ 12,/* 173 */ 12,/* 174 */ 12,/* 175 */ 12,/* 176 */ 12,/* 177 */ 12,/* 178 */ 12,/* 179 */ 12,
 | 
			
		||||
  /* 180 */ 13,/* 181 */ 13,/* 182 */ 13,/* 183 */ 13,/* 184 */ 13,/* 185 */ 13,/* 186 */ 13,/* 187 */ 13,/* 188 */ 13,/* 189 */ 13,
 | 
			
		||||
  /* 190 */ 13,/* 191 */ 13,/* 192 */ 13,/* 193 */ 13,/* 194 */ 13,/* 195 */ 13,/* 196 */ 13,/* 197 */ 13,/* 198 */ 13,/* 199 */ 13,
 | 
			
		||||
  /* 200 */ 13,/* 201 */ 13,/* 202 */ 13,/* 203 */ 13,/* 204 */ 13,/* 205 */ 13,/* 206 */ 13,/* 207 */ 13,/* 208 */ 13,/* 209 */ 13,
 | 
			
		||||
  /* 210 */ 13,/* 211 */ 13,/* 212 */ 13,/* 213 */ 13,/* 214 */ 13,/* 215 */ 13,/* 216 */ 13,/* 217 */ 13,/* 218 */ 13,/* 219 */ 13,
 | 
			
		||||
  /* 220 */ 14,/* 221 */ 14,/* 222 */ 14,/* 223 */ 14,/* 224 */ 14,/* 225 */ 14,/* 226 */ 14,/* 227 */ 14,/* 228 */ 14,/* 229 */ 14,
 | 
			
		||||
  /* 230 */ 14,/* 231 */ 14,/* 232 */ 14,/* 233 */ 14,/* 234 */ 14,/* 235 */ 14,/* 236 */ 14,/* 237 */ 14,/* 238 */ 14,/* 239 */ 14,
 | 
			
		||||
  /* 240 */ 14,/* 241 */ 14,/* 242 */ 14,/* 243 */ 14,/* 244 */ 14,/* 245 */ 14,/* 246 */ 14,/* 247 */ 14,/* 248 */ 14,/* 249 */ 14,
 | 
			
		||||
  /* 250 */ 15,/* 251 */ 15,/* 252 */ 15,/* 253 */ 15,/* 254 */ 15,/* 255 */ 15,/* 256 */ 15,/* 257 */ 15,/* 258 */ 15,/* 259 */ 15,
 | 
			
		||||
  /* 260 */ 15,/* 261 */ 15,/* 262 */ 15,/* 263 */ 15,/* 264 */ 15,/* 265 */ 15,/* 266 */ 15,/* 267 */ 15,/* 268 */ 15,/* 269 */ 15,
 | 
			
		||||
  /* 270 */ 15,/* 271 */ 15,/* 272 */ 15,/* 273 */ 15,/* 274 */ 15,/* 275 */ 15,/* 276 */ 15,/* 277 */ 15,/* 278 */ 15,/* 279 */ 15,
 | 
			
		||||
  /* 280 */ 16,/* 281 */ 16,/* 282 */ 16,/* 283 */ 16,/* 284 */ 16,/* 285 */ 16,/* 286 */ 16,/* 287 */ 16,/* 288 */ 16,/* 289 */ 16,
 | 
			
		||||
  /* 290 */ 16,/* 291 */ 16,/* 292 */ 16,/* 293 */ 16,/* 294 */ 16,/* 295 */ 16,/* 296 */ 16,/* 297 */ 16,/* 298 */ 16,/* 299 */ 16,
 | 
			
		||||
  /* 300 */ 16,/* 301 */ 16,/* 302 */ 16,/* 303 */ 16,/* 304 */ 16,/* 305 */ 16,/* 306 */ 16,/* 307 */ 16,/* 308 */ 16,/* 309 */ 16,
 | 
			
		||||
  /* 310 */ 17,/* 311 */ 17,/* 312 */ 17,/* 313 */ 17,/* 314 */ 17,/* 315 */ 17,/* 316 */ 17,/* 317 */ 17,/* 318 */ 17,/* 319 */ 17,
 | 
			
		||||
  /* 320 */ 17,/* 321 */ 17,/* 322 */ 17,/* 323 */ 17,/* 324 */ 17,/* 325 */ 17,/* 326 */ 17,/* 327 */ 17,/* 328 */ 17,/* 329 */ 17,
 | 
			
		||||
  /* 330 */ 17,/* 331 */ 17,/* 332 */ 17,/* 333 */ 17,/* 334 */ 17,/* 335 */ 17,/* 336 */ 17,/* 337 */ 17,/* 338 */ 17,/* 339 */ 17,
 | 
			
		||||
  /* 340 */ 17,/* 341 */ 17,/* 342 */ 17,/* 343 */ 17,/* 344 */ 17,/* 345 */ 17,/* 346 */ 17,/* 347 */ 17,/* 348 */ 17,/* 349 */ 17,
 | 
			
		||||
  /* 350 */ 17,/* 351 */ 17,/* 352 */ 17,/* 353 */ 17,/* 354 */ 17,/* 355 */ 17,/* 356 */ 17,/* 357 */ 17,/* 358 */ 17,/* 359 */ 17,
 | 
			
		||||
  /* 360 */ 17,/* 361 */ 17,/* 362 */ 17,/* 363 */ 17,/* 364 */ 17,/* 365 */ 17,/* 366 */ 17,/* 367 */ 17,/* 368 */ 17,/* 369 */ 17,
 | 
			
		||||
  /* 370 */ 17,/* 371 */ 17,/* 372 */ 17,/* 373 */ 17,/* 374 */ 17,/* 375 */ 17,/* 376 */ 17,/* 377 */ 17,/* 378 */ 17,/* 379 */ 17,
 | 
			
		||||
  /* 380 */ 17,/* 381 */ 17,/* 382 */ 17,/* 383 */ 17,/* 384 */ 17,/* 385 */ 17,/* 386 */ 17,/* 387 */ 17,/* 388 */ 17,/* 389 */ 17,
 | 
			
		||||
  /* 390 */ 17,/* 391 */ 17,/* 392 */ 17,/* 393 */ 17,/* 394 */ 17,/* 395 */ 17,/* 396 */ 17,/* 397 */ 17,/* 398 */ 17,/* 399 */ 17,
 | 
			
		||||
  /* 400 */ 17,/* 401 */ 17,/* 402 */ 17,/* 403 */ 17,/* 404 */ 17,/* 405 */ 17,/* 406 */ 17,/* 407 */ 17,/* 408 */ 17,/* 409 */ 17,
 | 
			
		||||
  /* 410 */ 17,/* 411 */ 17,/* 412 */ 17,/* 413 */ 17,/* 414 */ 17,/* 415 */ 17,/* 416 */ 17,/* 417 */ 17,/* 418 */ 17,/* 419 */ 17,
 | 
			
		||||
  /* 420 */ 17,/* 421 */ 17,/* 422 */ 17,/* 423 */ 17,/* 424 */ 17,/* 425 */ 17,/* 426 */ 17,/* 427 */ 17,/* 428 */ 17,/* 429 */ 17,
 | 
			
		||||
  /* 430 */ 17,/* 431 */ 17,/* 432 */ 17,/* 433 */ 17,/* 434 */ 17,/* 435 */ 17,/* 436 */ 17,/* 437 */ 17,/* 438 */ 17,/* 439 */ 17,
 | 
			
		||||
  /* 440 */ 17,/* 441 */ 17,/* 442 */ 17,/* 443 */ 17,/* 444 */ 17,/* 445 */ 17,/* 446 */ 17,/* 447 */ 17,/* 448 */ 17,/* 449 */ 17,
 | 
			
		||||
  /* 450 */ 18,/* 451 */ 18,/* 452 */ 18,/* 453 */ 18,/* 454 */ 18,/* 455 */ 18,/* 456 */ 18,/* 457 */ 18,/* 458 */ 18,/* 459 */ 18,
 | 
			
		||||
  /* 460 */ 18,/* 461 */ 18,/* 462 */ 18,/* 463 */ 18,/* 464 */ 18,/* 465 */ 18,/* 466 */ 18,/* 467 */ 18,/* 468 */ 18,/* 469 */ 18,
 | 
			
		||||
  /* 470 */ 18,/* 471 */ 18,/* 472 */ 18,/* 473 */ 18,/* 474 */ 18,/* 475 */ 18,/* 476 */ 18,/* 477 */ 18,/* 478 */ 18,/* 479 */ 18,
 | 
			
		||||
  /* 480 */ 18,/* 481 */ 18,/* 482 */ 18,/* 483 */ 18,/* 484 */ 18,/* 485 */ 18,/* 486 */ 18,/* 487 */ 18,/* 488 */ 18,/* 489 */ 18,
 | 
			
		||||
  /* 490 */ 18,/* 491 */ 18,/* 492 */ 18,/* 493 */ 18,/* 494 */ 18,/* 495 */ 18,/* 496 */ 18,/* 497 */ 18,/* 498 */ 18,/* 499 */ 18,
 | 
			
		||||
  /* 500 */ 18,/* 501 */ 18,/* 502 */ 18,/* 503 */ 18,/* 504 */ 18,/* 505 */ 18,/* 506 */ 18,/* 507 */ 18,/* 508 */ 18,/* 509 */ 18,
 | 
			
		||||
  /* 510 */ 18,/* 511 */ 18,/* 512 */ 18,/* 513 */ 18,/* 514 */ 18,/* 515 */ 18,/* 516 */ 18,/* 517 */ 18,/* 518 */ 18,/* 519 */ 18,
 | 
			
		||||
  /* 520 */ 18,/* 521 */ 18,/* 522 */ 18,/* 523 */ 18,/* 524 */ 18,/* 525 */ 18,/* 526 */ 18,/* 527 */ 18,/* 528 */ 18,/* 529 */ 18,
 | 
			
		||||
  /* 530 */ 18,/* 531 */ 18,/* 532 */ 18,/* 533 */ 18,/* 534 */ 18,/* 535 */ 18,/* 536 */ 18,/* 537 */ 18,/* 538 */ 18,/* 539 */ 18,
 | 
			
		||||
  /* 540 */ 18,/* 541 */ 18,/* 542 */ 18,/* 543 */ 18,/* 544 */ 18,/* 545 */ 18,/* 546 */ 18,/* 547 */ 18,/* 548 */ 18,/* 549 */ 18,
 | 
			
		||||
  /* 550 */ 18,/* 551 */ 18,/* 552 */ 18,/* 553 */ 18,/* 554 */ 18,/* 555 */ 18,/* 556 */ 18,/* 557 */ 18,/* 558 */ 18,/* 559 */ 18,
 | 
			
		||||
  /* 560 */ 18,/* 561 */ 18,/* 562 */ 18,/* 563 */ 18,/* 564 */ 18,/* 565 */ 18,/* 566 */ 18,/* 567 */ 18,/* 568 */ 18,/* 569 */ 18,
 | 
			
		||||
  /* 570 */ 18,/* 571 */ 18,/* 572 */ 18,/* 573 */ 18,/* 574 */ 18,/* 575 */ 18,/* 576 */ 18,/* 577 */ 18,/* 578 */ 18,/* 579 */ 18,
 | 
			
		||||
  /* 580 */ 18,/* 581 */ 18,/* 582 */ 18,/* 583 */ 18,/* 584 */ 18,/* 585 */ 18,/* 586 */ 18,/* 587 */ 18,
 | 
			
		||||
  /* 588 */ 19,/* 589 */ 19,/* 590 */ 19,/* 591 */ 19,/* 592 */ 19,/* 593 */ 19,/* 594 */ 19,/* 595 */ 19,/* 596 */ 19,/* 597 */ 19,
 | 
			
		||||
  /* 598 */ 19,/* 599 */ 19,/* 600 */ 19,/* 601 */ 19,/* 602 */ 19,/* 603 */ 19,/* 604 */ 19,/* 605 */ 19,/* 606 */ 19,/* 607 */ 19,
 | 
			
		||||
  /* 608 */ 19,/* 609 */ 19,/* 610 */ 19,/* 611 */ 19,/* 612 */ 19,/* 613 */ 19,/* 614 */ 19,/* 615 */ 19,/* 616 */ 19,/* 617 */ 19,
 | 
			
		||||
  /* 618 */ 19,/* 619 */ 19,/* 620 */ 19,/* 621 */ 19,/* 622 */ 19,/* 623 */ 19,/* 624 */ 19,/* 625 */ 19,/* 626 */ 19,/* 627 */ 19,
 | 
			
		||||
  /* 628 */ 19,/* 629 */ 19,/* 630 */ 19,/* 631 */ 19,/* 632 */ 19,/* 633 */ 19,/* 634 */ 19,/* 635 */ 19,/* 636 */ 19,/* 637 */ 19,
 | 
			
		||||
  /* 638 */ 19,/* 639 */ 19,/* 640 */ 19,/* 641 */ 19,/* 642 */ 19,/* 643 */ 19,/* 644 */ 19,/* 645 */ 19,/* 646 */ 19,/* 647 */ 19,
 | 
			
		||||
  /* 648 */ 19,/* 649 */ 19
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define FREQ_50MHZ (50)
 | 
			
		||||
#define FREQ_TO_IDX_0_TO_49MHZ (0)
 | 
			
		||||
#define FREQ_TO_IDX_650_TO_1800MHZ (20)
 | 
			
		||||
 | 
			
		||||
int r820t_freq_get_idx(uint32_t freq_mhz)
 | 
			
		||||
{
 | 
			
		||||
  uint32_t freq_mhz_fix;
 | 
			
		||||
 | 
			
		||||
  if(freq_mhz < FREQ_50MHZ)
 | 
			
		||||
  {
 | 
			
		||||
    /* Frequency Less than 50MHz */
 | 
			
		||||
    return FREQ_TO_IDX_0_TO_49MHZ;
 | 
			
		||||
  }else
 | 
			
		||||
  {
 | 
			
		||||
    /* Frequency Between 50 to 649MHz use table */
 | 
			
		||||
    /* Fix the frequency for the table */
 | 
			
		||||
    freq_mhz_fix = freq_mhz - FREQ_50MHZ;
 | 
			
		||||
    if(freq_mhz_fix < FREQ_TO_IDX_SIZE)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
      return freq_to_idx[freq_mhz_fix];
 | 
			
		||||
    }else
 | 
			
		||||
    {
 | 
			
		||||
      /* Frequency Between 650 to 1800MHz */
 | 
			
		||||
      return FREQ_TO_IDX_650_TO_1800MHZ;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool r820t_is_power_enabled(void)
 | 
			
		||||
{
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Write regs 5 to 32 (R820T_INIT_NB_REGS values) using data parameter and write last reg to 0
 | 
			
		||||
 */
 | 
			
		||||
void airspy_r820t_write_init(r820t_priv_t *priv, const uint8_t* data)
 | 
			
		||||
{
 | 
			
		||||
  for (int i = 0; i < R820T_INIT_NB_REGS; i++) {
 | 
			
		||||
    priv->write_reg(i+REG_SHADOW_START, data[i], priv->ctx);
 | 
			
		||||
  }
 | 
			
		||||
  priv->write_reg(0x1F, 0, priv->ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Read from one or more contiguous registers. data[0] should be the first
 | 
			
		||||
 * register number, one or more values follow.
 | 
			
		||||
 */
 | 
			
		||||
 const uint8_t lut[16] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
 | 
			
		||||
      0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf };
 | 
			
		||||
 | 
			
		||||
static uint8_t r82xx_bitrev(uint8_t byte)
 | 
			
		||||
{
 | 
			
		||||
 return (lut[byte & 0xf] << 4) | lut[byte >> 4];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int r820t_write_reg(r820t_priv_t *priv, uint8_t reg, uint8_t val)
 | 
			
		||||
{
 | 
			
		||||
  if (r820t_read_cache_reg(priv, reg) == val)
 | 
			
		||||
    return 0;
 | 
			
		||||
  priv->write_reg(reg, val, priv->ctx);
 | 
			
		||||
  priv->regs[reg - REG_SHADOW_START] = val;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int r820t_read_cache_reg(r820t_priv_t *priv, int reg)
 | 
			
		||||
{
 | 
			
		||||
  reg -= REG_SHADOW_START;
 | 
			
		||||
 | 
			
		||||
  if (reg >= 0 && reg < NUM_REGS)
 | 
			
		||||
    return priv->regs[reg];
 | 
			
		||||
  else
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int r820t_write_reg_mask(r820t_priv_t *priv, uint8_t reg, uint8_t val, uint8_t bit_mask)
 | 
			
		||||
{
 | 
			
		||||
  int rc = r820t_read_cache_reg(priv, reg);
 | 
			
		||||
 | 
			
		||||
  if (rc < 0)
 | 
			
		||||
    return rc;
 | 
			
		||||
 | 
			
		||||
  val = (rc & ~bit_mask) | (val & bit_mask);
 | 
			
		||||
 | 
			
		||||
  return r820t_write_reg(priv, reg, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int r820t_read(r820t_priv_t *priv, uint8_t *val, int len)
 | 
			
		||||
{
 | 
			
		||||
  /* reg not used and assumed to be always 0 because start from reg0 to reg0+len */
 | 
			
		||||
  priv->read_reg(val, len, priv->ctx);
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * r820t tuning logic
 | 
			
		||||
 */
 | 
			
		||||
#ifdef OPTIM_SET_MUX
 | 
			
		||||
int r820t_set_mux_freq_idx = -1; /* Default set to invalid value in order to force set_mux */
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
"inspired by Mauro Carvalho Chehab set_mux technique"
 | 
			
		||||
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
 | 
			
		||||
part of r820t_set_mux() (set tracking filter)
 | 
			
		||||
*/
 | 
			
		||||
static int r820t_set_tf(r820t_priv_t *priv, uint32_t freq)
 | 
			
		||||
{
 | 
			
		||||
  const struct r820t_freq_range *range;
 | 
			
		||||
  int freq_idx;
 | 
			
		||||
  int rc = 0;
 | 
			
		||||
 | 
			
		||||
  /* Get the proper frequency range in MHz instead of Hz */
 | 
			
		||||
  /* Fast divide freq by 1000000 */
 | 
			
		||||
  freq = (uint32_t)((uint64_t)freq * 4295 >> 32);
 | 
			
		||||
 | 
			
		||||
  freq_idx = r820t_freq_get_idx(freq);
 | 
			
		||||
  range = &freq_ranges[freq_idx];
 | 
			
		||||
 | 
			
		||||
  /* Only reconfigure mux freq if modified vs previous range */
 | 
			
		||||
#ifdef OPTIM_SET_MUX
 | 
			
		||||
  if(freq_idx != r820t_set_mux_freq_idx)
 | 
			
		||||
  {
 | 
			
		||||
#endif
 | 
			
		||||
    /* Open Drain */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* RF_MUX,Polymux */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* TF BAND */
 | 
			
		||||
    rc = r820t_write_reg(priv, 0x1b, range->tf_c);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* XTAL CAP & Drive */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x10, 0x08, 0x0b);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x08, 0x00, 0x3f);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x09, 0x00, 0x3f);
 | 
			
		||||
#ifdef OPTIM_SET_MUX
 | 
			
		||||
  }
 | 
			
		||||
  r820t_set_mux_freq_idx = freq_idx;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int r820t_set_pll(r820t_priv_t *priv, uint32_t freq)
 | 
			
		||||
{
 | 
			
		||||
  const uint32_t vco_min = 1770000000;
 | 
			
		||||
  const uint32_t vco_max = 3900000000;
 | 
			
		||||
  uint32_t ref = priv->xtal_freq >> 1;
 | 
			
		||||
 | 
			
		||||
  int rc;
 | 
			
		||||
  uint32_t div_num;
 | 
			
		||||
  uint32_t vco;
 | 
			
		||||
  uint32_t rem;
 | 
			
		||||
  uint32_t mask;
 | 
			
		||||
  uint16_t sdm;
 | 
			
		||||
  uint8_t nint;
 | 
			
		||||
  uint8_t ni;
 | 
			
		||||
  uint8_t si;
 | 
			
		||||
  uint8_t div_found;
 | 
			
		||||
 | 
			
		||||
  /* Find a suitable divider */
 | 
			
		||||
  div_found = 0;
 | 
			
		||||
  for (div_num = 0; div_num <= 5; div_num++)
 | 
			
		||||
  {
 | 
			
		||||
    vco = freq << (div_num + 1);
 | 
			
		||||
    if (vco >= vco_min && vco <= vco_max)
 | 
			
		||||
    {
 | 
			
		||||
      div_found = 1;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!div_found)
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  vco += ref >> 16;
 | 
			
		||||
  ref <<= 8;
 | 
			
		||||
  mask = 1 << 23;
 | 
			
		||||
  rem = 0;
 | 
			
		||||
  while (mask > 0 && vco > 0)
 | 
			
		||||
  {
 | 
			
		||||
    if (vco >= ref)
 | 
			
		||||
    {
 | 
			
		||||
      rem |= mask;
 | 
			
		||||
      vco -= ref;
 | 
			
		||||
    }
 | 
			
		||||
    ref >>= 1;
 | 
			
		||||
    mask >>= 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  nint = rem >> 16;
 | 
			
		||||
  sdm = rem & 0xffff;
 | 
			
		||||
 | 
			
		||||
  nint -= 13;
 | 
			
		||||
  ni = nint >> 2;
 | 
			
		||||
  si = nint & 3;
 | 
			
		||||
 | 
			
		||||
  /* Set the phase splitter */
 | 
			
		||||
  rc = r820t_write_reg_mask(priv, 0x10, (uint8_t) (div_num << 5), 0xe0);
 | 
			
		||||
  if(rc < 0)
 | 
			
		||||
    return rc;
 | 
			
		||||
 | 
			
		||||
  /* Set the integer part of the PLL */
 | 
			
		||||
  rc = r820t_write_reg(priv, 0x14, (uint8_t) (ni + (si << 6)));
 | 
			
		||||
  if(rc < 0)
 | 
			
		||||
    return rc;
 | 
			
		||||
 | 
			
		||||
  if (sdm == 0)
 | 
			
		||||
  {
 | 
			
		||||
    /* Disable SDM */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x12, 0x08, 0x08);
 | 
			
		||||
    if(rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
  }
 | 
			
		||||
  else
 | 
			
		||||
  {
 | 
			
		||||
	/* Write SDM */
 | 
			
		||||
    rc = r820t_write_reg(priv, 0x15, (uint8_t)(sdm & 0xff));
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    rc = r820t_write_reg(priv, 0x16, (uint8_t)(sdm >> 8));
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* Enable SDM */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x12, 0x00, 0x08);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
  }
 | 
			
		||||
  return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int r820t_set_freq(r820t_priv_t *priv, uint32_t freq)
 | 
			
		||||
{
 | 
			
		||||
  int rc;
 | 
			
		||||
  uint32_t lo_freq = freq + priv->if_freq;
 | 
			
		||||
 | 
			
		||||
  rc = r820t_set_tf(priv, freq);
 | 
			
		||||
  if (rc < 0)
 | 
			
		||||
    return rc;
 | 
			
		||||
 | 
			
		||||
  rc = r820t_set_pll(priv, lo_freq);
 | 
			
		||||
  if (rc < 0)
 | 
			
		||||
    return rc;
 | 
			
		||||
 | 
			
		||||
  priv->freq = freq;
 | 
			
		||||
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index)
 | 
			
		||||
{
 | 
			
		||||
  return r820t_write_reg_mask(priv, 0x05, gain_index, 0x0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index)
 | 
			
		||||
{
 | 
			
		||||
  return r820t_write_reg_mask(priv, 0x07, gain_index, 0x0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index)
 | 
			
		||||
{
 | 
			
		||||
  return r820t_write_reg_mask(priv, 0x0c, gain_index, 0x0f);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value)
 | 
			
		||||
{
 | 
			
		||||
  value = value != 0 ? 0x00 : 0x10;
 | 
			
		||||
  return r820t_write_reg_mask(priv, 0x05, value, 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value)
 | 
			
		||||
{
 | 
			
		||||
  value = value != 0 ? 0x10 : 0x00;
 | 
			
		||||
  return r820t_write_reg_mask(priv, 0x07, value, 0x10);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 
 | 
			
		||||
"inspired by Mauro Carvalho Chehab calibration technique"
 | 
			
		||||
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
 | 
			
		||||
part of r820t_set_tv_standard()
 | 
			
		||||
*/
 | 
			
		||||
int r820t_calibrate(r820t_priv_t *priv)
 | 
			
		||||
{
 | 
			
		||||
  int i, rc, cal_code;
 | 
			
		||||
  uint8_t data[5];
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < 5; i++)
 | 
			
		||||
  {
 | 
			
		||||
    /* Set filt_cap */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x0b, 0x08, 0x60);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* set cali clk =on */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* X'tal cap 0pF for PLL */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    rc = r820t_set_pll(priv, CALIBRATION_LO * 1000);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* Start Trigger */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
 | 
			
		||||
 | 
			
		||||
    /* Stop Trigger */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* set cali clk =off */
 | 
			
		||||
    rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04);
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    /* Check if calibration worked */
 | 
			
		||||
    rc = r820t_read(priv, data, sizeof(data));
 | 
			
		||||
    if (rc < 0)
 | 
			
		||||
      return rc;
 | 
			
		||||
 | 
			
		||||
    cal_code = data[4] & 0x0f;
 | 
			
		||||
    if (cal_code && cal_code != 0x0f)
 | 
			
		||||
      return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx)
 | 
			
		||||
{
 | 
			
		||||
  int rc;
 | 
			
		||||
  uint32_t saved_freq;
 | 
			
		||||
 | 
			
		||||
  r820t_state_standby = 0;
 | 
			
		||||
  priv->if_freq = if_freq;
 | 
			
		||||
  priv->write_reg = write_reg;
 | 
			
		||||
  priv->read_reg = read_reg;
 | 
			
		||||
  priv->ctx = ctx;
 | 
			
		||||
  /* Initialize registers */
 | 
			
		||||
  airspy_r820t_write_init(priv, priv->regs);
 | 
			
		||||
 | 
			
		||||
  r820t_set_freq(priv, priv->freq);
 | 
			
		||||
 | 
			
		||||
  /* Calibrate the IF filter */
 | 
			
		||||
  saved_freq = priv->freq;
 | 
			
		||||
  rc = r820t_calibrate(priv);
 | 
			
		||||
  priv->freq = saved_freq;
 | 
			
		||||
  if (rc < 0)
 | 
			
		||||
  {
 | 
			
		||||
    saved_freq = priv->freq;
 | 
			
		||||
    r820t_calibrate(priv);
 | 
			
		||||
    priv->freq = saved_freq;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Restore freq as it has been modified by r820t_calibrate() */
 | 
			
		||||
  rc = r820t_set_freq(priv, priv->freq);
 | 
			
		||||
  return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw)
 | 
			
		||||
{
 | 
			
		||||
    const uint8_t modes[] = { 0xE0, 0x80, 0x60, 0x00 };
 | 
			
		||||
    const uint8_t opt[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
 | 
			
		||||
    uint8_t a = 0xB0 | opt[bw & 0x0F];
 | 
			
		||||
    uint8_t b = 0x0F | modes[bw >> 4];
 | 
			
		||||
    r820t_write_reg(priv, 0x0A, a);
 | 
			
		||||
    r820t_write_reg(priv, 0x0B, b);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								source_modules/badgesdr_source/src/r820t.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								source_modules/badgesdr_source/src/r820t.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright 2013-2016 Benjamin Vernoux <bvernoux@airspy.com>
 | 
			
		||||
 *
 | 
			
		||||
 * This file is part of AirSpy.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 2, or (at your option)
 | 
			
		||||
 * any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU General Public License
 | 
			
		||||
 * along with this program; see the file COPYING.  If not, write to
 | 
			
		||||
 * the Free Software Foundation, Inc., 51 Franklin Street,
 | 
			
		||||
 * Boston, MA 02110-1301, USA.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define REG_SHADOW_START 5
 | 
			
		||||
#define NUM_REGS 30
 | 
			
		||||
 | 
			
		||||
/* R820T Clock */
 | 
			
		||||
#define CALIBRATION_LO 88000
 | 
			
		||||
 | 
			
		||||
typedef void (*r820t_write_reg_f)(uint8_t reg, uint8_t value, void* ctx);
 | 
			
		||||
typedef void (*r820t_read_f)(uint8_t* data, int len, void* ctx);
 | 
			
		||||
 | 
			
		||||
typedef struct
 | 
			
		||||
{
 | 
			
		||||
  uint32_t xtal_freq; /* XTAL_FREQ_HZ */
 | 
			
		||||
  uint32_t freq;
 | 
			
		||||
  uint32_t if_freq;
 | 
			
		||||
  uint8_t regs[NUM_REGS];
 | 
			
		||||
  uint16_t padding;
 | 
			
		||||
 | 
			
		||||
  r820t_write_reg_f write_reg;
 | 
			
		||||
  r820t_read_f read_reg;
 | 
			
		||||
  void* ctx;
 | 
			
		||||
 | 
			
		||||
} r820t_priv_t;
 | 
			
		||||
 | 
			
		||||
void airspy_r820t_write_single(r820t_priv_t *priv, uint8_t reg, uint8_t val);
 | 
			
		||||
uint8_t airspy_r820t_read_single(r820t_priv_t *priv, uint8_t reg);
 | 
			
		||||
 | 
			
		||||
int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx);
 | 
			
		||||
int r820t_set_freq(r820t_priv_t *priv, uint32_t freq);
 | 
			
		||||
int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index);
 | 
			
		||||
int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index);
 | 
			
		||||
int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index);
 | 
			
		||||
int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value);
 | 
			
		||||
int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value);
 | 
			
		||||
void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw);
 | 
			
		||||
							
								
								
									
										21
									
								
								source_modules/fobossdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								source_modules/fobossdr_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.13)
 | 
			
		||||
project(fobossdr_source)
 | 
			
		||||
 | 
			
		||||
file(GLOB SRC "src/*.cpp")
 | 
			
		||||
 | 
			
		||||
include(${SDRPP_MODULE_CMAKE})
 | 
			
		||||
 | 
			
		||||
if (MSVC)
 | 
			
		||||
    # Lib path
 | 
			
		||||
    target_link_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/lib/")
 | 
			
		||||
    target_include_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/include/")
 | 
			
		||||
    target_link_libraries(fobossdr_source PRIVATE fobos)
 | 
			
		||||
else (MSVC)
 | 
			
		||||
    find_package(PkgConfig)
 | 
			
		||||
 | 
			
		||||
    pkg_check_modules(LIBFOBOS REQUIRED libfobos)
 | 
			
		||||
 | 
			
		||||
    target_include_directories(fobossdr_source PRIVATE ${LIBFOBOS_INCLUDE_DIRS})
 | 
			
		||||
    target_link_directories(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARY_DIRS})
 | 
			
		||||
    target_link_libraries(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARIES})
 | 
			
		||||
endif ()
 | 
			
		||||
							
								
								
									
										560
									
								
								source_modules/fobossdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										560
									
								
								source_modules/fobossdr_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,560 @@
 | 
			
		||||
#include <imgui.h>
 | 
			
		||||
#include <module.h>
 | 
			
		||||
#include <gui/gui.h>
 | 
			
		||||
#include <gui/smgui.h>
 | 
			
		||||
#include <signal_path/signal_path.h>
 | 
			
		||||
#include <core.h>
 | 
			
		||||
#include <utils/optionlist.h>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <fobos.h>
 | 
			
		||||
 | 
			
		||||
SDRPP_MOD_INFO{
 | 
			
		||||
    /* Name:            */ "fobossdr_source",
 | 
			
		||||
    /* Description:     */ "FobosSDR Source Module",
 | 
			
		||||
    /* Author:          */ "Ryzerth",
 | 
			
		||||
    /* Version:         */ 0, 1, 0,
 | 
			
		||||
    /* Max instances    */ -1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
ConfigManager config;
 | 
			
		||||
 | 
			
		||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
 | 
			
		||||
 | 
			
		||||
// Work around for the fobos API not including
 | 
			
		||||
#define FOBOS_LNA_GAIN_MIN  1
 | 
			
		||||
#define FOBOS_LNA_GAIN_MAX  3
 | 
			
		||||
#define FOBOS_VGA_GAIN_MIN  0
 | 
			
		||||
#define FOBOS_VGA_GAIN_MAX  31
 | 
			
		||||
 | 
			
		||||
class FobosSDRSourceModule : public ModuleManager::Instance {
 | 
			
		||||
public:
 | 
			
		||||
    FobosSDRSourceModule(std::string name) {
 | 
			
		||||
        this->name = name;
 | 
			
		||||
 | 
			
		||||
        sampleRate = 50000000.0;
 | 
			
		||||
 | 
			
		||||
        // Initialize the DDC
 | 
			
		||||
        ddc.init(&ddcIn, 50e6, 50e6, 50e6, 0.0);
 | 
			
		||||
 | 
			
		||||
        handler.ctx = this;
 | 
			
		||||
        handler.selectHandler = menuSelected;
 | 
			
		||||
        handler.deselectHandler = menuDeselected;
 | 
			
		||||
        handler.menuHandler = menuHandler;
 | 
			
		||||
        handler.startHandler = start;
 | 
			
		||||
        handler.stopHandler = stop;
 | 
			
		||||
        handler.tuneHandler = tune;
 | 
			
		||||
        handler.stream = &ddc.out;
 | 
			
		||||
 | 
			
		||||
        // Refresh devices
 | 
			
		||||
        refresh();
 | 
			
		||||
 | 
			
		||||
        // Select device from config
 | 
			
		||||
        config.acquire();
 | 
			
		||||
        std::string devSerial = config.conf["device"];
 | 
			
		||||
        config.release();
 | 
			
		||||
        select(devSerial);
 | 
			
		||||
 | 
			
		||||
        sigpath::sourceManager.registerSource("FobosSDR", &handler);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ~FobosSDRSourceModule() {
 | 
			
		||||
        // Nothing to do
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void postInit() {}
 | 
			
		||||
 | 
			
		||||
    void enable() {
 | 
			
		||||
        enabled = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void disable() {
 | 
			
		||||
        enabled = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool isEnabled() {
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum Port {
 | 
			
		||||
        PORT_RF,
 | 
			
		||||
        PORT_HF1,
 | 
			
		||||
        PORT_HF2
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::string getBandwdithScaled(double bw) {
 | 
			
		||||
        char buf[1024];
 | 
			
		||||
        if (bw >= 1000000.0) {
 | 
			
		||||
            sprintf(buf, "%.1lfMHz", bw / 1000000.0);
 | 
			
		||||
        }
 | 
			
		||||
        else if (bw >= 1000.0) {
 | 
			
		||||
            sprintf(buf, "%.1lfKHz", bw / 1000.0);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            sprintf(buf, "%.1lfHz", bw);
 | 
			
		||||
        }
 | 
			
		||||
        return std::string(buf);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void refresh() {
 | 
			
		||||
        devices.clear();
 | 
			
		||||
        
 | 
			
		||||
        // Get device list
 | 
			
		||||
        char serials[1024];
 | 
			
		||||
        memset(serials, 0, sizeof(serials));
 | 
			
		||||
        int devCount = fobos_rx_list_devices(serials);
 | 
			
		||||
        if (devCount < 0) {
 | 
			
		||||
            flog::error("Failed to get device list: {}", devCount);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If no device, give up
 | 
			
		||||
        if (!devCount) { return; }
 | 
			
		||||
 | 
			
		||||
        // Generate device entries
 | 
			
		||||
        const char* _serials = serials;
 | 
			
		||||
        int index = 0;
 | 
			
		||||
        while (*_serials) {
 | 
			
		||||
            // Read serial until space
 | 
			
		||||
            std::string serial = "";
 | 
			
		||||
            while (*_serials) {
 | 
			
		||||
                // Get a character
 | 
			
		||||
                char c = *(_serials++);
 | 
			
		||||
 | 
			
		||||
                // If it's a space, we're done
 | 
			
		||||
                if (c == ' ') { break; }
 | 
			
		||||
 | 
			
		||||
                // Otherwise, add it to the string
 | 
			
		||||
                serial += c;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Create entry
 | 
			
		||||
            devices.define(serial, serial, index++);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void select(const std::string& serial) {
 | 
			
		||||
        // If there are no devices, give up
 | 
			
		||||
        if (devices.empty()) {
 | 
			
		||||
            selectedSerial.clear();
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // If the serial was not found, select the first available serial
 | 
			
		||||
        if (!devices.keyExists(serial)) {
 | 
			
		||||
            select(devices.key(0));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get the ID in the list
 | 
			
		||||
        int id = devices.keyId(serial);
 | 
			
		||||
        selectedDevId = devices[id];
 | 
			
		||||
 | 
			
		||||
        // Open the device
 | 
			
		||||
        fobos_dev_t* dev;
 | 
			
		||||
        int err = fobos_rx_open(&dev, selectedDevId);
 | 
			
		||||
        if (err) {
 | 
			
		||||
            flog::error("Failed to open device: {}", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get a list of supported samplerates
 | 
			
		||||
        double srList[128];
 | 
			
		||||
        unsigned int srCount;
 | 
			
		||||
        err = fobos_rx_get_samplerates(dev, srList, &srCount);
 | 
			
		||||
        if (err) {
 | 
			
		||||
            flog::error("Failed to get samplerate list: {}", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Generate samplerate list
 | 
			
		||||
        samplerates.clear();
 | 
			
		||||
        for (int i = 0; i < srCount; i++) {
 | 
			
		||||
            std::string str = getBandwdithScaled(srList[i]);
 | 
			
		||||
            samplerates.define(srList[i], str, srList[i]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Add some custom samplerates
 | 
			
		||||
        samplerates.define(5e6, "5.0MHz", 5e6);
 | 
			
		||||
        samplerates.define(2.5e6, "2.5MHz", 2.5e6);
 | 
			
		||||
        samplerates.define(1.25e6, "1.25MHz", 1.25e6);
 | 
			
		||||
 | 
			
		||||
        // Define the ports
 | 
			
		||||
        ports.clear();
 | 
			
		||||
        ports.define("rf", "RF", PORT_RF);
 | 
			
		||||
        ports.define("hf1", "HF1", PORT_HF1);
 | 
			
		||||
        ports.define("hf2", "HF2", PORT_HF2);
 | 
			
		||||
 | 
			
		||||
        // Define clock sources
 | 
			
		||||
        clockSources.clear();
 | 
			
		||||
        clockSources.define("internal", "Internal", 0);
 | 
			
		||||
        clockSources.define("external", "External", 1);
 | 
			
		||||
 | 
			
		||||
        // Close the device
 | 
			
		||||
        fobos_rx_close(dev);
 | 
			
		||||
 | 
			
		||||
        // Save serial number
 | 
			
		||||
        selectedSerial = serial;
 | 
			
		||||
        devId = id;
 | 
			
		||||
 | 
			
		||||
        // Load default options
 | 
			
		||||
        sampleRate = 50e6;
 | 
			
		||||
        srId = samplerates.valueId(sampleRate);
 | 
			
		||||
        port = PORT_RF;
 | 
			
		||||
        portId = ports.valueId(port);
 | 
			
		||||
        clkSrcId = clockSources.nameId("Internal");
 | 
			
		||||
        lnaGain = 0;
 | 
			
		||||
        vgaGain = 0;
 | 
			
		||||
 | 
			
		||||
        // Load config
 | 
			
		||||
        config.acquire();
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("samplerate")) {
 | 
			
		||||
            int desiredSr = config.conf["devices"][selectedSerial]["samplerate"];
 | 
			
		||||
            if (samplerates.keyExists(desiredSr)) {
 | 
			
		||||
                srId = samplerates.keyId(desiredSr);
 | 
			
		||||
                sampleRate = samplerates[srId];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("port")) {
 | 
			
		||||
            std::string desiredPort = config.conf["devices"][selectedSerial]["port"];
 | 
			
		||||
            if (ports.keyExists(desiredPort)) {
 | 
			
		||||
                portId = ports.keyId(desiredPort);
 | 
			
		||||
                port = ports[portId];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("clkSrc")) {
 | 
			
		||||
            std::string desiredClkSrc = config.conf["devices"][selectedSerial]["clkSrc"];
 | 
			
		||||
            if (clockSources.keyExists(desiredClkSrc)) {
 | 
			
		||||
                clkSrcId = clockSources.keyId(desiredClkSrc);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("lnaGain")) {
 | 
			
		||||
            lnaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["lnaGain"], FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX);
 | 
			
		||||
        }
 | 
			
		||||
        if (config.conf["devices"][selectedSerial].contains("vgaGain")) {
 | 
			
		||||
            vgaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["vgaGain"], FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX);
 | 
			
		||||
        }
 | 
			
		||||
        config.release();
 | 
			
		||||
 | 
			
		||||
        // Update the samplerate
 | 
			
		||||
        core::setInputSampleRate(sampleRate);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuSelected(void* ctx) {
 | 
			
		||||
        FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
 | 
			
		||||
        core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
        flog::info("FobosSDRSourceModule '{0}': Menu Select!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuDeselected(void* ctx) {
 | 
			
		||||
        FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
 | 
			
		||||
        flog::info("FobosSDRSourceModule '{0}': Menu Deselect!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void start(void* ctx) {
 | 
			
		||||
        FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) { return; }
 | 
			
		||||
 | 
			
		||||
        // Open the device
 | 
			
		||||
        int err = fobos_rx_open(&_this->openDev, _this->selectedDevId);
 | 
			
		||||
        if (err) {
 | 
			
		||||
            flog::error("Failed to open device: {}", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Get the selected port
 | 
			
		||||
        _this->port = _this->ports[_this->portId];
 | 
			
		||||
 | 
			
		||||
        // Configure the device
 | 
			
		||||
        double actualSr, actualFreq;
 | 
			
		||||
        fobos_rx_set_samplerate(_this->openDev, (_this->sampleRate >= 50e6) ? _this->sampleRate : 50e6, &actualSr);
 | 
			
		||||
        fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
 | 
			
		||||
        fobos_rx_set_direct_sampling(_this->openDev, _this->port != PORT_RF);
 | 
			
		||||
        fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
 | 
			
		||||
        fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
 | 
			
		||||
        fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
 | 
			
		||||
 | 
			
		||||
        // Configure the DDC
 | 
			
		||||
        if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
 | 
			
		||||
            // Set the frequency
 | 
			
		||||
            fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
 | 
			
		||||
        }
 | 
			
		||||
        else if (_this->port == PORT_RF) {
 | 
			
		||||
            // Set the frequency
 | 
			
		||||
            fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
 | 
			
		||||
 | 
			
		||||
            // Configure and start the DDC for decimation only
 | 
			
		||||
            _this->ddc.setInSamplerate(actualSr);
 | 
			
		||||
            _this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
 | 
			
		||||
            _this->ddc.setOffset(0.0);
 | 
			
		||||
            _this->ddc.start();
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            // Configure and start the DDC
 | 
			
		||||
            _this->ddc.setInSamplerate(actualSr);
 | 
			
		||||
            _this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
 | 
			
		||||
            _this->ddc.setOffset(_this->freq);
 | 
			
		||||
            _this->ddc.start();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Compute buffer size (Lower than usual, but it's a workaround for their API having broken streaming)
 | 
			
		||||
        _this->bufferSize = _this->sampleRate / 400.0;
 | 
			
		||||
 | 
			
		||||
        // Start streaming
 | 
			
		||||
        err = fobos_rx_start_sync(_this->openDev, _this->bufferSize);
 | 
			
		||||
        if (err) {
 | 
			
		||||
            flog::error("Failed to start stream: {}", err);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Start worker
 | 
			
		||||
        _this->run = true;
 | 
			
		||||
        _this->workerThread = std::thread(&FobosSDRSourceModule::worker, _this);
 | 
			
		||||
        
 | 
			
		||||
        _this->running = true;
 | 
			
		||||
        flog::info("FobosSDRSourceModule '{0}': Start!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void stop(void* ctx) {
 | 
			
		||||
        FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
 | 
			
		||||
        if (!_this->running) { return; }
 | 
			
		||||
        _this->running = false;
 | 
			
		||||
 | 
			
		||||
        // Stop worker
 | 
			
		||||
        _this->run = false;
 | 
			
		||||
        if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
 | 
			
		||||
            _this->ddc.out.stopWriter();
 | 
			
		||||
            if (_this->workerThread.joinable()) { _this->workerThread.join(); }
 | 
			
		||||
            _this->ddc.out.clearWriteStop();
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            _this->ddcIn.stopWriter();
 | 
			
		||||
            if (_this->workerThread.joinable()) { _this->workerThread.join(); }
 | 
			
		||||
            _this->ddcIn.clearWriteStop();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Stop streaming
 | 
			
		||||
        fobos_rx_stop_sync(_this->openDev);
 | 
			
		||||
 | 
			
		||||
        // Stop the DDC
 | 
			
		||||
        _this->ddc.stop();
 | 
			
		||||
 | 
			
		||||
        // Close the device
 | 
			
		||||
        fobos_rx_close(_this->openDev);
 | 
			
		||||
 | 
			
		||||
        flog::info("FobosSDRSourceModule '{0}': Stop!", _this->name);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void tune(double freq, void* ctx) {
 | 
			
		||||
        FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
 | 
			
		||||
        if (_this->running) {
 | 
			
		||||
            if (_this->port == PORT_RF) {
 | 
			
		||||
                double actual; // Dummy, don't care
 | 
			
		||||
                fobos_rx_set_frequency(_this->openDev, freq, &actual);
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                _this->ddc.setOffset(freq);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        _this->freq = freq;
 | 
			
		||||
        flog::info("FobosSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static void menuHandler(void* ctx) {
 | 
			
		||||
        FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
 | 
			
		||||
        
 | 
			
		||||
        if (_this->running) { SmGui::BeginDisabled(); }
 | 
			
		||||
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        SmGui::ForceSync();
 | 
			
		||||
        if (SmGui::Combo(CONCAT("##_fobossdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
 | 
			
		||||
            _this->select(_this->devices.key(_this->devId));
 | 
			
		||||
            core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
            config.acquire();
 | 
			
		||||
            config.conf["device"] = _this->selectedSerial;
 | 
			
		||||
            config.release(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (SmGui::Combo(CONCAT("##_fobossdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
 | 
			
		||||
            _this->sampleRate = _this->samplerates.value(_this->srId);
 | 
			
		||||
            core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
            if (!_this->selectedSerial.empty()) {
 | 
			
		||||
                config.acquire();
 | 
			
		||||
                config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->samplerates.key(_this->srId);
 | 
			
		||||
                config.release(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::SameLine();
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        SmGui::ForceSync();
 | 
			
		||||
        if (SmGui::Button(CONCAT("Refresh##_fobossdr_refr_", _this->name))) {
 | 
			
		||||
            _this->refresh();
 | 
			
		||||
            _this->select(_this->selectedSerial);
 | 
			
		||||
            core::setInputSampleRate(_this->sampleRate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("Antenna Port");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Combo(CONCAT("##_fobossdr_port_", _this->name), &_this->portId, _this->ports.txt)) {
 | 
			
		||||
            if (!_this->selectedSerial.empty()) {
 | 
			
		||||
                config.acquire();
 | 
			
		||||
                config.conf["devices"][_this->selectedSerial]["port"] = _this->ports.key(_this->portId);
 | 
			
		||||
                config.release(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_this->running) { SmGui::EndDisabled(); }
 | 
			
		||||
 | 
			
		||||
        SmGui::LeftLabel("Clock Source");
 | 
			
		||||
        SmGui::FillWidth();
 | 
			
		||||
        if (SmGui::Combo(CONCAT("##_fobossdr_clk_", _this->name), &_this->clkSrcId, _this->clockSources.txt)) {
 | 
			
		||||
            if (_this->running) {
 | 
			
		||||
                fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
 | 
			
		||||
            }
 | 
			
		||||
            if (!_this->selectedSerial.empty()) {
 | 
			
		||||
                config.acquire();
 | 
			
		||||
                config.conf["devices"][_this->selectedSerial]["clkSrc"] = _this->clockSources.key(_this->clkSrcId);
 | 
			
		||||
                config.release(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (_this->port == PORT_RF) {
 | 
			
		||||
            SmGui::LeftLabel("LNA Gain");
 | 
			
		||||
            SmGui::FillWidth();
 | 
			
		||||
            if (SmGui::SliderInt(CONCAT("##_fobossdr_lna_gain_", _this->name), &_this->lnaGain, FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX)) {
 | 
			
		||||
                if (_this->running) {
 | 
			
		||||
                    fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
 | 
			
		||||
                }
 | 
			
		||||
                if (!_this->selectedSerial.empty()) {
 | 
			
		||||
                    config.acquire();
 | 
			
		||||
                    config.conf["devices"][_this->selectedSerial]["lnaGain"] = _this->lnaGain;
 | 
			
		||||
                    config.release(true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            SmGui::LeftLabel("VGA Gain");
 | 
			
		||||
            SmGui::FillWidth();
 | 
			
		||||
            if (SmGui::SliderInt(CONCAT("##_fobossdr_vga_gain_", _this->name), &_this->vgaGain, FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX)) {
 | 
			
		||||
                if (_this->running) {
 | 
			
		||||
                    fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
 | 
			
		||||
                }
 | 
			
		||||
                if (!_this->selectedSerial.empty()) {
 | 
			
		||||
                    config.acquire();
 | 
			
		||||
                    config.conf["devices"][_this->selectedSerial]["vgaGain"] = _this->vgaGain;
 | 
			
		||||
                    config.release(true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void worker() {
 | 
			
		||||
        // Select different processing depending on the mode
 | 
			
		||||
        if (port == PORT_RF && sampleRate >= 50e6) {
 | 
			
		||||
            while (run) {
 | 
			
		||||
                // Read samples
 | 
			
		||||
                unsigned int sampCount = 0;
 | 
			
		||||
                int err = fobos_rx_read_sync(openDev, (float*)ddc.out.writeBuf, &sampCount);
 | 
			
		||||
                if (err) { break; }
 | 
			
		||||
                
 | 
			
		||||
                // Send out samples to the core
 | 
			
		||||
                if (!ddc.out.swap(sampCount)) { break; }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (port == PORT_RF) {
 | 
			
		||||
            while (run) {
 | 
			
		||||
                // Read samples
 | 
			
		||||
                unsigned int sampCount = 0;
 | 
			
		||||
                int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
 | 
			
		||||
                if (err) { break; }
 | 
			
		||||
                
 | 
			
		||||
                // Send samples to the DDC
 | 
			
		||||
                if (!ddcIn.swap(sampCount)) { break; }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (port == PORT_HF1) {
 | 
			
		||||
            while (run) {
 | 
			
		||||
                // Read samples
 | 
			
		||||
                unsigned int sampCount = 0;
 | 
			
		||||
                int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
 | 
			
		||||
                if (err) { break; }
 | 
			
		||||
 | 
			
		||||
                // Null out the HF2 samples
 | 
			
		||||
                for (int i = 0; i < sampCount; i++) {
 | 
			
		||||
                    ddcIn.writeBuf[i].im = 0.0f;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Send samples to the DDC
 | 
			
		||||
                if (!ddcIn.swap(sampCount)) { break; }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (port == PORT_HF2) {
 | 
			
		||||
            while (run) {
 | 
			
		||||
                // Read samples
 | 
			
		||||
                unsigned int sampCount = 0;
 | 
			
		||||
                int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
 | 
			
		||||
                if (err) { break; }
 | 
			
		||||
 | 
			
		||||
                // Null out the HF2 samples
 | 
			
		||||
                for (int i = 0; i < sampCount; i++) {
 | 
			
		||||
                    ddcIn.writeBuf[i].re = 0.0f;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Send samples to the DDC
 | 
			
		||||
                if (!ddcIn.swap(sampCount)) { break; }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string name;
 | 
			
		||||
    bool enabled = true;
 | 
			
		||||
    double sampleRate;
 | 
			
		||||
    SourceManager::SourceHandler handler;
 | 
			
		||||
    bool running = false;
 | 
			
		||||
    double freq;
 | 
			
		||||
 | 
			
		||||
    OptionList<std::string, int> devices;
 | 
			
		||||
    OptionList<int, double> samplerates;
 | 
			
		||||
    OptionList<std::string, Port> ports;
 | 
			
		||||
    OptionList<std::string, int> clockSources;
 | 
			
		||||
    int devId = 0;
 | 
			
		||||
    int srId = 0;
 | 
			
		||||
    int portId = 0;
 | 
			
		||||
    int clkSrcId = 0;
 | 
			
		||||
    Port port;
 | 
			
		||||
    int lnaGain = 0;
 | 
			
		||||
    int vgaGain = 0;
 | 
			
		||||
    std::string selectedSerial;
 | 
			
		||||
    int selectedDevId;
 | 
			
		||||
 | 
			
		||||
    fobos_dev_t* openDev;
 | 
			
		||||
 | 
			
		||||
    int bufferSize;
 | 
			
		||||
    std::thread workerThread;
 | 
			
		||||
    std::atomic<bool> run = false;
 | 
			
		||||
 | 
			
		||||
    dsp::stream<dsp::complex_t> ddcIn;
 | 
			
		||||
    dsp::channel::RxVFO ddc;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _INIT_() {
 | 
			
		||||
    json def = json({});
 | 
			
		||||
    def["devices"] = json({});
 | 
			
		||||
    def["device"] = "";
 | 
			
		||||
    config.setPath(core::args["root"].s() + "/fobossdr_config.json");
 | 
			
		||||
    config.load(def);
 | 
			
		||||
    config.enableAutoSave();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
 | 
			
		||||
    return new FobosSDRSourceModule(name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
 | 
			
		||||
    delete (FobosSDRSourceModule*)instance;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MOD_EXPORT void _END_() {
 | 
			
		||||
    config.disableAutoSave();
 | 
			
		||||
    config.save();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
@@ -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