mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-07-08 18:15:21 +02:00
Compare commits
13 Commits
fobos_test
...
debug
Author | SHA1 | Date | |
---|---|---|---|
15112c63b7 | |||
115cb23672 | |||
101f6777ee | |||
82a2a4c04a | |||
c4086f5719 | |||
5f77718d75 | |||
e613087e97 | |||
beb18972ea | |||
1b0a5ed88e | |||
b1ad7590cc | |||
9537ccf2d2 | |||
0ac1bd56bc | |||
936c99dc40 |
47
.github/workflows/build_all.yml
vendored
47
.github/workflows/build_all.yml
vendored
@ -36,13 +36,6 @@ 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
|
||||
|
||||
@ -65,27 +58,25 @@ 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 spdlog:x64-windows
|
||||
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows
|
||||
|
||||
- name: Install rtaudio
|
||||
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
|
||||
|
||||
- name: Install libperseus-sdr
|
||||
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake -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 .
|
||||
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
|
||||
|
||||
- name: Prepare CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake -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
|
||||
run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DCMAKE_BUILD_TYPE=Debug -DCOPY_MSVC_REDISTRIBUTABLES=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config Release --verbose
|
||||
run: cmake --build . --config Debug --verbose
|
||||
|
||||
- name: Run tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: ./Debug/min_broken.exe
|
||||
|
||||
- name: Create Archive
|
||||
working-directory: ${{runner.workspace}}
|
||||
@ -107,7 +98,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 spdlog && pip3 install mako
|
||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
|
||||
|
||||
- name: Install volk
|
||||
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
@ -125,20 +116,14 @@ 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 && 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 ..
|
||||
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
|
||||
|
||||
- name: Install more recent librtlsdr
|
||||
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_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 -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -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 -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@ -164,7 +149,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 spdlog && pip3 install mako --break-system-packages
|
||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && 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 ../../
|
||||
@ -184,18 +169,12 @@ 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 -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -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 -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
@ -15,9 +15,7 @@ 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)
|
||||
@ -44,14 +42,12 @@ 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
|
||||
@ -144,18 +140,10 @@ 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)
|
||||
@ -248,10 +236,6 @@ 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)
|
||||
@ -276,10 +260,6 @@ 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)
|
||||
@ -320,6 +300,7 @@ endif (OPT_BUILD_SCHEDULER)
|
||||
|
||||
if (MSVC)
|
||||
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
|
||||
add_executable(min_broken "min_broken/main.cpp" "win32/resources.rc")
|
||||
else ()
|
||||
add_executable(sdrpp "src/main.cpp")
|
||||
endif ()
|
||||
@ -328,12 +309,13 @@ target_link_libraries(sdrpp PRIVATE sdrpp_core)
|
||||
|
||||
# Compiler arguments
|
||||
target_compile_options(sdrpp PRIVATE ${SDRPP_COMPILER_FLAGS})
|
||||
target_compile_options(min_broken PRIVATE ${SDRPP_COMPILER_FLAGS})
|
||||
|
||||
# Copy dynamic libs over
|
||||
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)
|
||||
@ -350,7 +332,6 @@ if (MSVC)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.so\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
||||
endif ()
|
||||
@ -388,6 +369,4 @@ endif ()
|
||||
|
||||
# Create uninstall target
|
||||
configure_file(${CMAKE_SOURCE_DIR}/cmake_uninstall.cmake ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY)
|
||||
add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
|
||||
# Create headers target
|
||||
add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
@ -173,22 +173,16 @@ 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"]["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"]["PerseusSDR Source"]["module"] = "perseus_source";
|
||||
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
|
||||
defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";
|
||||
@ -199,12 +193,8 @@ 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,12 +37,9 @@ namespace sdrpp_credits {
|
||||
const char* hardwareDonators[] = {
|
||||
"Aaronia AG",
|
||||
"Airspy",
|
||||
"Alex 4Z5LV",
|
||||
"Analog Devices",
|
||||
"CaribouLabs",
|
||||
"Deepace",
|
||||
"Ettus Research",
|
||||
"Harogic",
|
||||
"Howard Su",
|
||||
"MicroPhase",
|
||||
"Microtelecom",
|
||||
@ -50,7 +47,6 @@ namespace sdrpp_credits {
|
||||
"Nuand",
|
||||
"RFNM",
|
||||
"RFspace",
|
||||
"RigExpert",
|
||||
"RTL-SDRblog",
|
||||
"SDRplay"
|
||||
};
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <volk/volk.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace dsp::buffer {
|
||||
template<class T>
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <volk/volk.h>
|
||||
#include "../buffer/buffer.h"
|
||||
|
||||
namespace dsp {
|
||||
template<class T>
|
||||
|
@ -52,4 +52,15 @@ namespace ImGui {
|
||||
bufferMtx.unlock();
|
||||
}
|
||||
|
||||
void SymbolDiagram::setCount(int count) {
|
||||
std::lock_guard<std::mutex> lck(bufferMtx);
|
||||
delete[] buffer;
|
||||
buffer = new float[count];
|
||||
sampleCount = count;
|
||||
memset(buffer, 0, sampleCount * sizeof(float));
|
||||
}
|
||||
|
||||
int SymbolDiagram::getCount() {
|
||||
return sampleCount;
|
||||
}
|
||||
}
|
@ -18,6 +18,10 @@ namespace ImGui {
|
||||
|
||||
void releaseBuffer();
|
||||
|
||||
void setCount(int count);
|
||||
|
||||
int getCount();
|
||||
|
||||
std::vector<float> lines;
|
||||
|
||||
private:
|
||||
|
@ -110,7 +110,7 @@ namespace ImGui {
|
||||
viewBandwidth = 1.0;
|
||||
wholeBandwidth = 1.0;
|
||||
|
||||
updatePallette(DEFAULT_COLOR_MAP, 13);
|
||||
//updatePallette(DEFAULT_COLOR_MAP, 13);
|
||||
}
|
||||
|
||||
void WaterFall::init() {
|
||||
|
@ -1,37 +0,0 @@
|
||||
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 ()
|
@ -1,280 +0,0 @@
|
||||
#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;
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,163 +0,0 @@
|
||||
#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();
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
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,15 +8,15 @@
|
||||
#include "dsp.h"
|
||||
#include "pocsag.h"
|
||||
|
||||
#define BAUDRATE 2400
|
||||
#define SAMPLERATE (BAUDRATE*10)
|
||||
|
||||
class POCSAGDecoder : public Decoder {
|
||||
public:
|
||||
POCSAGDecoder(const std::string& name, VFOManager::VFO* vfo) : diag(0.6, BAUDRATE) {
|
||||
POCSAGDecoder(const std::string& name, VFOManager::VFO* vfo) : diag(0.6, 2400) {
|
||||
this->name = name;
|
||||
this->vfo = vfo;
|
||||
|
||||
// Default baudrate (TODO: Load from config)
|
||||
baudrate = 2400;
|
||||
|
||||
// Define baudrate options
|
||||
baudrates.define(512, "512 Baud", 512);
|
||||
baudrates.define(1200, "1200 Baud", 1200);
|
||||
@ -24,9 +24,9 @@ public:
|
||||
|
||||
// Init DSP
|
||||
vfo->setBandwidthLimits(12500, 12500, true);
|
||||
vfo->setSampleRate(SAMPLERATE, 12500);
|
||||
dsp.init(vfo->output, SAMPLERATE, BAUDRATE);
|
||||
reshape.init(&dsp.soft, BAUDRATE, (BAUDRATE / 30.0) - BAUDRATE);
|
||||
vfo->setSampleRate(baudrate*10.0, 12500);
|
||||
dsp.init(vfo->output, baudrate*10.0, baudrate);
|
||||
reshape.init(&dsp.soft, baudrate, (baudrate / 30.0) - baudrate);
|
||||
dataHandler.init(&dsp.out, _dataHandler, this);
|
||||
diagHandler.init(&reshape.out, _diagHandler, this);
|
||||
|
||||
@ -42,7 +42,7 @@ public:
|
||||
ImGui::LeftLabel("Baudrate");
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::Combo(("##pager_decoder_pocsag_br_" + name).c_str(), &brId, baudrates.txt)) {
|
||||
// TODO
|
||||
setBaudrate(baudrates.value(brId));
|
||||
}
|
||||
|
||||
ImGui::FillWidth();
|
||||
@ -79,7 +79,8 @@ private:
|
||||
static void _diagHandler(float* data, int count, void* ctx) {
|
||||
POCSAGDecoder* _this = (POCSAGDecoder*)ctx;
|
||||
float* buf = _this->diag.acquireBuffer();
|
||||
memcpy(buf, data, count * sizeof(float));
|
||||
int maxCount = std::min<int>(count, _this->diag.getCount());
|
||||
memcpy(buf, data, maxCount * sizeof(float));
|
||||
_this->diag.releaseBuffer();
|
||||
}
|
||||
|
||||
@ -87,6 +88,15 @@ private:
|
||||
flog::debug("[{}]: '{}'", (uint32_t)addr, msg);
|
||||
}
|
||||
|
||||
void setBaudrate(double baudrate) {
|
||||
vfo->setSampleRate(baudrate*10.0, 12500);
|
||||
stop();
|
||||
reshape.setKeep(baudrate);
|
||||
reshape.setSkip((baudrate / 30.0) - baudrate);
|
||||
diag.setCount(baudrate);
|
||||
start();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
VFOManager::VFO* vfo;
|
||||
|
||||
@ -100,6 +110,7 @@ private:
|
||||
ImGui::SymbolDiagram diag;
|
||||
|
||||
int brId = 2;
|
||||
double baudrate = 2400;
|
||||
|
||||
OptionList<int, int> baudrates;
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
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/")
|
@ -1,139 +0,0 @@
|
||||
#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_() {
|
||||
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#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;
|
||||
};
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#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];
|
||||
};
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
#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;
|
||||
};
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
#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];
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
#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;
|
||||
};
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
#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;
|
||||
};
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
#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
|
||||
};
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
#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;
|
||||
};
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
#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 libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -25,30 +25,10 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -6,7 +6,7 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -25,30 +25,10 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -6,7 +6,7 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -25,30 +25,10 @@ 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 -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_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
|
||||
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 libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -25,30 +25,10 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -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 libspdlog-dev
|
||||
libcodec2-dev libudev-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -51,26 +51,6 @@ 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
|
||||
@ -86,9 +66,9 @@ 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 -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_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
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
# Generate package
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk1-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk1-bin, librtaudio6, libzstd1'
|
@ -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 libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -25,31 +25,11 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk2-bin, librtaudio6, libzstd1'
|
@ -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 libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -25,31 +25,11 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk2-bin, librtaudio6, libzstd1'
|
@ -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 libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -25,31 +25,11 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk-bin, librtaudio6, libzstd1'
|
@ -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 libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
@ -25,31 +25,11 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk-bin, librtaudio6, libzstd1'
|
@ -35,13 +35,11 @@ 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
|
||||
|
@ -7,93 +7,86 @@ mkdir sdrpp_windows_x64
|
||||
cp -Recurse $root_dir/* sdrpp_windows_x64/
|
||||
|
||||
# Copy core
|
||||
cp $build_dir/Release/* sdrpp_windows_x64/
|
||||
cp $build_dir/Debug/* sdrpp_windows_x64/
|
||||
cp $build_dir/core/Debug/sdrpp_core.pdb sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/PothosSDR/bin/volk.dll' sdrpp_windows_x64/
|
||||
|
||||
# Copy source modules
|
||||
cp $build_dir/source_modules/airspy_source/Release/airspy_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/airspy_source/Debug/airspy_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/airspy.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/airspyhf_source/Release/airspyhf_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/airspyhf_source/Debug/airspyhf_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/airspyhf.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/audio_source/Release/audio_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/audio_source/Debug/audio_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/bladerf_source/Release/bladerf_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/bladerf_source/Debug/bladerf_source.dll sdrpp_windows_x64/modules/
|
||||
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/file_source/Debug/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 $build_dir/source_modules/hackrf_source/Debug/hackrf_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/hermes_source/Debug/hermes_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/limesdr_source/Debug/limesdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/perseus_source/Debug/perseus_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/plutosdr_source/Debug/plutosdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/
|
||||
|
||||
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/Debug/rfspace_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
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/
|
||||
cp $build_dir/source_modules/rtl_sdr_source/Debug/rtl_sdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/rtlsdr.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/rtl_tcp_source/Release/rtl_tcp_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/rtl_tcp_source/Debug/rtl_tcp_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/sdrplay_source/Release/sdrplay_source.dll sdrpp_windows_x64/modules/ -ErrorAction SilentlyContinue
|
||||
cp $build_dir/source_modules/sdrplay_source/Debug/sdrplay_source.dll sdrpp_windows_x64/modules/ -ErrorAction SilentlyContinue
|
||||
cp 'C:/Program Files/SDRplay/API/x64/sdrplay_api.dll' sdrpp_windows_x64/ -ErrorAction SilentlyContinue
|
||||
|
||||
cp $build_dir/source_modules/sdrpp_server_source/Release/sdrpp_server_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/sdrpp_server_source/Debug/sdrpp_server_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/spyserver_source/Release/spyserver_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/spyserver_source/Debug/spyserver_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
# cp $build_dir/source_modules/usrp_source/Release/usrp_source.dll sdrpp_windows_x64/modules/
|
||||
# cp $build_dir/source_modules/usrp_source/Debug/usrp_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
|
||||
# Copy sink modules
|
||||
cp $build_dir/sink_modules/audio_sink/Release/audio_sink.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/sink_modules/audio_sink/Debug/audio_sink.dll sdrpp_windows_x64/modules/
|
||||
cp "C:/Program Files (x86)/RtAudio/bin/rtaudio.dll" sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/sink_modules/network_sink/Release/network_sink.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/sink_modules/network_sink/Debug/network_sink.dll sdrpp_windows_x64/modules/
|
||||
|
||||
|
||||
# Copy decoder modules
|
||||
cp $build_dir/decoder_modules/m17_decoder/Release/m17_decoder.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/decoder_modules/m17_decoder/Debug/m17_decoder.dll sdrpp_windows_x64/modules/
|
||||
cp "C:/Program Files/codec2/lib/libcodec2.dll" sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/decoder_modules/meteor_demodulator/Release/meteor_demodulator.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/decoder_modules/meteor_demodulator/Debug/meteor_demodulator.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/decoder_modules/radio/Release/radio.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/decoder_modules/radio/Debug/radio.dll sdrpp_windows_x64/modules/
|
||||
|
||||
|
||||
# Copy misc modules
|
||||
cp $build_dir/misc_modules/discord_integration/Release/discord_integration.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/discord_integration/Debug/discord_integration.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/frequency_manager/Release/frequency_manager.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/frequency_manager/Debug/frequency_manager.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/iq_exporter/Release/iq_exporter.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/iq_exporter/Debug/iq_exporter.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/recorder/Release/recorder.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/recorder/Debug/recorder.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/rigctl_client/Release/rigctl_client.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/rigctl_client/Debug/rigctl_client.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/rigctl_server/Release/rigctl_server.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/rigctl_server/Debug/rigctl_server.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/scanner/Release/scanner.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/scanner/Debug/scanner.dll sdrpp_windows_x64/modules/
|
||||
|
||||
|
||||
# Copy supporting libs
|
||||
|
12
min_broken/main.cpp
Normal file
12
min_broken/main.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
#include <mutex>
|
||||
|
||||
std::recursive_mutex mtx;
|
||||
|
||||
int main() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
printf("Works just fine!\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -249,6 +249,7 @@ 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)) {
|
||||
@ -283,11 +284,8 @@ 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)) {
|
||||
@ -296,7 +294,6 @@ 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,13 +41,14 @@ 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, use apt to install it:
|
||||
Then, run:
|
||||
|
||||
```sh
|
||||
sudo apt install path/to/the/sdrpp_debian_amd64.deb
|
||||
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
|
||||
```
|
||||
|
||||
**IMPORTANT: You must install the drivers for your SDR. Follow instructions from your manufacturer as to how to do this on your particular distro.**
|
||||
If `libvolk2-dev` is not available, use `libvolk1-dev`.
|
||||
|
||||
### Arch-based
|
||||
|
||||
@ -324,15 +325,13 @@ 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 | ✅ | ✅ | ✅ |
|
||||
@ -340,9 +339,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
|
||||
|
||||
@ -359,7 +358,6 @@ 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, Armand31",
|
||||
"author_url": "http://f4eed.wordpress.com, https://github.com/Armand31",
|
||||
"author_name": "Fred F4EED",
|
||||
"author_url": "http://f4eed.wordpress.com",
|
||||
"bands": [
|
||||
{
|
||||
"name": "137KHz - Radioamateur",
|
||||
@ -355,7 +355,7 @@
|
||||
"end": 54000000
|
||||
},
|
||||
{
|
||||
"name": "Radiodiffusion - Bande FM",
|
||||
"name": "Bande FM - Radiodif.",
|
||||
"type": "broadcast",
|
||||
"start": 80000000,
|
||||
"end": 108000000
|
||||
@ -396,12 +396,6 @@
|
||||
"start": 162362500,
|
||||
"end": 162587500
|
||||
},
|
||||
{
|
||||
"name": "Radiodiffusion - Bande DAB",
|
||||
"type": "broadcast",
|
||||
"start": 174000000,
|
||||
"end": 223000000
|
||||
},
|
||||
{
|
||||
"name": "Military Aviation",
|
||||
"type": "military",
|
||||
@ -414,12 +408,6 @@
|
||||
"start": 240000000,
|
||||
"end": 270000000
|
||||
},
|
||||
{
|
||||
"name": "Police (TETRAPOL)",
|
||||
"type": "military",
|
||||
"start": 380000000,
|
||||
"end": 400000000
|
||||
},
|
||||
{
|
||||
"name": "70cm - Radioamateur",
|
||||
"type": "amateur",
|
||||
@ -432,24 +420,12 @@
|
||||
"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",
|
||||
|
@ -1,285 +0,0 @@
|
||||
{
|
||||
"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,19 +217,14 @@ private:
|
||||
}
|
||||
|
||||
void startServer() {
|
||||
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);
|
||||
if (modeId == SINK_MODE_TCP) {
|
||||
listener = net::listen(hostname, port);
|
||||
if (listener) {
|
||||
listener->acceptAsync(clientHandler, this);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
flog::error("Failed to open socket: {}", e.what());
|
||||
else {
|
||||
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
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 ()
|
@ -1,309 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
#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);
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
#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
|
||||
}
|
@ -1,622 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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);
|
@ -1,21 +0,0 @@
|
||||
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 ()
|
@ -1,560 +0,0 @@
|
||||
#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();
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
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 ()
|
@ -1,510 +0,0 @@
|
||||
#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,17 +5,6 @@ file(GLOB SRC "src/*.cpp")
|
||||
|
||||
include(${SDRPP_MODULE_CMAKE})
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_link_directories(rfnm_source PRIVATE "C:/Program Files/RFNM/lib/")
|
||||
target_include_directories(rfnm_source PUBLIC "C:/Program Files/RFNM/include/")
|
||||
target_link_libraries(rfnm_source PRIVATE rfnm)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(LIBRFNM REQUIRED librfnm)
|
||||
|
||||
target_include_directories(rfnm_source PRIVATE ${LIBRFNM_INCLUDE_DIRS})
|
||||
target_link_directories(rfnm_source PRIVATE ${LIBRFNM_LIBRARY_DIRS})
|
||||
target_link_libraries(rfnm_source PRIVATE ${LIBRFNM_LIBRARIES})
|
||||
endif ()
|
||||
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)
|
@ -3,10 +3,9 @@
|
||||
#include <gui/gui.h>
|
||||
#include <gui/smgui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <librfnm/librfnm.h>
|
||||
#include <librfnm.h>
|
||||
#include <core.h>
|
||||
#include <utils/optionlist.h>
|
||||
#include <atomic>
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "rfnm_source",
|
||||
@ -62,7 +61,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void refresh() {
|
||||
void refresh() {
|
||||
#ifndef __ANDROID__
|
||||
devices.clear();
|
||||
auto list = librfnm::find(librfnm_transport::LIBRFNM_TRANSPORT_USB);
|
||||
for (const auto& info : list) {
|
||||
@ -76,6 +76,16 @@ private:
|
||||
// Save device
|
||||
devices.define((char*)info.motherboard.serial_number, devName, (char*)info.motherboard.serial_number);
|
||||
}
|
||||
#else
|
||||
// Check for device presence
|
||||
int vid, pid;
|
||||
devFd = backend::getDeviceFD(vid, pid, backend::RFNM_VIDPIDS);
|
||||
if (devFd < 0) { return; }
|
||||
|
||||
// Get device info
|
||||
std::string fakeName = "RFNM USB";
|
||||
devices.define(fakeName, fakeName, fakeName);
|
||||
#endif
|
||||
}
|
||||
|
||||
void select(const std::string& serial) {
|
||||
@ -91,131 +101,8 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the device
|
||||
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
|
||||
|
||||
// Define samplerates
|
||||
samplerates.clear();
|
||||
samplerates.define(61440000, "61.44 MHz", 2);
|
||||
samplerates.define(122880000, "122.88 MHz", 1);
|
||||
|
||||
// Define daughterboards
|
||||
daughterboards.clear();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// If not present, skip
|
||||
if (!dev->s->hwinfo.daughterboard[i].board_id) { continue; }
|
||||
|
||||
// Format the daughterboard name
|
||||
std::string name = (i ? "[SEC] " : "[PRI] ") + std::string(dev->s->hwinfo.daughterboard[i].user_readable_name);
|
||||
|
||||
// Add the daughterboard to the list
|
||||
daughterboards.define(name, name, i);
|
||||
}
|
||||
|
||||
// Load options (TODO)
|
||||
srId = samplerates.keyId(61440000);
|
||||
dgbId = 0;
|
||||
|
||||
// Select the daughterboard
|
||||
selectDaughterboard(dev, 0);
|
||||
|
||||
// Update samplerate
|
||||
sampleRate = samplerates.key(srId);
|
||||
|
||||
// Close device
|
||||
delete dev;
|
||||
|
||||
// Save serial number
|
||||
selectedSerial = serial;
|
||||
}
|
||||
|
||||
struct PathConfig {
|
||||
rfnm_rf_path path;
|
||||
int chId;
|
||||
uint16_t appliesCh;
|
||||
|
||||
bool operator==(const PathConfig& b) const {
|
||||
return b.path == path;
|
||||
}
|
||||
};
|
||||
|
||||
void selectDaughterboard(librfnm* dev, int id) {
|
||||
// If no daugherboard is populated, give up
|
||||
if (!dev->s->hwinfo.daughterboard[0].board_id && !dev->s->hwinfo.daughterboard[1].board_id) {
|
||||
flog::error("The selected device has no daughterboards");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the ID is not populated, select the other one
|
||||
if (id >= 2 || !dev->s->hwinfo.daughterboard[id].board_id) {
|
||||
selectDaughterboard(dev, 1 - id);
|
||||
}
|
||||
|
||||
// Compute the channel offset
|
||||
int offset = 0;
|
||||
for (int i = 0; i < id; i++) {
|
||||
offset += dev->s->hwinfo.daughterboard[i].rx_ch_cnt;
|
||||
}
|
||||
|
||||
// Define antenna paths by going through all channels
|
||||
paths.clear();
|
||||
int count = dev->s->hwinfo.daughterboard[id].rx_ch_cnt;
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Go through each possible path
|
||||
for (int j = 0; j < 10; j++) {
|
||||
// If it's the null path, stop searching
|
||||
rfnm_rf_path path = dev->s->rx.ch[offset + i].path_possible[j];
|
||||
if (path == RFNM_PATH_NULL) { continue; }
|
||||
|
||||
// Get the path
|
||||
PathConfig pc = { path, offset + i, (uint16_t)(1 << (offset + i + 8))};
|
||||
|
||||
// If it's not in the list, add it
|
||||
if (!paths.valueExists(pc)) {
|
||||
std::string name = librfnm::rf_path_to_string(pc.path);
|
||||
std::string capName = name;
|
||||
if (std::islower(capName[0])) { capName[0] = std::toupper(capName[0]); }
|
||||
paths.define(name, capName, pc);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the preferred path
|
||||
PathConfig preferred_pc = { dev->s->rx.ch[offset + i].path_preferred, 0, 0 };
|
||||
|
||||
// Make sure the path is accessible or give up
|
||||
if (!paths.valueExists(preferred_pc)) { continue; }
|
||||
|
||||
// Set this channel as the channel of its prefered path (cursed af but lazy)
|
||||
const PathConfig& pc = paths.value(paths.valueId(preferred_pc));
|
||||
((PathConfig*)&pc)->chId = offset + i;
|
||||
((PathConfig*)&pc)->appliesCh = (uint16_t)(1 << (offset + i + 8));
|
||||
}
|
||||
|
||||
// Dump antenna paths
|
||||
for (int i = 0; i < paths.size(); i++) {
|
||||
flog::debug("PATH[{}]: Name={}, Ch={}, Path={}", i, paths.name(i), paths.value(i).chId, (int)paths.value(i).path);
|
||||
}
|
||||
|
||||
// Load configuration (TODO)
|
||||
selectedPath = paths.key(0);
|
||||
|
||||
// Select antenna path
|
||||
selectPath(dev, id, selectedPath);
|
||||
|
||||
// Save selected daughterboard
|
||||
dgbId = id;
|
||||
}
|
||||
|
||||
void selectPath(librfnm* dev, int dgbId, const std::string& path) {
|
||||
// If the path doesn't exist, select the first path
|
||||
if (!paths.keyExists(path)) {
|
||||
selectPath(dev, dgbId, paths.key(0));
|
||||
}
|
||||
|
||||
// Save selected path
|
||||
selectedPath = path;
|
||||
pathId = paths.keyId(path);
|
||||
currentPath = paths.value(pathId);
|
||||
// // Open the device
|
||||
// librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
|
||||
|
||||
// Define bandwidths
|
||||
bandwidths.clear();
|
||||
@ -227,8 +114,27 @@ private:
|
||||
}
|
||||
|
||||
// Get gain range
|
||||
gainMin = dev->s->rx.ch[currentPath.chId].gain_range.min;
|
||||
gainMax = dev->s->rx.ch[currentPath.chId].gain_range.max;
|
||||
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;
|
||||
}
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
@ -247,7 +153,24 @@ private:
|
||||
if (_this->running) { return; }
|
||||
|
||||
// Open the device
|
||||
#ifndef __ANDROID__
|
||||
_this->openDev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
|
||||
#else
|
||||
_this->openDev = new librfnm(_this->devFd);
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
@ -263,24 +186,7 @@ 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;
|
||||
@ -293,20 +199,13 @@ 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->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_OFF;
|
||||
_this->openDev->set(_this->currentPath.appliesCh);
|
||||
|
||||
// Flush buffers
|
||||
_this->openDev->rx_flush();
|
||||
_this->openDev->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
|
||||
_this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
||||
|
||||
// Close device
|
||||
delete _this->openDev;
|
||||
@ -322,8 +221,8 @@ private:
|
||||
static void tune(double freq, void* ctx) {
|
||||
RFNMSourceModule* _this = (RFNMSourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].freq = freq;
|
||||
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||
_this->openDev->librfnm_s->rx.ch[0].freq = freq;
|
||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
||||
if (fail != rfnm_api_failcode::RFNM_API_OK) {
|
||||
flog::error("Failed to tune: {}", (int)fail);
|
||||
}
|
||||
@ -360,48 +259,11 @@ 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
|
||||
}
|
||||
|
||||
@ -409,16 +271,16 @@ private:
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_rfnm_gain_", _this->name), &_this->gain, _this->gainMin, _this->gainMax)) {
|
||||
if (_this->running) {
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain;
|
||||
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||
_this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
|
||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
if (SmGui::Checkbox(CONCAT("FM Notch##_rfnm_", _this->name), &_this->fmNotch)) {
|
||||
if (_this->running) {
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
||||
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||
_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);
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
@ -427,18 +289,12 @@ private:
|
||||
void worker() {
|
||||
librfnm_rx_buf* lrxbuf;
|
||||
int sampCount = bufferSize/4;
|
||||
uint8_t ch = (1 << currentPath.chId);
|
||||
|
||||
// Define number of buffers per swap to maintain 200 fps
|
||||
int maxBufCount = STREAM_BUFFER_SIZE / sampCount;
|
||||
int bufCount = (sampleRate / sampCount) / 200;
|
||||
if (bufCount <= 0) { bufCount = 1; }
|
||||
if (bufCount > maxBufCount) { bufCount = maxBufCount; }
|
||||
// TODO: Define number of buffers per swap to maintain 200 fps
|
||||
|
||||
int count = 0;
|
||||
while (run) {
|
||||
while (true) {
|
||||
// Receive a buffer
|
||||
auto fail = openDev->rx_dqbuf(&lrxbuf, ch, 1000);
|
||||
auto fail = openDev->rx_dqbuf(&lrxbuf, LIBRFNM_CH0, 1000);
|
||||
if (fail == rfnm_api_failcode::RFNM_API_DQBUF_NO_DATA) {
|
||||
flog::error("Dequeue buffer didn't have any data");
|
||||
continue;
|
||||
@ -446,17 +302,13 @@ private:
|
||||
else if (fail) { break; }
|
||||
|
||||
// Convert buffer to CF32
|
||||
volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*sampCount], (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
|
||||
volk_16i_s32f_convert_32f((float*)stream.writeBuf, (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
|
||||
|
||||
// Reque buffer
|
||||
openDev->rx_qbuf(lrxbuf);
|
||||
|
||||
// Swap data
|
||||
if (count >= bufCount) {
|
||||
if (!stream.swap(count*sampCount)) { break; }
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if (!stream.swap(sampCount)) { break; }
|
||||
}
|
||||
|
||||
flog::debug("Worker exiting");
|
||||
@ -471,15 +323,12 @@ 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;
|
||||
@ -487,11 +336,12 @@ 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;
|
||||
#ifdef __ANDROID__
|
||||
int devFd = 0;
|
||||
#endif
|
||||
|
||||
std::thread workerThread;
|
||||
};
|
||||
|
||||
|
@ -33,13 +33,10 @@ public:
|
||||
SpectranSourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
AARTSAAPI_Result res;
|
||||
if ((res = AARTSAAPI_Init(AARTSAAPI_MEMORY_MEDIUM)) != AARTSAAPI_OK) {
|
||||
flog::error("Failed to initialize the RTSAAPI: {}", (uint32_t)res);
|
||||
if (AARTSAAPI_Init(AARTSAAPI_MEMORY_MEDIUM) != AARTSAAPI_OK) {
|
||||
return;
|
||||
}
|
||||
if ((res = AARTSAAPI_Open(&api)) != AARTSAAPI_OK) {
|
||||
flog::error("Failed to open the RTSAAPI: {}", (uint32_t)res);
|
||||
if (AARTSAAPI_Open(&api) != AARTSAAPI_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -455,16 +452,13 @@ private:
|
||||
|
||||
void updateRef() {
|
||||
// Get and update bounds
|
||||
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);
|
||||
AARTSAAPI_Config config;
|
||||
AARTSAAPI_ConfigInfo refInfo;
|
||||
AARTSAAPI_ConfigFind(&dev, &croot, &config, L"main/reflevel");
|
||||
AARTSAAPI_ConfigGetInfo(&dev, &config, &refInfo);
|
||||
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,4 +1,5 @@
|
||||
#include <core.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return sdrpp_main(argc, argv);
|
||||
|
Reference in New Issue
Block a user