mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-07-09 10:35:21 +02:00
Compare commits
51 Commits
better_ins
...
new_rigctl
Author | SHA1 | Date | |
---|---|---|---|
711ed7711f | |||
a8e6f24b29 | |||
b914587228 | |||
3c1d0c7422 | |||
11f87e0fe2 | |||
e192cb963b | |||
fe407a2f27 | |||
6891d0bb0f | |||
b835d07573 | |||
f205d97b52 | |||
628dcfcce0 | |||
d1e7cc56b4 | |||
334860c963 | |||
69161253e8 | |||
5ab3428b90 | |||
7f002f6276 | |||
a728403a3f | |||
0f1d2da3b7 | |||
6d0b65c27f | |||
f640cdcb6a | |||
80a90e13d9 | |||
3982db73d3 | |||
bd64f07a20 | |||
c9950d9331 | |||
9bc609f4e4 | |||
bcc8e20e66 | |||
b07e828fed | |||
bd24a4a5eb | |||
fe4a7b32a7 | |||
0e1ab29b5d | |||
fbbafddd3d | |||
1cbc8ec6f5 | |||
9f65e3ec71 | |||
08f3a7d201 | |||
9ce62f8885 | |||
caeaa2d46c | |||
7ae030a3a6 | |||
1b27379a3d | |||
e52123038e | |||
ec8c60111d | |||
f61799cf5f | |||
17eccf5156 | |||
e835c8dd9a | |||
acb1be121c | |||
0fa89614bb | |||
256affd918 | |||
e80cdbf248 | |||
75e66226c3 | |||
c2f0e756a5 | |||
79dd5bdcbb | |||
6dce28345c |
19
.github/workflows/build_all.yml
vendored
19
.github/workflows/build_all.yml
vendored
@ -71,14 +71,17 @@ jobs:
|
|||||||
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
|
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
|
- name: Install libperseus-sdr
|
||||||
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
|
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake -DCMAKE_BUILD_TYPE=Release "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
|
||||||
|
|
||||||
- name: Install librfnm
|
- name: Install librfnm
|
||||||
run: git clone https://github.com/AlexandreRouma/librfnm ; cd librfnm ; mkdir build ; cd build ; cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
|
run: git clone https://github.com/AlexandreRouma/librfnm ; cd librfnm ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
|
||||||
|
|
||||||
|
- name: Install libfobos
|
||||||
|
run: git clone https://github.com/AlexandreRouma/libfobos ; cd libfobos ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
|
||||||
|
|
||||||
- name: Prepare CMake
|
- name: Prepare CMake
|
||||||
working-directory: ${{runner.workspace}}/build
|
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
|
run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
@ -127,12 +130,15 @@ jobs:
|
|||||||
- name: Install librfnm
|
- 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 ..
|
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
|
- 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 ../../
|
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
|
- name: Prepare CMake
|
||||||
working-directory: ${{runner.workspace}}/build
|
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 -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
@ -181,12 +187,15 @@ jobs:
|
|||||||
- name: Install librfnm
|
- 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 ..
|
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
|
- 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 ../../
|
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
|
- name: Prepare CMake
|
||||||
working-directory: ${{runner.workspace}}/build
|
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 -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
@ -15,9 +15,11 @@ option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)
|
|||||||
option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF)
|
option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF)
|
||||||
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" 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_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_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_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_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
||||||
|
option(OPT_BUILD_KCSDR_SOURCE "Build KCSDR Source Module (Dependencies: libkcsdr)" OFF)
|
||||||
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
|
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
|
||||||
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
|
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
|
||||||
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
|
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
|
||||||
@ -43,6 +45,7 @@ option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: port
|
|||||||
|
|
||||||
# Decoders
|
# Decoders
|
||||||
option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF)
|
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_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_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_M17_DECODER "Build the M17 decoder module (Dependencies: codec2)" OFF)
|
||||||
@ -97,56 +100,13 @@ set(SDRPP_MODULE_COMPILER_FLAGS ${SDRPP_COMPILER_FLAGS})
|
|||||||
|
|
||||||
# Set a default install prefix
|
# Set a default install prefix
|
||||||
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
if (MSVC)
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
set(CMAKE_INSTALL_PREFIX "C:/Program Files/SDR++/" CACHE PATH "..." FORCE)
|
|
||||||
elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
|
||||||
set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "..." FORCE)
|
set(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "..." FORCE)
|
||||||
else()
|
else()
|
||||||
set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "..." FORCE)
|
set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "..." FORCE)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Include standard install directory definitions
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
# Set up SDR++ directory load paths
|
|
||||||
if (MSVC)
|
|
||||||
set(SDRPP_MODULES_LOAD_DIR "./modules")
|
|
||||||
set(SDRPP_RES_LOAD_DIR "./res")
|
|
||||||
elseif (USE_BUNDLE_DEFAULTS)
|
|
||||||
set(SDRPP_MODULES_LOAD_DIR "../Plugins")
|
|
||||||
set(SDRPP_RES_LOAD_DIR "../Resources")
|
|
||||||
elseif (ANDROID)
|
|
||||||
set(SDRPP_MODULES_LOAD_DIR "/modules")
|
|
||||||
set(SDRPP_RES_LOAD_DIR "/res")
|
|
||||||
else()
|
|
||||||
set(SDRPP_MODULES_LOAD_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/sdrpp/plugins")
|
|
||||||
set(SDRPP_RES_LOAD_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/sdrpp")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Set up SDR++ directory install paths
|
|
||||||
if (MSVC)
|
|
||||||
set(SDRPP_BIN_INSTALL_DIR "/")
|
|
||||||
set(SDRPP_LIB_INSTALL_DIR "/")
|
|
||||||
set(SDRPP_MODULES_INSTALL_DIR "modules")
|
|
||||||
set(SDRPP_RES_INSTALL_DIR "res")
|
|
||||||
elseif (USE_BUNDLE_DEFAULTS)
|
|
||||||
set(SDRPP_BIN_INSTALL_DIR "Contents/MacOS")
|
|
||||||
set(SDRPP_LIB_INSTALL_DIR "Contents/Frameworks")
|
|
||||||
set(SDRPP_MODULES_INSTALL_DIR "Contents/Plugins")
|
|
||||||
set(SDRPP_RES_INSTALL_DIR "Contents/Resources")
|
|
||||||
elseif (ANDROID)
|
|
||||||
set(SDRPP_BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
|
|
||||||
set(SDRPP_LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}")
|
|
||||||
set(SDRPP_MODULES_INSTALL_DIR "/modules")
|
|
||||||
set(SDRPP_RES_INSTALL_DIR "/res")
|
|
||||||
else()
|
|
||||||
set(SDRPP_BIN_INSTALL_DIR "${CMAKE_INSTALL_BINDIR}")
|
|
||||||
set(SDRPP_LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}")
|
|
||||||
set(SDRPP_MODULES_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/sdrpp/plugins")
|
|
||||||
set(SDRPP_RES_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/sdrpp")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Configure toolchain for android
|
# Configure toolchain for android
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS
|
set(CMAKE_SHARED_LINKER_FLAGS
|
||||||
@ -185,6 +145,10 @@ if (OPT_BUILD_FILE_SOURCE)
|
|||||||
add_subdirectory("source_modules/file_source")
|
add_subdirectory("source_modules/file_source")
|
||||||
endif (OPT_BUILD_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)
|
if (OPT_BUILD_HACKRF_SOURCE)
|
||||||
add_subdirectory("source_modules/hackrf_source")
|
add_subdirectory("source_modules/hackrf_source")
|
||||||
endif (OPT_BUILD_HACKRF_SOURCE)
|
endif (OPT_BUILD_HACKRF_SOURCE)
|
||||||
@ -197,6 +161,10 @@ if (OPT_BUILD_HERMES_SOURCE)
|
|||||||
add_subdirectory("source_modules/hermes_source")
|
add_subdirectory("source_modules/hermes_source")
|
||||||
endif (OPT_BUILD_HERMES_SOURCE)
|
endif (OPT_BUILD_HERMES_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_KCSDR_SOURCE)
|
||||||
|
add_subdirectory("source_modules/kcsdr_source")
|
||||||
|
endif (OPT_BUILD_KCSDR_SOURCE)
|
||||||
|
|
||||||
if (OPT_BUILD_LIMESDR_SOURCE)
|
if (OPT_BUILD_LIMESDR_SOURCE)
|
||||||
add_subdirectory("source_modules/limesdr_source")
|
add_subdirectory("source_modules/limesdr_source")
|
||||||
endif (OPT_BUILD_LIMESDR_SOURCE)
|
endif (OPT_BUILD_LIMESDR_SOURCE)
|
||||||
@ -285,6 +253,10 @@ if (OPT_BUILD_ATV_DECODER)
|
|||||||
add_subdirectory("decoder_modules/atv_decoder")
|
add_subdirectory("decoder_modules/atv_decoder")
|
||||||
endif (OPT_BUILD_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)
|
if (OPT_BUILD_FALCON9_DECODER)
|
||||||
add_subdirectory("decoder_modules/falcon9_decoder")
|
add_subdirectory("decoder_modules/falcon9_decoder")
|
||||||
endif (OPT_BUILD_FALCON9_DECODER)
|
endif (OPT_BUILD_FALCON9_DECODER)
|
||||||
@ -406,18 +378,17 @@ endif ()
|
|||||||
# Create module cmake file
|
# Create module cmake file
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/sdrpp_module.cmake ${CMAKE_CURRENT_BINARY_DIR}/sdrpp_module.cmake @ONLY)
|
configure_file(${CMAKE_SOURCE_DIR}/sdrpp_module.cmake ${CMAKE_CURRENT_BINARY_DIR}/sdrpp_module.cmake @ONLY)
|
||||||
|
|
||||||
# Create desktop entry file
|
# Install directives
|
||||||
|
install(TARGETS sdrpp DESTINATION bin)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/bandplans DESTINATION share/sdrpp)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/colormaps DESTINATION share/sdrpp)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/fonts DESTINATION share/sdrpp)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/icons DESTINATION share/sdrpp)
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/themes DESTINATION share/sdrpp)
|
||||||
configure_file(${CMAKE_SOURCE_DIR}/sdrpp.desktop ${CMAKE_CURRENT_BINARY_DIR}/sdrpp.desktop @ONLY)
|
configure_file(${CMAKE_SOURCE_DIR}/sdrpp.desktop ${CMAKE_CURRENT_BINARY_DIR}/sdrpp.desktop @ONLY)
|
||||||
|
|
||||||
# Install directives
|
|
||||||
install(TARGETS sdrpp DESTINATION ${SDRPP_BIN_INSTALL_DIR})
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/bandplans DESTINATION ${SDRPP_RES_INSTALL_DIR})
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/colormaps DESTINATION ${SDRPP_RES_INSTALL_DIR})
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/fonts DESTINATION ${SDRPP_RES_INSTALL_DIR})
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/icons DESTINATION ${SDRPP_RES_INSTALL_DIR})
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/root/res/themes DESTINATION ${SDRPP_RES_INSTALL_DIR})
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sdrpp.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sdrpp.desktop DESTINATION share/applications)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Create uninstall target
|
# Create uninstall target
|
||||||
|
@ -18,10 +18,6 @@ if (MSVC)
|
|||||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Add compiler definitions for the directories
|
|
||||||
add_definitions(-DSDRPP_MODULES_LOAD_DIR="${SDRPP_MODULES_LOAD_DIR}")
|
|
||||||
add_definitions(-DSDRPP_RES_LOAD_DIR="${SDRPP_RES_LOAD_DIR}")
|
|
||||||
|
|
||||||
# Configure backend sources
|
# Configure backend sources
|
||||||
if (OPT_BACKEND_GLFW)
|
if (OPT_BACKEND_GLFW)
|
||||||
file(GLOB_RECURSE BACKEND_SRC "backends/glfw/*.cpp" "backends/glfw/*.c")
|
file(GLOB_RECURSE BACKEND_SRC "backends/glfw/*.cpp" "backends/glfw/*.c")
|
||||||
@ -37,6 +33,9 @@ add_library(sdrpp_core SHARED ${SRC} ${BACKEND_SRC})
|
|||||||
# Set compiler options
|
# Set compiler options
|
||||||
target_compile_options(sdrpp_core PRIVATE ${SDRPP_COMPILER_FLAGS})
|
target_compile_options(sdrpp_core PRIVATE ${SDRPP_COMPILER_FLAGS})
|
||||||
|
|
||||||
|
# Set the install prefix
|
||||||
|
target_compile_definitions(sdrpp_core PUBLIC INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}")
|
||||||
|
|
||||||
# Include core headers
|
# Include core headers
|
||||||
target_include_directories(sdrpp_core PUBLIC "src/")
|
target_include_directories(sdrpp_core PUBLIC "src/")
|
||||||
target_include_directories(sdrpp_core PUBLIC "src/imgui")
|
target_include_directories(sdrpp_core PUBLIC "src/imgui")
|
||||||
@ -168,4 +167,4 @@ set(CORE_FILES ${RUNTIME_OUTPUT_DIRECTORY} PARENT_SCOPE)
|
|||||||
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
# cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/dev/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||||
|
|
||||||
# Install directives
|
# Install directives
|
||||||
install(TARGETS sdrpp_core DESTINATION ${SDRPP_LIB_INSTALL_DIR})
|
install(TARGETS sdrpp_core DESTINATION lib)
|
@ -24,16 +24,14 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef INSTALL_PREFIX
|
||||||
// Default install dirs to make the IDE happy
|
#ifdef __APPLE__
|
||||||
#ifndef SDRPP_MODULES_LOAD_DIR
|
#define INSTALL_PREFIX "/usr/local"
|
||||||
#define SDRPP_MODULES_LOAD_DIR ""
|
#else
|
||||||
|
#define INSTALL_PREFIX "/usr"
|
||||||
#endif
|
#endif
|
||||||
#ifndef SDRPP_RES_LOAD_DIR
|
|
||||||
#define SDRPP_RES_LOAD_DIR ""
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace core {
|
namespace core {
|
||||||
ConfigManager configManager;
|
ConfigManager configManager;
|
||||||
ModuleManager moduleManager;
|
ModuleManager moduleManager;
|
||||||
@ -149,12 +147,12 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
defConfig["menuElements"][3]["name"] = "Sinks";
|
defConfig["menuElements"][3]["name"] = "Sinks";
|
||||||
defConfig["menuElements"][3]["open"] = true;
|
defConfig["menuElements"][3]["open"] = true;
|
||||||
|
|
||||||
defConfig["menuElements"][3]["name"] = "Frequency Manager";
|
defConfig["menuElements"][4]["name"] = "Frequency Manager";
|
||||||
defConfig["menuElements"][3]["open"] = true;
|
|
||||||
|
|
||||||
defConfig["menuElements"][4]["name"] = "VFO Color";
|
|
||||||
defConfig["menuElements"][4]["open"] = true;
|
defConfig["menuElements"][4]["open"] = true;
|
||||||
|
|
||||||
|
defConfig["menuElements"][5]["name"] = "VFO Color";
|
||||||
|
defConfig["menuElements"][5]["open"] = true;
|
||||||
|
|
||||||
defConfig["menuElements"][6]["name"] = "Band Plan";
|
defConfig["menuElements"][6]["name"] = "Band Plan";
|
||||||
defConfig["menuElements"][6]["open"] = true;
|
defConfig["menuElements"][6]["open"] = true;
|
||||||
|
|
||||||
@ -175,16 +173,22 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["File Source"]["module"] = "file_source";
|
defConfig["moduleInstances"]["File Source"]["module"] = "file_source";
|
||||||
defConfig["moduleInstances"]["File Source"]["enabled"] = true;
|
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"]["module"] = "hackrf_source";
|
||||||
defConfig["moduleInstances"]["HackRF Source"]["enabled"] = true;
|
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"]["module"] = "hermes_source";
|
||||||
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
|
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
|
||||||
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
|
defConfig["moduleInstances"]["Network Source"]["module"] = "network_source";
|
||||||
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["Network Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
|
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
|
||||||
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
|
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"]["module"] = "rfnm_source";
|
||||||
defConfig["moduleInstances"]["RFNM Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["RFNM Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
|
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
|
||||||
@ -197,8 +201,12 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["SDR++ Server Source"]["module"] = "sdrpp_server_source";
|
defConfig["moduleInstances"]["SDR++ Server Source"]["module"] = "sdrpp_server_source";
|
||||||
defConfig["moduleInstances"]["SDR++ Server Source"]["enabled"] = true;
|
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"]["module"] = "spyserver_source";
|
||||||
defConfig["moduleInstances"]["SpyServer Source"]["enabled"] = true;
|
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"]["Audio Sink"] = "audio_sink";
|
||||||
defConfig["moduleInstances"]["Network Sink"] = "network_sink";
|
defConfig["moduleInstances"]["Network Sink"] = "network_sink";
|
||||||
@ -224,12 +232,19 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
defConfig["modules"] = json::array();
|
defConfig["modules"] = json::array();
|
||||||
|
|
||||||
defConfig["offsetMode"] = (int)0; // Off
|
defConfig["offsets"]["SpyVerter"] = 120000000.0;
|
||||||
defConfig["offset"] = 0.0;
|
defConfig["offsets"]["Ham-It-Up"] = 125000000.0;
|
||||||
|
defConfig["offsets"]["MMDS S-band (1998MHz)"] = -1998000000.0;
|
||||||
|
defConfig["offsets"]["DK5AV X-Band"] = -6800000000.0;
|
||||||
|
defConfig["offsets"]["Ku LNB (9750MHz)"] = -9750000000.0;
|
||||||
|
defConfig["offsets"]["Ku LNB (10700MHz)"] = -10700000000.0;
|
||||||
|
|
||||||
|
defConfig["selectedOffset"] = "None";
|
||||||
|
defConfig["manualOffset"] = 0.0;
|
||||||
defConfig["showMenu"] = true;
|
defConfig["showMenu"] = true;
|
||||||
defConfig["showWaterfall"] = true;
|
defConfig["showWaterfall"] = true;
|
||||||
defConfig["source"] = "";
|
defConfig["source"] = "";
|
||||||
defConfig["decimationPower"] = 0;
|
defConfig["decimation"] = 1;
|
||||||
defConfig["iqCorrection"] = false;
|
defConfig["iqCorrection"] = false;
|
||||||
defConfig["invertIQ"] = false;
|
defConfig["invertIQ"] = false;
|
||||||
|
|
||||||
@ -250,15 +265,20 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
defConfig["lockMenuOrder"] = false;
|
defConfig["lockMenuOrder"] = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#if defined(_WIN32)
|
||||||
defConfig["modulesDirectory"] = root + SDRPP_MODULES_LOAD_DIR;
|
defConfig["modulesDirectory"] = "./modules";
|
||||||
defConfig["resourcesDirectory"] = root + SDRPP_RES_LOAD_DIR;
|
defConfig["resourcesDirectory"] = "./res";
|
||||||
|
#elif defined(IS_MACOS_BUNDLE)
|
||||||
|
defConfig["modulesDirectory"] = "../Plugins";
|
||||||
|
defConfig["resourcesDirectory"] = "../Resources";
|
||||||
|
#elif defined(__ANDROID__)
|
||||||
|
defConfig["modulesDirectory"] = root + "/modules";
|
||||||
|
defConfig["resourcesDirectory"] = root + "/res";
|
||||||
#else
|
#else
|
||||||
defConfig["modulesDirectory"] = SDRPP_MODULES_LOAD_DIR;
|
defConfig["modulesDirectory"] = INSTALL_PREFIX "/lib/sdrpp/plugins";
|
||||||
defConfig["resourcesDirectory"] = SDRPP_RES_LOAD_DIR;
|
defConfig["resourcesDirectory"] = INSTALL_PREFIX "/share/sdrpp";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
flog::info("Loading config");
|
flog::info("Loading config");
|
||||||
core::configManager.setPath(root + "/config.json");
|
core::configManager.setPath(root + "/config.json");
|
||||||
@ -305,12 +325,18 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
// Remove unused elements
|
// Remove unused elements
|
||||||
auto items = core::configManager.conf.items();
|
auto items = core::configManager.conf.items();
|
||||||
|
auto newConf = core::configManager.conf;
|
||||||
|
bool configCorrected = false;
|
||||||
for (auto const& item : items) {
|
for (auto const& item : items) {
|
||||||
if (!defConfig.contains(item.key())) {
|
if (!defConfig.contains(item.key())) {
|
||||||
flog::info("Unused key in config {0}, repairing", item.key());
|
flog::info("Unused key in config {0}, repairing", item.key());
|
||||||
core::configManager.conf.erase(item.key());
|
newConf.erase(item.key());
|
||||||
|
configCorrected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (configCorrected) {
|
||||||
|
core::configManager.conf = newConf;
|
||||||
|
}
|
||||||
|
|
||||||
// Update to new module representation in config if needed
|
// Update to new module representation in config if needed
|
||||||
for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {
|
for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {
|
||||||
|
@ -37,9 +37,12 @@ namespace sdrpp_credits {
|
|||||||
const char* hardwareDonators[] = {
|
const char* hardwareDonators[] = {
|
||||||
"Aaronia AG",
|
"Aaronia AG",
|
||||||
"Airspy",
|
"Airspy",
|
||||||
|
"Alex 4Z5LV",
|
||||||
"Analog Devices",
|
"Analog Devices",
|
||||||
"CaribouLabs",
|
"CaribouLabs",
|
||||||
|
"Deepace",
|
||||||
"Ettus Research",
|
"Ettus Research",
|
||||||
|
"Harogic",
|
||||||
"Howard Su",
|
"Howard Su",
|
||||||
"MicroPhase",
|
"MicroPhase",
|
||||||
"Microtelecom",
|
"Microtelecom",
|
||||||
@ -47,6 +50,7 @@ namespace sdrpp_credits {
|
|||||||
"Nuand",
|
"Nuand",
|
||||||
"RFNM",
|
"RFNM",
|
||||||
"RFspace",
|
"RFspace",
|
||||||
|
"RigExpert",
|
||||||
"RTL-SDRblog",
|
"RTL-SDRblog",
|
||||||
"SDRplay"
|
"SDRplay"
|
||||||
};
|
};
|
||||||
|
@ -5,113 +5,120 @@
|
|||||||
#include <gui/main_window.h>
|
#include <gui/main_window.h>
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <utils/optionlist.h>
|
||||||
|
#include <gui/dialogs/dialog_box.h>
|
||||||
|
|
||||||
namespace sourcemenu {
|
namespace sourcemenu {
|
||||||
int offsetMode = 0;
|
|
||||||
int sourceId = 0;
|
int sourceId = 0;
|
||||||
double customOffset = 0.0;
|
EventHandler<std::string> sourcesChangedHandler;
|
||||||
double effectiveOffset = 0.0;
|
EventHandler<std::string> sourceUnregisterHandler;
|
||||||
int decimationPower = 0;
|
OptionList<std::string, std::string> sources;
|
||||||
|
std::string selectedSource;
|
||||||
|
|
||||||
|
int decimId = 0;
|
||||||
|
OptionList<int, int> decimations;
|
||||||
|
|
||||||
bool iqCorrection = false;
|
bool iqCorrection = false;
|
||||||
bool invertIQ = false;
|
bool invertIQ = false;
|
||||||
|
|
||||||
EventHandler<std::string> sourceRegisteredHandler;
|
int offsetId = 0;
|
||||||
EventHandler<std::string> sourceUnregisterHandler;
|
double manualOffset = 0.0;
|
||||||
EventHandler<std::string> sourceUnregisteredHandler;
|
std::string selectedOffset;
|
||||||
|
double effectiveOffset = 0.0;
|
||||||
|
OptionList<std::string, double> offsets;
|
||||||
|
std::map<std::string, double> namedOffsets;
|
||||||
|
|
||||||
std::vector<std::string> sourceNames;
|
bool showAddOffsetDialog = false;
|
||||||
std::string sourceNamesTxt;
|
char newOffsetName[1024];
|
||||||
std::string selectedSource;
|
double newOffset = 0.0;
|
||||||
|
|
||||||
|
bool showDelOffsetDialog = false;
|
||||||
|
std::string delOffsetName = "";
|
||||||
|
|
||||||
|
// Offset IDs
|
||||||
enum {
|
enum {
|
||||||
OFFSET_MODE_NONE,
|
OFFSET_ID_NONE,
|
||||||
OFFSET_MODE_CUSTOM,
|
OFFSET_ID_MANUAL,
|
||||||
OFFSET_MODE_SPYVERTER,
|
OFFSET_ID_CUSTOM_BASE
|
||||||
OFFSET_MODE_HAM_IT_UP,
|
|
||||||
OFFSET_MODE_MMDS_SB_1998,
|
|
||||||
OFFSET_MODE_DK5AV_XB,
|
|
||||||
OFFSET_MODE_KU_LNB_9750,
|
|
||||||
OFFSET_MODE_KU_LNB_10700,
|
|
||||||
_OFFSET_MODE_COUNT
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* offsetModesTxt = "None\0"
|
|
||||||
"Custom\0"
|
|
||||||
"SpyVerter\0"
|
|
||||||
"Ham-It-Up\0"
|
|
||||||
"MMDS S-band (1998MHz)\0"
|
|
||||||
"DK5AV X-Band\0"
|
|
||||||
"Ku LNB (9750MHz)\0"
|
|
||||||
"Ku LNB (10700MHz)\0";
|
|
||||||
|
|
||||||
const char* decimationStages = "None\0"
|
|
||||||
"2\0"
|
|
||||||
"4\0"
|
|
||||||
"8\0"
|
|
||||||
"16\0"
|
|
||||||
"32\0"
|
|
||||||
"64\0";
|
|
||||||
|
|
||||||
void updateOffset() {
|
void updateOffset() {
|
||||||
if (offsetMode == OFFSET_MODE_CUSTOM) { effectiveOffset = customOffset; }
|
// Compute the effective offset
|
||||||
else if (offsetMode == OFFSET_MODE_SPYVERTER) {
|
switch (offsetId) {
|
||||||
effectiveOffset = 120000000;
|
case OFFSET_ID_NONE:
|
||||||
} // 120MHz Up-conversion
|
|
||||||
else if (offsetMode == OFFSET_MODE_HAM_IT_UP) {
|
|
||||||
effectiveOffset = 125000000;
|
|
||||||
} // 125MHz Up-conversion
|
|
||||||
else if (offsetMode == OFFSET_MODE_MMDS_SB_1998) {
|
|
||||||
effectiveOffset = -1998000000;
|
|
||||||
} // 1.998GHz Down-conversion
|
|
||||||
else if (offsetMode == OFFSET_MODE_DK5AV_XB) {
|
|
||||||
effectiveOffset = -6800000000;
|
|
||||||
} // 6.8GHz Down-conversion
|
|
||||||
else if (offsetMode == OFFSET_MODE_KU_LNB_9750) {
|
|
||||||
effectiveOffset = -9750000000;
|
|
||||||
} // 9.750GHz Down-conversion
|
|
||||||
else if (offsetMode == OFFSET_MODE_KU_LNB_10700) {
|
|
||||||
effectiveOffset = -10700000000;
|
|
||||||
} // 10.7GHz Down-conversion
|
|
||||||
else {
|
|
||||||
effectiveOffset = 0;
|
effectiveOffset = 0;
|
||||||
|
break;
|
||||||
|
case OFFSET_ID_MANUAL:
|
||||||
|
effectiveOffset = manualOffset;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
effectiveOffset = namedOffsets[offsets.name(offsetId)];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply it
|
||||||
sigpath::sourceManager.setTuningOffset(effectiveOffset);
|
sigpath::sourceManager.setTuningOffset(effectiveOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void selectOffsetById(int id) {
|
||||||
|
// Update the offset mode
|
||||||
|
offsetId = id;
|
||||||
|
selectedOffset = offsets.name(id);
|
||||||
|
|
||||||
|
// Update the offset
|
||||||
|
updateOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectOffsetByName(const std::string& name) {
|
||||||
|
// If the name doesn't exist, select 'None'
|
||||||
|
if (!offsets.nameExists(name)) {
|
||||||
|
selectOffsetById(OFFSET_ID_NONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select using the ID associated with the name
|
||||||
|
selectOffsetById(offsets.nameId(name));
|
||||||
|
}
|
||||||
|
|
||||||
void refreshSources() {
|
void refreshSources() {
|
||||||
sourceNames = sigpath::sourceManager.getSourceNames();
|
// Get sources
|
||||||
sourceNamesTxt.clear();
|
auto sourceNames = sigpath::sourceManager.getSourceNames();
|
||||||
|
|
||||||
|
// Define source options
|
||||||
|
sources.clear();
|
||||||
for (auto name : sourceNames) {
|
for (auto name : sourceNames) {
|
||||||
sourceNamesTxt += name;
|
sources.define(name, name, name);
|
||||||
sourceNamesTxt += '\0';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectSource(std::string name) {
|
void selectSource(std::string name) {
|
||||||
if (sourceNames.empty()) {
|
// If there is no source, give up
|
||||||
|
if (sources.empty()) {
|
||||||
|
sourceId = 0;
|
||||||
selectedSource.clear();
|
selectedSource.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto it = std::find(sourceNames.begin(), sourceNames.end(), name);
|
|
||||||
if (it == sourceNames.end()) {
|
// If a source with the given name doesn't exist, select the first source instead
|
||||||
selectSource(sourceNames[0]);
|
if (!sources.valueExists(name)) {
|
||||||
|
selectSource(sources.value(0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sourceId = std::distance(sourceNames.begin(), it);
|
|
||||||
selectedSource = sourceNames[sourceId];
|
// Update the GUI variables
|
||||||
sigpath::sourceManager.selectSource(sourceNames[sourceId]);
|
sourceId = sources.valueId(name);
|
||||||
|
selectedSource = name;
|
||||||
|
|
||||||
|
// Select the source module
|
||||||
|
sigpath::sourceManager.selectSource(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSourceRegistered(std::string name, void* ctx) {
|
void onSourcesChanged(std::string name, void* ctx) {
|
||||||
|
// Update the source list
|
||||||
refreshSources();
|
refreshSources();
|
||||||
|
|
||||||
if (selectedSource.empty()) {
|
// Reselect the current source
|
||||||
sourceId = 0;
|
selectSource(selectedSource);
|
||||||
selectSource(sourceNames[0]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSourceUnregister(std::string name, void* ctx) {
|
void onSourceUnregister(std::string name, void* ctx) {
|
||||||
@ -120,60 +127,173 @@ namespace sourcemenu {
|
|||||||
// TODO: Stop everything
|
// TODO: Stop everything
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSourceUnregistered(std::string name, void* ctx) {
|
void reloadOffsets() {
|
||||||
refreshSources();
|
// Clear list
|
||||||
|
offsets.clear();
|
||||||
|
namedOffsets.clear();
|
||||||
|
|
||||||
if (sourceNames.empty()) {
|
// Define special offset modes
|
||||||
selectedSource = "";
|
offsets.define("None", OFFSET_ID_NONE);
|
||||||
return;
|
offsets.define("Manual", OFFSET_ID_MANUAL);
|
||||||
|
|
||||||
|
// Acquire the config file
|
||||||
|
core::configManager.acquire();
|
||||||
|
|
||||||
|
// Load custom offsets
|
||||||
|
auto ofs = core::configManager.conf["offsets"].items();
|
||||||
|
for (auto& o : ofs) {
|
||||||
|
namedOffsets[o.key()] = (double)o.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name == selectedSource) {
|
// Define custom offsets
|
||||||
sourceId = std::clamp<int>(sourceId, 0, sourceNames.size() - 1);
|
for (auto& [name, offset] : namedOffsets) {
|
||||||
selectSource(sourceNames[sourceId]);
|
offsets.define(name, offsets.size());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
|
// Release the config file
|
||||||
|
core::configManager.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
|
// Load offset modes
|
||||||
|
reloadOffsets();
|
||||||
|
|
||||||
|
// Define decimation values
|
||||||
|
decimations.define(1, "None", 1);
|
||||||
|
decimations.define(2, "2x", 2);
|
||||||
|
decimations.define(4, "4x", 4);
|
||||||
|
decimations.define(8, "8x", 8);
|
||||||
|
decimations.define(16, "16x", 16);
|
||||||
|
decimations.define(32, "32x", 32);
|
||||||
|
decimations.define(64, "64x", 64);
|
||||||
|
|
||||||
|
// Acquire the config file
|
||||||
core::configManager.acquire();
|
core::configManager.acquire();
|
||||||
std::string selected = core::configManager.conf["source"];
|
|
||||||
customOffset = core::configManager.conf["offset"];
|
// Load other settings
|
||||||
offsetMode = core::configManager.conf["offsetMode"];
|
std::string selectedSource = core::configManager.conf["source"];
|
||||||
decimationPower = core::configManager.conf["decimationPower"];
|
manualOffset = core::configManager.conf["manualOffset"];
|
||||||
|
std::string selectedOffset = core::configManager.conf["selectedOffset"];
|
||||||
iqCorrection = core::configManager.conf["iqCorrection"];
|
iqCorrection = core::configManager.conf["iqCorrection"];
|
||||||
invertIQ = core::configManager.conf["invertIQ"];
|
invertIQ = core::configManager.conf["invertIQ"];
|
||||||
|
int decimation = core::configManager.conf["decimation"];
|
||||||
|
if (decimations.keyExists(decimation)) {
|
||||||
|
decimId = decimations.keyId(decimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release the config file
|
||||||
|
core::configManager.release();
|
||||||
|
|
||||||
|
// Select the source module
|
||||||
|
refreshSources();
|
||||||
|
selectSource(selectedSource);
|
||||||
|
|
||||||
|
// Update frontend settings
|
||||||
sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
|
sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
|
||||||
sigpath::iqFrontEnd.setInvertIQ(invertIQ);
|
sigpath::iqFrontEnd.setInvertIQ(invertIQ);
|
||||||
updateOffset();
|
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
|
||||||
|
selectOffsetByName(selectedOffset);
|
||||||
|
|
||||||
refreshSources();
|
// Register handlers
|
||||||
selectSource(selected);
|
sourcesChangedHandler.handler = onSourcesChanged;
|
||||||
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
|
|
||||||
|
|
||||||
sourceRegisteredHandler.handler = onSourceRegistered;
|
|
||||||
sourceUnregisterHandler.handler = onSourceUnregister;
|
sourceUnregisterHandler.handler = onSourceUnregister;
|
||||||
sourceUnregisteredHandler.handler = onSourceUnregistered;
|
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourcesChangedHandler);
|
||||||
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
|
|
||||||
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
|
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
|
||||||
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
|
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourcesChangedHandler);
|
||||||
|
}
|
||||||
|
|
||||||
core::configManager.release();
|
void addOffset(const std::string& name, double offset) {
|
||||||
|
// Acquire the config file
|
||||||
|
core::configManager.acquire();
|
||||||
|
|
||||||
|
// Define a new offset
|
||||||
|
core::configManager.conf["offsets"][name] = offset;
|
||||||
|
|
||||||
|
// Acquire the config file
|
||||||
|
core::configManager.release(true);
|
||||||
|
|
||||||
|
// Reload the offsets
|
||||||
|
reloadOffsets();
|
||||||
|
|
||||||
|
// Attempt to re-select the same one
|
||||||
|
selectOffsetByName(selectedOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void delOffset(const std::string& name) {
|
||||||
|
// Acquire the config file
|
||||||
|
core::configManager.acquire();
|
||||||
|
|
||||||
|
// Define a new offset
|
||||||
|
core::configManager.conf["offsets"].erase(name);
|
||||||
|
|
||||||
|
// Acquire the config file
|
||||||
|
core::configManager.release(true);
|
||||||
|
|
||||||
|
// Reload the offsets
|
||||||
|
reloadOffsets();
|
||||||
|
|
||||||
|
// Attempt to re-select the same one
|
||||||
|
selectOffsetByName(selectedOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addOffsetDialog() {
|
||||||
|
bool open = true;
|
||||||
|
gui::mainWindow.lockWaterfallControls = true;
|
||||||
|
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
|
||||||
|
const char* id = "Add offset##sdrpp_add_offset_dialog_";
|
||||||
|
ImGui::OpenPopup(id);
|
||||||
|
|
||||||
|
if (ImGui::BeginPopup(id, ImGuiWindowFlags_NoResize)) {
|
||||||
|
ImGui::LeftLabel("Name");
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
ImGui::InputText("##sdrpp_add_offset_name", newOffsetName, 1023);
|
||||||
|
|
||||||
|
ImGui::LeftLabel("Offset");
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
ImGui::InputDouble("##sdrpp_add_offset_offset", &newOffset);
|
||||||
|
|
||||||
|
bool nameExists = offsets.nameExists(newOffsetName);
|
||||||
|
bool reservedName = !strcmp(newOffsetName, "None") || !strcmp(newOffsetName, "Manual");
|
||||||
|
bool denyApply = !newOffsetName[0] || nameExists || reservedName;
|
||||||
|
|
||||||
|
if (nameExists) {
|
||||||
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "An offset with the given name already exists.");
|
||||||
|
}
|
||||||
|
else if (reservedName) {
|
||||||
|
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "The given name is reserved.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (denyApply) { style::beginDisabled(); }
|
||||||
|
if (ImGui::Button("Apply")) {
|
||||||
|
addOffset(newOffsetName, newOffset);
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
if (denyApply) { style::endDisabled(); }
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Cancel")) {
|
||||||
|
open = false;
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
return open;
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw(void* ctx) {
|
void draw(void* ctx) {
|
||||||
float itemWidth = ImGui::GetContentRegionAvail().x;
|
float itemWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
float lineHeight = ImGui::GetTextLineHeightWithSpacing();
|
||||||
|
float spacing = lineHeight - ImGui::GetTextLineHeight();
|
||||||
bool running = gui::mainWindow.sdrIsRunning();
|
bool running = gui::mainWindow.sdrIsRunning();
|
||||||
|
|
||||||
if (running) { style::beginDisabled(); }
|
if (running) { style::beginDisabled(); }
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(itemWidth);
|
ImGui::SetNextItemWidth(itemWidth);
|
||||||
if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
|
if (ImGui::Combo("##source", &sourceId, sources.txt)) {
|
||||||
selectSource(sourceNames[sourceId]);
|
std::string newSource = sources.value(sourceId);
|
||||||
|
selectSource(newSource);
|
||||||
core::configManager.acquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["source"] = sourceNames[sourceId];
|
core::configManager.conf["source"] = newSource;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,21 +316,45 @@ namespace sourcemenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::LeftLabel("Offset mode");
|
ImGui::LeftLabel("Offset mode");
|
||||||
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX() - 2.0f*(lineHeight + 1.5f*spacing));
|
||||||
if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) {
|
if (ImGui::Combo("##_sdrpp_offset", &offsetId, offsets.txt)) {
|
||||||
updateOffset();
|
selectOffsetById(offsetId);
|
||||||
core::configManager.acquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["offsetMode"] = offsetMode;
|
core::configManager.conf["selectedOffset"] = offsets.key(offsetId);
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
|
||||||
|
if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::BeginDisabled(); }
|
||||||
|
if (ImGui::Button("-##_sdrpp_offset_del_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
|
||||||
|
delOffsetName = selectedOffset;
|
||||||
|
showDelOffsetDialog = true;
|
||||||
|
}
|
||||||
|
if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::EndDisabled(); }
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
|
||||||
|
if (ImGui::Button("+##_sdrpp_offset_add_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
|
||||||
|
strcpy(newOffsetName, "New Offset");
|
||||||
|
showAddOffsetDialog = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset delete confirmation
|
||||||
|
if (ImGui::GenericDialog("sdrpp_del_offset_confirm", showDelOffsetDialog, GENERIC_DIALOG_BUTTONS_YES_NO, []() {
|
||||||
|
ImGui::Text("Deleting offset named \"%s\". Are you sure?", delOffsetName.c_str());
|
||||||
|
}) == GENERIC_DIALOG_BUTTON_YES) {
|
||||||
|
delOffset(delOffsetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Offset add diaglog
|
||||||
|
if (showAddOffsetDialog) { showAddOffsetDialog = addOffsetDialog(); }
|
||||||
|
|
||||||
ImGui::LeftLabel("Offset");
|
ImGui::LeftLabel("Offset");
|
||||||
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
ImGui::FillWidth();
|
||||||
if (offsetMode == OFFSET_MODE_CUSTOM) {
|
if (offsetId == OFFSET_ID_MANUAL) {
|
||||||
if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) {
|
if (ImGui::InputDouble("##freq_offset", &manualOffset, 1.0, 100.0)) {
|
||||||
updateOffset();
|
updateOffset();
|
||||||
core::configManager.acquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["offset"] = customOffset;
|
core::configManager.conf["manualOffset"] = manualOffset;
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,11 +366,11 @@ namespace sourcemenu {
|
|||||||
|
|
||||||
if (running) { style::beginDisabled(); }
|
if (running) { style::beginDisabled(); }
|
||||||
ImGui::LeftLabel("Decimation");
|
ImGui::LeftLabel("Decimation");
|
||||||
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
ImGui::FillWidth();
|
||||||
if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) {
|
if (ImGui::Combo("##source_decim", &decimId, decimations.txt)) {
|
||||||
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
|
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
|
||||||
core::configManager.acquire();
|
core::configManager.acquire();
|
||||||
core::configManager.conf["decimationPower"] = decimationPower;
|
core::configManager.conf["decimation"] = decimations.key(decimId);
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
if (running) { style::endDisabled(); }
|
if (running) { style::endDisabled(); }
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <backend.h>
|
#include <backend.h>
|
||||||
|
#include <utils/hrfreq.h>
|
||||||
|
|
||||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
@ -90,6 +91,7 @@ void FrequencySelect::moveCursorToDigit(int i) {
|
|||||||
|
|
||||||
void FrequencySelect::draw() {
|
void FrequencySelect::draw() {
|
||||||
auto window = ImGui::GetCurrentWindow();
|
auto window = ImGui::GetCurrentWindow();
|
||||||
|
auto io = ImGui::GetIO();
|
||||||
widgetPos = ImGui::GetWindowContentRegionMin();
|
widgetPos = ImGui::GetWindowContentRegionMin();
|
||||||
ImVec2 cursorPos = ImGui::GetCursorPos();
|
ImVec2 cursorPos = ImGui::GetCursorPos();
|
||||||
widgetPos.x += window->Pos.x + cursorPos.x;
|
widgetPos.x += window->Pos.x + cursorPos.x;
|
||||||
@ -132,7 +134,7 @@ void FrequencySelect::draw() {
|
|||||||
ImVec2 mousePos = ImGui::GetMousePos();
|
ImVec2 mousePos = ImGui::GetMousePos();
|
||||||
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||||
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
|
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
|
||||||
int mw = ImGui::GetIO().MouseWheel;
|
int mw = io.MouseWheel;
|
||||||
bool onDigit = false;
|
bool onDigit = false;
|
||||||
bool hovered = false;
|
bool hovered = false;
|
||||||
|
|
||||||
@ -174,7 +176,7 @@ void FrequencySelect::draw() {
|
|||||||
moveCursorToDigit(i + 1);
|
moveCursorToDigit(i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto chars = ImGui::GetIO().InputQueueCharacters;
|
auto chars = io.InputQueueCharacters;
|
||||||
|
|
||||||
// For each keyboard characters, type it
|
// For each keyboard characters, type it
|
||||||
for (int j = 0; j < chars.Size; j++) {
|
for (int j = 0; j < chars.Size; j++) {
|
||||||
@ -194,6 +196,34 @@ void FrequencySelect::draw() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
digitHovered = hovered;
|
digitHovered = hovered;
|
||||||
|
|
||||||
|
if (isInArea(mousePos, digitTopMins[0], digitBottomMaxs[11])) {
|
||||||
|
bool shortcutKey = io.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
|
||||||
|
bool ctrlOnly = (io.KeyMods == ImGuiKeyModFlags_Ctrl);
|
||||||
|
bool shiftOnly = (io.KeyMods == ImGuiKeyModFlags_Shift);
|
||||||
|
bool copy = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_C)) || (ctrlOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
|
||||||
|
bool paste = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_V)) || (shiftOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
|
||||||
|
if (copy) {
|
||||||
|
// Convert the freqency to a string
|
||||||
|
std::string freqStr = hrfreq::toString(frequency);
|
||||||
|
|
||||||
|
// Write it to the clipboard
|
||||||
|
ImGui::SetClipboardText(freqStr.c_str());
|
||||||
|
}
|
||||||
|
if (paste) {
|
||||||
|
// Attempt to parse the clipboard as a number
|
||||||
|
const char* clip = ImGui::GetClipboardText();
|
||||||
|
|
||||||
|
// If the clipboard is not empty, attempt to parse it
|
||||||
|
if (clip) {
|
||||||
|
double newFreq;
|
||||||
|
if (hrfreq::fromString(clip, newFreq)) {
|
||||||
|
setFrequency(abs(newFreq));
|
||||||
|
frequencyChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t freq = 0;
|
uint64_t freq = 0;
|
||||||
|
120
core/src/utils/hrfreq.cpp
Normal file
120
core/src/utils/hrfreq.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#include "hrfreq.h"
|
||||||
|
#include <utils/flog.h>
|
||||||
|
|
||||||
|
namespace hrfreq {
|
||||||
|
|
||||||
|
|
||||||
|
std::string toString(double freq) {
|
||||||
|
// Determine the scale
|
||||||
|
int maxDecimals = 0;
|
||||||
|
const char* suffix = "Hz";
|
||||||
|
if (freq >= 1e9) {
|
||||||
|
freq /= 1e9;
|
||||||
|
maxDecimals = 9;
|
||||||
|
suffix = "GHz";
|
||||||
|
}
|
||||||
|
else if (freq >= 1e6) {
|
||||||
|
freq /= 1e6;
|
||||||
|
maxDecimals = 6;
|
||||||
|
suffix = "MHz";
|
||||||
|
}
|
||||||
|
else if (freq >= 1e3) {
|
||||||
|
freq /= 1e3;
|
||||||
|
maxDecimals = 3;
|
||||||
|
suffix = "KHz";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to string (TODO: Not sure if limiting the decimals rounds)
|
||||||
|
char numBuf[128];
|
||||||
|
int numLen = sprintf(numBuf, "%0.*lf", maxDecimals, freq);
|
||||||
|
|
||||||
|
// If there is a decimal point, remove the useless zeros
|
||||||
|
if (maxDecimals) {
|
||||||
|
for (int i = numLen-1; i >= 0; i--) {
|
||||||
|
bool dot = (numBuf[i] == '.');
|
||||||
|
if (numBuf[i] != '0' && !dot) { break; }
|
||||||
|
numBuf[i] = 0;
|
||||||
|
if (dot) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concat the suffix
|
||||||
|
char finalBuf[128];
|
||||||
|
sprintf(finalBuf, "%s%s", numBuf, suffix);
|
||||||
|
|
||||||
|
// Return the final string
|
||||||
|
return finalBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNumeric(char c) {
|
||||||
|
return std::isdigit(c) || c == '+' || c == '-' || c == '.' || c == ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fromString(const std::string& str, double& freq) {
|
||||||
|
// Skip non-numeric characters
|
||||||
|
int i = 0;
|
||||||
|
char c;
|
||||||
|
for (; i < str.size(); i++) {
|
||||||
|
if (isNumeric(str[i])) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the numeric part
|
||||||
|
std::string numeric;
|
||||||
|
for (; i < str.size(); i++) {
|
||||||
|
// Get the character
|
||||||
|
c = str[i];
|
||||||
|
|
||||||
|
// If it's a letter, stop
|
||||||
|
if (std::isalpha(c)) { break; }
|
||||||
|
|
||||||
|
// If isn't numeric, skip it
|
||||||
|
if (!isNumeric(c)) { continue; }
|
||||||
|
|
||||||
|
// If it's a comma, skip it for now. This enforces a dot as a decimal point
|
||||||
|
if (c == ',') { continue; }
|
||||||
|
|
||||||
|
// Add the character to the numeric string
|
||||||
|
numeric += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse the numeric part
|
||||||
|
double num;
|
||||||
|
try {
|
||||||
|
num = std::stod(numeric);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
flog::error("Failed to parse numeric part: '{}'", numeric);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no more text is available, assume the numeric part gives a frequency in Hz
|
||||||
|
if (i == str.size()) {
|
||||||
|
flog::warn("No unit given, assuming it's Hz");
|
||||||
|
freq = num;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale the numeric value depending on the first scale character
|
||||||
|
char scale = std::toupper(str[i]);
|
||||||
|
switch (scale) {
|
||||||
|
case 'G':
|
||||||
|
num *= 1e9;
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
num *= 1e6;
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
num *= 1e3;
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
flog::warn("Unknown frequency scale: '{}'", scale);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the frequency
|
||||||
|
freq = num;
|
||||||
|
return true; // TODO
|
||||||
|
}
|
||||||
|
}
|
19
core/src/utils/hrfreq.h
Normal file
19
core/src/utils/hrfreq.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace hrfreq {
|
||||||
|
/**
|
||||||
|
* Convert a frequency to a human-readable string.
|
||||||
|
* @param freq Frequency in Hz.
|
||||||
|
* @return Human-readable representation of the frequency.
|
||||||
|
*/
|
||||||
|
std::string toString(double freq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a human-readable representation of a frequency to a frequency value.
|
||||||
|
* @param str String containing the human-readable frequency.
|
||||||
|
* @param freq Value to write the decoded frequency to.
|
||||||
|
* @return True on success, false otherwise.
|
||||||
|
*/
|
||||||
|
bool fromString(const std::string& str, double& freq);
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define VERSION_STR "1.2.0"
|
#define VERSION_STR "1.2.1"
|
37
decoder_modules/dab_decoder/CMakeLists.txt
Normal file
37
decoder_modules/dab_decoder/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(dab_decoder)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
target_include_directories(dab_decoder PRIVATE "src/")
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Lib path
|
||||||
|
target_include_directories(dab_decoder PRIVATE "C:/Program Files/codec2/include/")
|
||||||
|
target_link_directories(dab_decoder PRIVATE "C:/Program Files/codec2/lib")
|
||||||
|
|
||||||
|
target_link_libraries(dab_decoder PRIVATE libcodec2)
|
||||||
|
elseif (ANDROID)
|
||||||
|
target_include_directories(dab_decoder PUBLIC
|
||||||
|
/sdr-kit/${ANDROID_ABI}/include/codec2
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(dab_decoder PUBLIC
|
||||||
|
/sdr-kit/${ANDROID_ABI}/lib/libcodec2.so
|
||||||
|
)
|
||||||
|
else ()
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBCODEC2 REQUIRED codec2)
|
||||||
|
|
||||||
|
target_include_directories(dab_decoder PRIVATE ${LIBCODEC2_INCLUDE_DIRS})
|
||||||
|
target_link_directories(dab_decoder PRIVATE ${LIBCODEC2_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(dab_decoder PRIVATE ${LIBCODEC2_LIBRARIES})
|
||||||
|
|
||||||
|
# Include it because for some reason pkgconfig doesn't look here?
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
target_include_directories(dab_decoder PRIVATE "/usr/local/include")
|
||||||
|
endif()
|
||||||
|
endif ()
|
280
decoder_modules/dab_decoder/src/dab_dsp.h
Normal file
280
decoder_modules/dab_decoder/src/dab_dsp.h
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/processor.h>
|
||||||
|
#include <utils/flog.h>
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include "dab_phase_sym.h"
|
||||||
|
|
||||||
|
namespace dab {
|
||||||
|
class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||||
|
public:
|
||||||
|
CyclicSync() {}
|
||||||
|
|
||||||
|
// TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
|
||||||
|
CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
|
||||||
|
|
||||||
|
void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
|
||||||
|
// Computer the number of samples for the symbol and its cyclic prefix
|
||||||
|
symbolSamps = round(samplerate * symbolLength);
|
||||||
|
prefixSamps = round(samplerate * cyclicPrefixLength);
|
||||||
|
|
||||||
|
// Allocate and clear the delay buffer
|
||||||
|
delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
|
||||||
|
dsp::buffer::clear(delayBuf, symbolSamps);
|
||||||
|
|
||||||
|
// Allocate and clear the history buffer
|
||||||
|
histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
|
||||||
|
dsp::buffer::clear(histBuf, prefixSamps);
|
||||||
|
|
||||||
|
// Compute the delay input addresses
|
||||||
|
delayBufInput = &delayBuf[symbolSamps];
|
||||||
|
|
||||||
|
// Compute the correlation AGC configuration
|
||||||
|
this->agcRate = agcRate;
|
||||||
|
agcRateInv = 1.0f - agcRate;
|
||||||
|
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
base_type::tempStop();
|
||||||
|
|
||||||
|
base_type::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// Copy the data into the normal delay buffer
|
||||||
|
memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
// Flush the input stream
|
||||||
|
base_type::_in->flush();
|
||||||
|
|
||||||
|
// Do cross-correlation
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Get the current history slot
|
||||||
|
dsp::complex_t* slot = &histBuf[histId++];
|
||||||
|
|
||||||
|
// Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
|
||||||
|
histId %= prefixSamps;
|
||||||
|
|
||||||
|
// Kick out last value from the correlation
|
||||||
|
corr -= *slot;
|
||||||
|
|
||||||
|
// Save input value and compute the new prodct
|
||||||
|
dsp::complex_t val = delayBuf[i];
|
||||||
|
dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
|
||||||
|
|
||||||
|
// Add the new value to the correlation
|
||||||
|
*slot = prod;
|
||||||
|
|
||||||
|
// Add the new value to the history buffer
|
||||||
|
corr += prod;
|
||||||
|
|
||||||
|
// Compute sample amplitude
|
||||||
|
float rcorr = corr.amplitude();
|
||||||
|
|
||||||
|
// If a high enough peak is reached, reset the symbol counter
|
||||||
|
if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
|
||||||
|
peakCorr = rcorr;
|
||||||
|
peakLCorr = lastCorr;
|
||||||
|
samplesSincePeak = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the sample right after the peak, save it
|
||||||
|
if (samplesSincePeak == 1) {
|
||||||
|
peakRCorr = rcorr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the sample to the output
|
||||||
|
out.writeBuf[samplesSincePeak++] = val;
|
||||||
|
|
||||||
|
// If the end of the symbol is reached, send it off
|
||||||
|
if (samplesSincePeak >= symbolSamps) {
|
||||||
|
if (!out.swap(symbolSamps)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
samplesSincePeak = 0;
|
||||||
|
peakCorr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the average correlation
|
||||||
|
lastCorr = rcorr;
|
||||||
|
|
||||||
|
// Update the average correlation value
|
||||||
|
avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move unused data
|
||||||
|
memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int symbolSamps;
|
||||||
|
int prefixSamps;
|
||||||
|
|
||||||
|
int histId = 0;
|
||||||
|
dsp::complex_t* histBuf;
|
||||||
|
|
||||||
|
dsp::complex_t* delayBuf;
|
||||||
|
dsp::complex_t* delayBufInput;
|
||||||
|
|
||||||
|
dsp::complex_t corr = { 0.0f, 0.0f };
|
||||||
|
|
||||||
|
int samplesSincePeak = 0;
|
||||||
|
float lastCorr = 0.0f;
|
||||||
|
float peakCorr = 0.0f;
|
||||||
|
float peakLCorr = 0.0f;
|
||||||
|
float peakRCorr = 0.0f;
|
||||||
|
|
||||||
|
// Note only required for DAB
|
||||||
|
float avgCorr = 0.0f;
|
||||||
|
float agcRate;
|
||||||
|
float agcRateInv;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||||
|
public:
|
||||||
|
FrameFreqSync() {}
|
||||||
|
|
||||||
|
FrameFreqSync(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) { init(in, agcRate); }
|
||||||
|
|
||||||
|
void init(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) {
|
||||||
|
// Allocate buffers
|
||||||
|
amps = dsp::buffer::alloc<float>(2048);
|
||||||
|
conjRef = dsp::buffer::alloc<dsp::complex_t>(2048);
|
||||||
|
corrIn = (dsp::complex_t*)fftwf_alloc_complex(2048);
|
||||||
|
corrOut = (dsp::complex_t*)fftwf_alloc_complex(2048);
|
||||||
|
|
||||||
|
// Copy the phase reference
|
||||||
|
memcpy(conjRef, DAB_PHASE_SYM_CONJ, 2048 * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
// Plan the FFT computation
|
||||||
|
plan = fftwf_plan_dft_1d(2048, (fftwf_complex*)corrIn, (fftwf_complex*)corrOut, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
|
||||||
|
// Compute the correlation AGC configuration
|
||||||
|
this->agcRate = agcRate;
|
||||||
|
agcRateInv = 1.0f - agcRate;
|
||||||
|
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
base_type::tempStop();
|
||||||
|
|
||||||
|
base_type::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// Apply frequency shift
|
||||||
|
lv_32fc_t phase = lv_cmake(1.0f, 0.0f);
|
||||||
|
lv_32fc_t phaseDelta = lv_cmake(cos(offset), sin(offset));
|
||||||
|
#if VOLK_VERSION >= 030100
|
||||||
|
volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||||
|
#else
|
||||||
|
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Compute the amplitude amplitude of all samples
|
||||||
|
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)_in->readBuf, 2048);
|
||||||
|
|
||||||
|
// Compute the average signal level by adding up all values
|
||||||
|
float level = 0.0f;
|
||||||
|
volk_32f_accumulator_s32f(&level, amps, 2048);
|
||||||
|
|
||||||
|
// Detect a frame sync condition
|
||||||
|
if (level < avgLvl * 0.5f) {
|
||||||
|
// Reset symbol counter
|
||||||
|
sym = 1;
|
||||||
|
|
||||||
|
// Update the average level
|
||||||
|
avgLvl = agcRate*level + agcRateInv*avgLvl;
|
||||||
|
|
||||||
|
// Flush the input stream and return
|
||||||
|
base_type::_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the average level
|
||||||
|
avgLvl = agcRate*level + agcRateInv*avgLvl;
|
||||||
|
|
||||||
|
// Handle phase reference
|
||||||
|
if (sym == 1) {
|
||||||
|
// Output the symbols (DEBUG ONLY)
|
||||||
|
memcpy(corrIn, _in->readBuf, 2048 * sizeof(dsp::complex_t));
|
||||||
|
fftwf_execute(plan);
|
||||||
|
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
|
||||||
|
int outCount = 0;
|
||||||
|
dsp::complex_t pi4 = { cos(3.1415926535*0.25), sin(3.1415926535*0.25) };
|
||||||
|
for (int i = -767; i < 768; i++) {
|
||||||
|
if (!i) { continue; }
|
||||||
|
int cid0 = ((i-1) >= 0) ? (i-1) : 2048+(i-1);
|
||||||
|
int cid1 = (i >= 0) ? i : 2048+i;;
|
||||||
|
out.writeBuf[outCount++] = pi4 * (corrOut[cid1] * corrOut[cid0].conj()) * (1.0f/(amps[cid0]*amps[cid0]));
|
||||||
|
}
|
||||||
|
out.swap(outCount);
|
||||||
|
|
||||||
|
// Multiply the samples with the conjugated phase reference signal
|
||||||
|
volk_32fc_x2_multiply_32fc((lv_32fc_t*)corrIn, (lv_32fc_t*)_in->readBuf, (lv_32fc_t*)conjRef, 2048);
|
||||||
|
|
||||||
|
// Compute the FFT of the product
|
||||||
|
fftwf_execute(plan);
|
||||||
|
|
||||||
|
// Compute the amplitude of the bins
|
||||||
|
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
|
||||||
|
|
||||||
|
// Locate highest power bin
|
||||||
|
uint32_t peakId;
|
||||||
|
volk_32f_index_max_32u(&peakId, amps, 2048);
|
||||||
|
|
||||||
|
// Obtain the value of the bins next to the peak
|
||||||
|
float peakL = amps[(peakId + 2047) % 2048];
|
||||||
|
float peakR = amps[(peakId + 1) % 2048];
|
||||||
|
|
||||||
|
// Compute the integer frequency offset
|
||||||
|
float offInt = (peakId < 1024) ? (float)peakId : ((float)peakId - 2048.0f);
|
||||||
|
|
||||||
|
// Compute the frequency offset in rad/samp
|
||||||
|
float off = 3.1415926535f * (offInt + ((peakR - peakL) / (peakR + peakL))) * (1.0f / 1024.0f);
|
||||||
|
|
||||||
|
// Run control loop
|
||||||
|
offset -= 0.1f*off;
|
||||||
|
flog::debug("Offset: {} Hz, Error: {} Hz, Avg Level: {}", offset * (0.5f/3.1415926535f)*2.048e6, off * (0.5f/3.1415926535f)*2.048e6, avgLvl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the symbol counter
|
||||||
|
sym++;
|
||||||
|
|
||||||
|
// Flush the input stream and return
|
||||||
|
base_type::_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
fftwf_plan plan;
|
||||||
|
|
||||||
|
float* amps;
|
||||||
|
dsp::complex_t* conjRef;
|
||||||
|
dsp::complex_t* corrIn;
|
||||||
|
dsp::complex_t* corrOut;
|
||||||
|
|
||||||
|
int sym;
|
||||||
|
float offset = 0.0f;
|
||||||
|
|
||||||
|
float avgLvl = 0.0f;
|
||||||
|
float agcRate;
|
||||||
|
float agcRateInv;
|
||||||
|
};
|
||||||
|
}
|
2053
decoder_modules/dab_decoder/src/dab_phase_sym.h
Normal file
2053
decoder_modules/dab_decoder/src/dab_phase_sym.h
Normal file
File diff suppressed because it is too large
Load Diff
163
decoder_modules/dab_decoder/src/main.cpp
Normal file
163
decoder_modules/dab_decoder/src/main.cpp
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <gui/style.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <dsp/stream.h>
|
||||||
|
#include <dsp/buffer/reshaper.h>
|
||||||
|
#include <dsp/multirate/rational_resampler.h>
|
||||||
|
#include <dsp/sink/handler_sink.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <chrono>
|
||||||
|
#include "dab_dsp.h"
|
||||||
|
#include <gui/widgets/constellation_diagram.h>
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "dab_decoder",
|
||||||
|
/* Description: */ "DAB/DAB+ Decoder for SDR++",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigManager config;
|
||||||
|
|
||||||
|
#define INPUT_SAMPLE_RATE 2.048e6
|
||||||
|
#define VFO_BANDWIDTH 1.6e6
|
||||||
|
|
||||||
|
class M17DecoderModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
M17DecoderModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
config.acquire();
|
||||||
|
|
||||||
|
config.release(true);
|
||||||
|
|
||||||
|
// Initialize VFO
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
|
||||||
|
vfo->setSnapInterval(250);
|
||||||
|
|
||||||
|
// Initialize DSP here
|
||||||
|
csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE);
|
||||||
|
ffsync.init(&csync.out);
|
||||||
|
ns.init(&ffsync.out, handler, this);
|
||||||
|
|
||||||
|
// Start DSO Here
|
||||||
|
csync.start();
|
||||||
|
ffsync.start();
|
||||||
|
ns.start();
|
||||||
|
|
||||||
|
gui::menu.registerEntry(name, menuHandler, this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~M17DecoderModule() {
|
||||||
|
gui::menu.removeEntry(name);
|
||||||
|
// Stop DSP Here
|
||||||
|
if (enabled) {
|
||||||
|
csync.stop();
|
||||||
|
ffsync.stop();
|
||||||
|
ns.stop();
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
sigpath::sinkManager.unregisterStream(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
double bw = gui::waterfall.getBandwidth();
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
|
||||||
|
vfo->setSnapInterval(250);
|
||||||
|
|
||||||
|
// Set Input of demod here
|
||||||
|
csync.setInput(vfo->output);
|
||||||
|
|
||||||
|
// Start DSP here
|
||||||
|
csync.start();
|
||||||
|
ffsync.start();
|
||||||
|
ns.start();
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
// Stop DSP here
|
||||||
|
csync.stop();
|
||||||
|
ffsync.stop();
|
||||||
|
ns.stop();
|
||||||
|
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
M17DecoderModule* _this = (M17DecoderModule*)ctx;
|
||||||
|
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::beginDisabled(); }
|
||||||
|
|
||||||
|
_this->constDiagram.draw();
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::endDisabled(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream file;
|
||||||
|
|
||||||
|
static void handler(dsp::complex_t* data, int count, void* ctx) {
|
||||||
|
M17DecoderModule* _this = (M17DecoderModule*)ctx;
|
||||||
|
//_this->file.write((char*)data, count * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
|
||||||
|
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
|
||||||
|
_this->constDiagram.releaseBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
|
||||||
|
dab::CyclicSync csync;
|
||||||
|
dab::FrameFreqSync ffsync;
|
||||||
|
dsp::sink::Handler<dsp::complex_t> ns;
|
||||||
|
|
||||||
|
ImGui::ConstellationDiagram constDiagram;
|
||||||
|
|
||||||
|
// DSP Chain
|
||||||
|
VFOManager::VFO* vfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
// Create default recording directory
|
||||||
|
json def = json({});
|
||||||
|
config.setPath(core::args["root"].s() + "/dab_decoder_config.json");
|
||||||
|
config.load(def);
|
||||||
|
config.enableAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new M17DecoderModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (M17DecoderModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
config.disableAutoSave();
|
||||||
|
config.save();
|
||||||
|
}
|
34
decoder_modules/dab_decoder/src/optimized_algo.txt
Normal file
34
decoder_modules/dab_decoder/src/optimized_algo.txt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
0123456789
|
||||||
|
--- ---
|
||||||
|
|
||||||
|
0*4
|
||||||
|
1*5
|
||||||
|
2*6
|
||||||
|
|
||||||
|
1*5
|
||||||
|
2*6 = L + 3*7 - 0*4
|
||||||
|
3*7
|
||||||
|
|
||||||
|
2*6
|
||||||
|
3*7 = L + 4*8 - 1*5
|
||||||
|
4*8
|
||||||
|
|
||||||
|
3*7
|
||||||
|
4*8 = L + 5*9 - 2*6
|
||||||
|
5*9
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
0*5
|
||||||
|
1*6
|
||||||
|
2*7
|
||||||
|
|
||||||
|
1*6
|
||||||
|
2*7
|
||||||
|
3*8
|
||||||
|
|
||||||
|
2*7
|
||||||
|
3*8
|
||||||
|
4*9
|
||||||
|
|
||||||
|
=> Use same technique to cache the interpolation results
|
@ -23,9 +23,9 @@ SDRPP_MOD_INFO{
|
|||||||
/* Max instances */ -1
|
/* Max instances */ -1
|
||||||
};
|
};
|
||||||
|
|
||||||
#define INPUT_BANDWIDTH 138e3
|
#define INPUT_BANDWIDTH 600e3
|
||||||
#define INPUT_SAMPLE_RATE 250e3
|
#define INPUT_SAMPLE_RATE 1000e3
|
||||||
#define INPUT_BAUDRATE 125e3
|
#define INPUT_BAUDRATE 500e3
|
||||||
|
|
||||||
#define SYMBOL_DIAG_RATE 30
|
#define SYMBOL_DIAG_RATE 30
|
||||||
#define SYMBOL_DIAG_COUNT 1024
|
#define SYMBOL_DIAG_COUNT 1024
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include "dsp/processor.h"
|
#include "dsp/processor.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "dsp/processor.h"
|
#include "dsp/processor.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace ryfi {
|
namespace ryfi {
|
||||||
// Synchronization word.
|
// Synchronization word.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace ryfi {
|
namespace ryfi {
|
||||||
/**
|
/**
|
||||||
|
@ -72,6 +72,7 @@ namespace ryfi {
|
|||||||
uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE];
|
uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE];
|
||||||
int pktExpected = 0;
|
int pktExpected = 0;
|
||||||
int pktRead = 0;
|
int pktRead = 0;
|
||||||
|
int valid = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Read a frame
|
// Read a frame
|
||||||
@ -80,6 +81,7 @@ namespace ryfi {
|
|||||||
|
|
||||||
// Deserialize the frame
|
// Deserialize the frame
|
||||||
Frame::deserialize(rs.out.readBuf, frame);
|
Frame::deserialize(rs.out.readBuf, frame);
|
||||||
|
valid++;
|
||||||
|
|
||||||
// Flush the stream
|
// Flush the stream
|
||||||
rs.out.flush();
|
rs.out.flush();
|
||||||
@ -93,11 +95,12 @@ namespace ryfi {
|
|||||||
// If the frames aren't consecutive
|
// If the frames aren't consecutive
|
||||||
int frameRead = 0;
|
int frameRead = 0;
|
||||||
if (frame.counter != expectedCounter) {
|
if (frame.counter != expectedCounter) {
|
||||||
flog::warn("Lost at least {} frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000);
|
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
|
// Cancel the partial packet if there was one
|
||||||
pktExpected = 0;
|
pktExpected = 0;
|
||||||
pktRead = 0;
|
pktRead = 0;
|
||||||
|
valid = 1;
|
||||||
|
|
||||||
// If this frame is not an idle frame or continuation frame
|
// If this frame is not an idle frame or continuation frame
|
||||||
if (frame.firstPacket != PKT_OFFS_NONE) {
|
if (frame.firstPacket != PKT_OFFS_NONE) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include "dsp/processor.h"
|
#include "dsp/processor.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -35,10 +35,20 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -35,10 +35,20 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -35,10 +35,20 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -35,10 +35,20 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
FROM fedora:40
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
COPY do_build.sh /root
|
|
||||||
RUN chmod +x /root/do_build.sh
|
|
@ -1,31 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
cd /root
|
|
||||||
|
|
||||||
# Install dependencies and tools
|
|
||||||
dnf update -y
|
|
||||||
dnf install -y make gcc g++ cmake git wget p7zip fftw-devel glfw-devel volk-devel libzstd-devel codec2-devel airspyone_host-devel rtaudio-devel \
|
|
||||||
hackrf-devel rtl-sdr-devel portaudio-devel spdlog-devel libusbg-devel
|
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
|
||||||
7za x ./SDRplay_RSP_API-Linux-3.15.1.run
|
|
||||||
7za x ./SDRplay_RSP_API-Linux-3.15.1
|
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
|
||||||
cp inc/* /usr/include/
|
|
||||||
|
|
||||||
# Install 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 ../../
|
|
||||||
|
|
||||||
cd SDRPlusPlus
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DOPT_BUILD_AIRSPYHF_SOURCE=OFF -DOPT_BUILD_PLUTOSDR_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
|
||||||
make VERBOSE=1 -j2
|
|
@ -61,6 +61,16 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
# Fix missing .pc file for codec2
|
||||||
echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc
|
echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc
|
||||||
echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc
|
echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc
|
||||||
@ -76,7 +86,7 @@ echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
|
|||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
# Generate package
|
# Generate package
|
||||||
|
@ -35,10 +35,20 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -35,10 +35,20 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -35,10 +35,20 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -35,10 +35,20 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
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
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd 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
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -35,9 +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/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/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/file_source.dylib
|
||||||
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/fobossdr_source/fobossdr_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/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/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/limesdr_source/limesdr_source.dylib
|
||||||
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/network_source/network_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/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/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/rfnm_source/rfnm_source.dylib
|
||||||
|
@ -24,6 +24,9 @@ cp 'C:/Program Files/PothosSDR/bin/bladeRF.dll' sdrpp_windows_x64/
|
|||||||
|
|
||||||
cp $build_dir/source_modules/file_source/Release/file_source.dll sdrpp_windows_x64/modules/
|
cp $build_dir/source_modules/file_source/Release/file_source.dll sdrpp_windows_x64/modules/
|
||||||
|
|
||||||
|
cp $build_dir/source_modules/fobossdr_source/Release/fobossdr_source.dll sdrpp_windows_x64/modules/
|
||||||
|
cp 'C:/Program Files/RigExpert/Fobos/bin/fobos.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
cp $build_dir/source_modules/hackrf_source/Release/hackrf_source.dll sdrpp_windows_x64/modules/
|
cp $build_dir/source_modules/hackrf_source/Release/hackrf_source.dll sdrpp_windows_x64/modules/
|
||||||
cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/
|
cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
@ -32,6 +35,8 @@ cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windo
|
|||||||
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
|
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
|
||||||
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
|
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
|
cp $build_dir/source_modules/network_source/Release/network_source.dll sdrpp_windows_x64/modules/
|
||||||
|
|
||||||
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
|
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
|
||||||
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
|
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
|
@ -168,10 +168,9 @@ public:
|
|||||||
writer.setSamplerate(samplerate);
|
writer.setSamplerate(samplerate);
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
|
||||||
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
|
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
|
||||||
std::string extension = ".wav";
|
std::string extension = ".wav";
|
||||||
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
|
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, recMode, vfoName) + extension);
|
||||||
if (!writer.open(expandedPath)) {
|
if (!writer.open(expandedPath)) {
|
||||||
flog::error("Failed to open file for recording: {0}", expandedPath);
|
flog::error("Failed to open file for recording: {0}", expandedPath);
|
||||||
return;
|
return;
|
||||||
@ -452,7 +451,7 @@ private:
|
|||||||
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string genFileName(std::string templ, std::string type, std::string name) {
|
std::string genFileName(std::string templ, int mode, std::string name) {
|
||||||
// Get data
|
// Get data
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
tm* ltm = localtime(&now);
|
tm* ltm = localtime(&now);
|
||||||
@ -462,6 +461,9 @@ private:
|
|||||||
freq += gui::waterfall.vfos[name]->generalOffset;
|
freq += gui::waterfall.vfos[name]->generalOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Select the recording type string
|
||||||
|
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
||||||
|
|
||||||
// Format to string
|
// Format to string
|
||||||
char freqStr[128];
|
char freqStr[128];
|
||||||
char hourStr[128];
|
char hourStr[128];
|
||||||
@ -470,7 +472,7 @@ private:
|
|||||||
char dayStr[128];
|
char dayStr[128];
|
||||||
char monStr[128];
|
char monStr[128];
|
||||||
char yearStr[128];
|
char yearStr[128];
|
||||||
const char* modeStr = "Unknown";
|
const char* modeStr = (recMode == RECORDER_MODE_AUDIO) ? "Unknown" : "IQ";
|
||||||
sprintf(freqStr, "%.0lfHz", freq);
|
sprintf(freqStr, "%.0lfHz", freq);
|
||||||
sprintf(hourStr, "%02d", ltm->tm_hour);
|
sprintf(hourStr, "%02d", ltm->tm_hour);
|
||||||
sprintf(minStr, "%02d", ltm->tm_min);
|
sprintf(minStr, "%02d", ltm->tm_min);
|
||||||
|
@ -10,23 +10,54 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <radio_interface.h>
|
#include <radio_interface.h>
|
||||||
|
#include <utils/optionlist.h>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
SDRPP_MOD_INFO{
|
SDRPP_MOD_INFO{
|
||||||
/* Name: */ "rigctl_client",
|
/* Name: */ "rigctl_client",
|
||||||
/* Description: */ "Client for the RigCTL protocol",
|
/* Description: */ "Client for the RigCTL protocol",
|
||||||
/* Author: */ "Ryzerth",
|
/* Author: */ "Ryzerth",
|
||||||
/* Version: */ 0, 1, 0,
|
/* Version: */ 0, 2, 0,
|
||||||
/* Max instances */ 1
|
/* Max instances */ 1
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigManager config;
|
ConfigManager config;
|
||||||
|
|
||||||
|
enum Mode {
|
||||||
|
MODE_PANADAPTER,
|
||||||
|
MODE_MIRROR
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Priority {
|
||||||
|
PRIOR_SDR,
|
||||||
|
PRIOR_RIGCTL
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::map<int, net::rigctl::Mode> RADIO_TO_RIGCTL = {
|
||||||
|
{ RADIO_IFACE_MODE_NFM, net::rigctl::MODE_FM },
|
||||||
|
{ RADIO_IFACE_MODE_WFM, net::rigctl::MODE_WFM },
|
||||||
|
{ RADIO_IFACE_MODE_AM , net::rigctl::MODE_AM },
|
||||||
|
{ RADIO_IFACE_MODE_DSB, net::rigctl::MODE_DSB },
|
||||||
|
{ RADIO_IFACE_MODE_USB, net::rigctl::MODE_USB },
|
||||||
|
{ RADIO_IFACE_MODE_CW , net::rigctl::MODE_CW },
|
||||||
|
{ RADIO_IFACE_MODE_LSB, net::rigctl::MODE_LSB }
|
||||||
|
};
|
||||||
|
|
||||||
class RigctlClientModule : public ModuleManager::Instance {
|
class RigctlClientModule : public ModuleManager::Instance {
|
||||||
public:
|
public:
|
||||||
RigctlClientModule(std::string name) {
|
RigctlClientModule(std::string name) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
|
|
||||||
|
// Define the operation modes
|
||||||
|
modes.define("panadapter", "Pandapter", MODE_PANADAPTER);
|
||||||
|
modes.define("mirror", "Mirror", MODE_MIRROR);
|
||||||
|
|
||||||
|
// Define the priority modes
|
||||||
|
priorities.define("sdr", "SDR", PRIOR_SDR);
|
||||||
|
priorities.define("rigctl", "RigCTL", PRIOR_RIGCTL);
|
||||||
|
|
||||||
// Load default
|
// Load default
|
||||||
strcpy(host, "127.0.0.1");
|
strcpy(host, "127.0.0.1");
|
||||||
|
|
||||||
@ -40,13 +71,28 @@ public:
|
|||||||
port = config.conf[name]["port"];
|
port = config.conf[name]["port"];
|
||||||
port = std::clamp<int>(port, 1, 65535);
|
port = std::clamp<int>(port, 1, 65535);
|
||||||
}
|
}
|
||||||
|
if (config.conf[name].contains("mode")) {
|
||||||
|
std::string modeStr = config.conf[name]["mode"];
|
||||||
|
if (modes.keyExists(modeStr)) { modeId = modes.keyId(modeStr); }
|
||||||
|
}
|
||||||
|
if (config.conf[name].contains("priority")) {
|
||||||
|
std::string priorityStr = config.conf[name]["priority"];
|
||||||
|
if (priorities.keyExists(priorityStr)) { priorityId = modes.keyId(priorityStr); }
|
||||||
|
}
|
||||||
|
if (config.conf[name].contains("interval")) {
|
||||||
|
interval = config.conf[name]["interval"];
|
||||||
|
interval = std::clamp<int>(interval, 100, 1000);
|
||||||
|
}
|
||||||
if (config.conf[name].contains("ifFreq")) {
|
if (config.conf[name].contains("ifFreq")) {
|
||||||
ifFreq = config.conf[name]["ifFreq"];
|
ifFreq = config.conf[name]["ifFreq"];
|
||||||
}
|
}
|
||||||
|
if (config.conf[name].contains("vfo")) {
|
||||||
|
selectedVFO = config.conf[name]["vfo"];
|
||||||
|
}
|
||||||
config.release();
|
config.release();
|
||||||
|
|
||||||
_retuneHandler.ctx = this;
|
// Refresh VFOs
|
||||||
_retuneHandler.handler = retuneHandler;
|
refreshVFOs();
|
||||||
|
|
||||||
gui::menu.registerEntry(name, menuHandler, this, NULL);
|
gui::menu.registerEntry(name, menuHandler, this, NULL);
|
||||||
}
|
}
|
||||||
@ -85,10 +131,20 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mode == MODE_PANADAPTER) {
|
||||||
// Switch source to panadapter mode
|
// Switch source to panadapter mode
|
||||||
sigpath::sourceManager.setPanadapterIF(ifFreq);
|
sigpath::sourceManager.setPanadapterIF(ifFreq);
|
||||||
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER);
|
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER);
|
||||||
sigpath::sourceManager.onRetune.bindHandler(&_retuneHandler);
|
}
|
||||||
|
|
||||||
|
// Start the worker thread
|
||||||
|
run = true;
|
||||||
|
if (mode == MODE_PANADAPTER) {
|
||||||
|
workerThread = std::thread(&RigctlClientModule::panadapterWorker, this);
|
||||||
|
}
|
||||||
|
else if (mode == MODE_MIRROR) {
|
||||||
|
workerThread = std::thread(&RigctlClientModule::mirrorWorker, this);
|
||||||
|
}
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
}
|
}
|
||||||
@ -97,9 +153,15 @@ public:
|
|||||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
if (!running) { return; }
|
if (!running) { return; }
|
||||||
|
|
||||||
|
// Stop the worker thread
|
||||||
|
run = false;
|
||||||
|
if (workerThread.joinable()) { workerThread.join(); }
|
||||||
|
|
||||||
|
if (mode == MODE_PANADAPTER) {
|
||||||
// Switch source back to normal mode
|
// Switch source back to normal mode
|
||||||
sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler);
|
sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler);
|
||||||
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL);
|
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
// Disconnect from rigctl server
|
// Disconnect from rigctl server
|
||||||
client->close();
|
client->close();
|
||||||
@ -125,8 +187,35 @@ private:
|
|||||||
config.conf[_this->name]["port"] = _this->port;
|
config.conf[_this->name]["port"] = _this->port;
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
if (_this->running) { style::endDisabled(); }
|
|
||||||
|
|
||||||
|
ImGui::LeftLabel("Mode");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Combo(CONCAT("##_rigctl_cli_mode_", _this->name), &_this->modeId, _this->modes.txt)) {
|
||||||
|
_this->mode = _this->modes[_this->modeId];
|
||||||
|
config.acquire();
|
||||||
|
config.conf[_this->name]["mode"] = _this->modes.key(_this->modeId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::LeftLabel("Priority");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Combo(CONCAT("##_rigctl_cli_priority_", _this->name), &_this->priorityId, _this->priorities.txt)) {
|
||||||
|
_this->priority = _this->priorities[_this->priorityId];
|
||||||
|
config.acquire();
|
||||||
|
config.conf[_this->name]["priority"] = _this->priorities.key(_this->priorityId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::LeftLabel("Interval");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::InputInt(CONCAT("##_rigctl_cli_interval_", _this->name), &_this->interval, 10, 100)) {
|
||||||
|
_this->interval = std::clamp<int>(_this->interval, 100, 1000);
|
||||||
|
config.acquire();
|
||||||
|
config.conf[_this->name]["interval"] = _this->interval;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->mode == MODE_PANADAPTER) {
|
||||||
ImGui::LeftLabel("IF Frequency");
|
ImGui::LeftLabel("IF Frequency");
|
||||||
ImGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
if (ImGui::InputDouble(CONCAT("##_rigctl_if_freq_", _this->name), &_this->ifFreq, 100.0, 100000.0, "%.0f")) {
|
if (ImGui::InputDouble(CONCAT("##_rigctl_if_freq_", _this->name), &_this->ifFreq, 100.0, 100000.0, "%.0f")) {
|
||||||
@ -137,6 +226,30 @@ private:
|
|||||||
config.conf[_this->name]["ifFreq"] = _this->ifFreq;
|
config.conf[_this->name]["ifFreq"] = _this->ifFreq;
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (_this->mode == MODE_MIRROR) {
|
||||||
|
ImGui::LeftLabel("Controlled VFO");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Combo(CONCAT("##_rigctl_cli_vfo_", _this->name), &_this->vfoId, _this->vfos.txt)) {
|
||||||
|
_this->selectedVFO = _this->vfos[_this->vfoId];
|
||||||
|
config.acquire();
|
||||||
|
config.conf[_this->name]["vfo"] = _this->vfos.key(_this->vfoId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Checkbox(CONCAT("Sync Frequency##_rigctl_sync_freq_", _this->name), &_this->syncFrequency);
|
||||||
|
if (_this->vfoIsRadio) {
|
||||||
|
ImGui::Checkbox(CONCAT("Sync Mode##_rigctl_sync_freq_", _this->name), &_this->syncMode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bool dummy = false;
|
||||||
|
if (!_this->running) { style::beginDisabled(); }
|
||||||
|
ImGui::Checkbox(CONCAT("Sync Mode##_rigctl_sync_freq_", _this->name), &dummy);
|
||||||
|
if (!_this->running) { style::endDisabled(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { style::endDisabled(); }
|
||||||
|
|
||||||
ImGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
if (_this->running && ImGui::Button(CONCAT("Stop##_rigctl_cli_stop_", _this->name), ImVec2(menuWidth, 0))) {
|
if (_this->running && ImGui::Button(CONCAT("Stop##_rigctl_cli_stop_", _this->name), ImVec2(menuWidth, 0))) {
|
||||||
@ -159,11 +272,131 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void retuneHandler(double freq, void* ctx) {
|
void selectVFO(const std::string& vfoName) {
|
||||||
RigctlClientModule* _this = (RigctlClientModule*)ctx;
|
// If no vfo is available, deselect
|
||||||
if (!_this->client || !_this->client->isOpen()) { return; }
|
if (vfos.empty()) {
|
||||||
if (_this->client->setFreq(freq)) {
|
selectedVFO.clear();
|
||||||
flog::error("Could not set frequency");
|
vfoIsRadio = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a vfo with that name isn't found, select the first VFO in the list
|
||||||
|
if (!vfos.keyExists(vfoName)) {
|
||||||
|
selectVFO(vfos.key(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the VFO is from a radio module
|
||||||
|
vfoIsRadio = (core::moduleManager.getInstanceModuleName(vfoName) == "radio");
|
||||||
|
|
||||||
|
// Update the selected VFO
|
||||||
|
selectedVFO = vfoName;
|
||||||
|
vfoId = vfos.keyId(vfoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void refreshVFOs() {
|
||||||
|
// Clear the list
|
||||||
|
vfos.clear();
|
||||||
|
|
||||||
|
// Define using the VFO list from the waterfall
|
||||||
|
for (auto const& [_name, vfo] : gui::waterfall.vfos) {
|
||||||
|
vfos.define(_name, _name, _name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reselect the current VFO
|
||||||
|
selectVFO(selectedVFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
void panadapterWorker() {
|
||||||
|
int64_t lastRigctlFreq = -1;
|
||||||
|
int64_t lastCenterFreq = -1;
|
||||||
|
int64_t rigctlFreq;
|
||||||
|
int64_t centerFreq;
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
// Query the current modes
|
||||||
|
try {
|
||||||
|
// Get the current rigctl frequency
|
||||||
|
rigctlFreq = (int64_t)client->getFreq();
|
||||||
|
|
||||||
|
// Get the current center frequency
|
||||||
|
centerFreq = (int64_t)gui::waterfall.getCenterFrequency();
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
flog::error("Error while getting frequencies: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update frequencies depending on the priority
|
||||||
|
if (priority == PRIOR_SDR) {
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (priority == PRIOR_RIGCTL) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save frequencies
|
||||||
|
lastRigctlFreq = rigctlFreq;
|
||||||
|
lastCenterFreq = centerFreq;
|
||||||
|
|
||||||
|
// Wait for the time interval
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void mirrorWorker() {
|
||||||
|
int64_t lastRigctlFreq = -1;
|
||||||
|
int64_t lastFreq = -1;
|
||||||
|
int64_t rigctlFreq;
|
||||||
|
int64_t freq;
|
||||||
|
int lastRigctlMode = -1;
|
||||||
|
int lastVFOMode = -1;
|
||||||
|
int rigctlMode;
|
||||||
|
int vfoMode;
|
||||||
|
|
||||||
|
while (run) {
|
||||||
|
// Query the current modes
|
||||||
|
try {
|
||||||
|
// Get the current rigctl frequency
|
||||||
|
rigctlFreq = (int64_t)client->getFreq();
|
||||||
|
|
||||||
|
// Get the rigctl and VFO modes
|
||||||
|
if (selectedVFO.empty()) {
|
||||||
|
// Get the VFO frequency
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Get the mode if needed
|
||||||
|
if (syncMode && vfoIsRadio) {
|
||||||
|
// Get the current rigctl mode
|
||||||
|
// rigctlMode = client->getMode();
|
||||||
|
|
||||||
|
// Get the current VFO mode
|
||||||
|
core::modComManager.callInterface(selectedVFO, RADIO_IFACE_CMD_GET_MODE, NULL, &vfoMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
freq = (int64_t)gui::waterfall.getCenterFrequency();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
flog::error("Error while getting frequencies: {}", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update frequencies depending on the priority
|
||||||
|
if (priority == PRIOR_SDR) {
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (priority == PRIOR_RIGCTL) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save modes and frequencies
|
||||||
|
lastRigctlFreq = rigctlFreq;
|
||||||
|
lastFreq = freq;
|
||||||
|
lastRigctlMode = rigctlMode;
|
||||||
|
lastVFOMode = vfoMode;
|
||||||
|
|
||||||
|
// Wait for the time interval
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(interval));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,9 +409,31 @@ private:
|
|||||||
int port = 4532;
|
int port = 4532;
|
||||||
std::shared_ptr<net::rigctl::Client> client;
|
std::shared_ptr<net::rigctl::Client> client;
|
||||||
|
|
||||||
|
Mode mode = MODE_PANADAPTER;
|
||||||
|
int modeId = 0;
|
||||||
|
|
||||||
|
Priority priority = PRIOR_SDR;
|
||||||
|
int priorityId = 0;
|
||||||
|
|
||||||
|
int interval = 100;
|
||||||
|
|
||||||
double ifFreq = 8830000.0;
|
double ifFreq = 8830000.0;
|
||||||
|
|
||||||
|
bool syncFrequency = true;
|
||||||
|
bool syncMode = true;
|
||||||
|
bool vfoIsRadio = false;
|
||||||
|
|
||||||
EventHandler<double> _retuneHandler;
|
EventHandler<double> _retuneHandler;
|
||||||
|
|
||||||
|
OptionList<std::string, Mode> modes;
|
||||||
|
OptionList<std::string, Priority> priorities;
|
||||||
|
|
||||||
|
std::string selectedVFO = "";
|
||||||
|
int vfoId = 0;
|
||||||
|
OptionList<std::string, std::string> vfos;
|
||||||
|
|
||||||
|
std::atomic_bool run;
|
||||||
|
std::thread workerThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
MOD_EXPORT void _INIT_() {
|
MOD_EXPORT void _INIT_() {
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <utils/networking.h>
|
|
||||||
|
|
||||||
class RigCTLClient {
|
|
||||||
public:
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
};
|
|
@ -18,7 +18,7 @@ SDRPP_MOD_INFO{
|
|||||||
/* Name: */ "rigctl_server",
|
/* Name: */ "rigctl_server",
|
||||||
/* Description: */ "My fancy new module",
|
/* Description: */ "My fancy new module",
|
||||||
/* Author: */ "Ryzerth",
|
/* Author: */ "Ryzerth",
|
||||||
/* Version: */ 0, 1, 0,
|
/* Version: */ 0, 1, 1,
|
||||||
/* Max instances */ -1
|
/* Max instances */ -1
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,21 +153,16 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::BeginTable(CONCAT("Stop##_rigctl_srv_tbl_", _this->name), 2);
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableSetColumnIndex(0);
|
|
||||||
if (ImGui::Checkbox(CONCAT("Tuning##_rigctl_srv_tune_ena_", _this->name), &_this->tuningEnabled)) {
|
if (ImGui::Checkbox(CONCAT("Tuning##_rigctl_srv_tune_ena_", _this->name), &_this->tuningEnabled)) {
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["tuning"] = _this->tuningEnabled;
|
config.conf[_this->name]["tuning"] = _this->tuningEnabled;
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
ImGui::TableSetColumnIndex(1);
|
|
||||||
if (ImGui::Checkbox(CONCAT("Recording##_rigctl_srv_tune_ena_", _this->name), &_this->recordingEnabled)) {
|
if (ImGui::Checkbox(CONCAT("Recording##_rigctl_srv_tune_ena_", _this->name), &_this->recordingEnabled)) {
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["recording"] = _this->recordingEnabled;
|
config.conf[_this->name]["recording"] = _this->recordingEnabled;
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
ImGui::EndTable();
|
|
||||||
|
|
||||||
if (ImGui::Checkbox(CONCAT("Listen on startup##_rigctl_srv_auto_lst_", _this->name), &_this->autoStart)) {
|
if (ImGui::Checkbox(CONCAT("Listen on startup##_rigctl_srv_auto_lst_", _this->name), &_this->autoStart)) {
|
||||||
config.acquire();
|
config.acquire();
|
||||||
|
21
readme.md
21
readme.md
@ -324,11 +324,13 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
|
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
|
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
|
||||||
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
||||||
|
| fobossdr_source | Working | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
|
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| harogic_source | Unfinished | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ⛔ |
|
| harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ✅ |
|
||||||
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
|
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
|
||||||
|
| kcsdr_source | Unfinished | libkcsdr | OPT_BUILD_KCSDR_SOURCE | ⛔ | ⛔ | ⛔ |
|
||||||
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
|
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
|
||||||
| network_source | Unfinished | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | ⛔ |
|
| network_source | Beta | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
|
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
|
||||||
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_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 | ⛔ | ✅ | ✅ |
|
||||||
@ -339,9 +341,9 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| sdrpp_server_source | Working | - | OPT_BUILD_SDRPP_SERVER_SOURCE | ✅ | ✅ | ✅ |
|
| sdrpp_server_source | Working | - | OPT_BUILD_SDRPP_SERVER_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| soapy_source | Deprecated | soapysdr | OPT_BUILD_SOAPY_SOURCE | ⛔ | ⛔ | ⛔ |
|
| soapy_source | Deprecated | soapysdr | OPT_BUILD_SOAPY_SOURCE | ⛔ | ⛔ | ⛔ |
|
||||||
| spectran_source | Unfinished | RTSA Suite | OPT_BUILD_SPECTRAN_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 | ✅ | ✅ | ✅ |
|
| spyserver_source | Working | - | OPT_BUILD_SPYSERVER_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| usrp_source | Beta | libuhd | OPT_BUILD_USRP_SOURCE | ⛔ | ⛔ | ⛔ |
|
| usrp_source | Beta | libuhd | OPT_BUILD_USRP_SOURCE | ⛔ | ⛔ | ✅ |
|
||||||
|
|
||||||
## Sinks
|
## Sinks
|
||||||
|
|
||||||
@ -350,14 +352,15 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
|
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
|
||||||
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
|
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
|
||||||
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
|
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
|
||||||
| new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
| new_portaudio_sink | Working | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||||
| portaudio_sink | Beta | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
| portaudio_sink | Working | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||||
|
|
||||||
## Decoders
|
## Decoders
|
||||||
|
|
||||||
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|
||||||
|---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:|
|
|---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:|
|
||||||
| atv_decoder | Unfinished | - | OPT_BUILD_ATV_DECODER | ⛔ | ⛔ | ⛔ |
|
| atv_decoder | Unfinished | - | OPT_BUILD_ATV_DECODER | ⛔ | ⛔ | ⛔ |
|
||||||
|
| dab_decoder | Unfinished | - | OPT_BUILD_DAB_DECODER | ⛔ | ⛔ | ⛔ |
|
||||||
| falcon9_decoder | Unfinished | ffplay | OPT_BUILD_FALCON9_DECODER | ⛔ | ⛔ | ⛔ |
|
| falcon9_decoder | Unfinished | ffplay | OPT_BUILD_FALCON9_DECODER | ⛔ | ⛔ | ⛔ |
|
||||||
| kgsstv_decoder | Unfinished | - | OPT_BUILD_KGSSTV_DECODER | ⛔ | ⛔ | ⛔ |
|
| kgsstv_decoder | Unfinished | - | OPT_BUILD_KGSSTV_DECODER | ⛔ | ⛔ | ⛔ |
|
||||||
| m17_decoder | Working | - | OPT_BUILD_M17_DECODER | ⛔ | ✅ | ⛔ |
|
| m17_decoder | Working | - | OPT_BUILD_M17_DECODER | ⛔ | ✅ | ⛔ |
|
||||||
@ -374,7 +377,7 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| frequency_manager | Working | - | OPT_BUILD_FREQUENCY_MANAGER | ✅ | ✅ | ✅ |
|
| frequency_manager | Working | - | OPT_BUILD_FREQUENCY_MANAGER | ✅ | ✅ | ✅ |
|
||||||
| iq_exporter | Working | - | OPT_BUILD_IQ_EXPORTER | ✅ | ✅ | ⛔ |
|
| iq_exporter | Working | - | OPT_BUILD_IQ_EXPORTER | ✅ | ✅ | ⛔ |
|
||||||
| recorder | Working | - | OPT_BUILD_RECORDER | ✅ | ✅ | ✅ |
|
| recorder | Working | - | OPT_BUILD_RECORDER | ✅ | ✅ | ✅ |
|
||||||
| rigctl_client | Unfinished | - | OPT_BUILD_RIGCTL_CLIENT | ✅ | ✅ | ⛔ |
|
| rigctl_client | Beta | - | OPT_BUILD_RIGCTL_CLIENT | ✅ | ✅ | ⛔ |
|
||||||
| rigctl_server | Working | - | OPT_BUILD_RIGCTL_SERVER | ✅ | ✅ | ✅ |
|
| rigctl_server | Working | - | OPT_BUILD_RIGCTL_SERVER | ✅ | ✅ | ✅ |
|
||||||
| scanner | Beta | - | OPT_BUILD_SCANNER | ✅ | ✅ | ⛔ |
|
| scanner | Beta | - | OPT_BUILD_SCANNER | ✅ | ✅ | ⛔ |
|
||||||
| scheduler | Unfinished | - | OPT_BUILD_SCHEDULER | ⛔ | ⛔ | ⛔ |
|
| scheduler | Unfinished | - | OPT_BUILD_SCHEDULER | ⛔ | ⛔ | ⛔ |
|
||||||
@ -416,8 +419,8 @@ If you still have an issue, please open an issue about it or ask on the discord.
|
|||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Feel free to submit pull requests and report bugs via the GitHub issue tracker.
|
Feel free to submit band plans via the GitHub issue tracker.
|
||||||
I will soon publish a contributing.md listing the code style to use.
|
For code changes, please create a feature request instead.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
|
201
root/res/bandplans/ireland.json
Normal file
201
root/res/bandplans/ireland.json
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
{
|
||||||
|
"name": "Ireland",
|
||||||
|
"country_name": "Republic Of Ireland",
|
||||||
|
"country_code": "IE",
|
||||||
|
"author_name": "Oskar Dudek",
|
||||||
|
"author_url": "",
|
||||||
|
"bands": [
|
||||||
|
{
|
||||||
|
"name": "2200m Ham Band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 135700,
|
||||||
|
"end": 137800
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Long wave",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 148500,
|
||||||
|
"end": 282500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "AM broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 531000,
|
||||||
|
"end": 1602000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "160m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 1810000,
|
||||||
|
"end": 2000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "120m SW broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 2300000,
|
||||||
|
"end": 2495000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "90m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 3200000,
|
||||||
|
"end": 3400000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "80m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 3500000,
|
||||||
|
"end": 3800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "75m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 3900000,
|
||||||
|
"end": 4000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "60m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 4750000,
|
||||||
|
"end": 5060000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "60m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 5351500,
|
||||||
|
"end": 5366500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "49m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 5900000,
|
||||||
|
"end": 6200000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "40m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 7000000,
|
||||||
|
"end": 7200000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "40m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 7200000,
|
||||||
|
"end": 7450000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "31m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 9400000,
|
||||||
|
"end": 9900000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "30m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 10100000,
|
||||||
|
"end": 10150000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "25m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 11600000,
|
||||||
|
"end": 12100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "22m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 13570000,
|
||||||
|
"end": 13870000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "20m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 14000000,
|
||||||
|
"end": 14350000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "19m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 15100000,
|
||||||
|
"end": 15800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "17m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 18068000,
|
||||||
|
"end": 18168000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "16m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 17480000,
|
||||||
|
"end": 17900000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "15m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 18900000,
|
||||||
|
"end": 19020000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "15m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 21000000,
|
||||||
|
"end": 21450000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "13m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 21450000,
|
||||||
|
"end": 21850000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "12m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 24890000,
|
||||||
|
"end": 24990000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "11m SW Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 25670000,
|
||||||
|
"end": 26100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "10m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 28000000,
|
||||||
|
"end": 29700000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FM Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 87500000,
|
||||||
|
"end": 108000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "6m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 50000000,
|
||||||
|
"end": 52000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "4m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 70000000,
|
||||||
|
"end": 70500000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 144000000,
|
||||||
|
"end": 146000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70cm ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 430000000,
|
||||||
|
"end": 440000000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
549
root/res/bandplans/republic-of-korea.json
Normal file
549
root/res/bandplans/republic-of-korea.json
Normal file
@ -0,0 +1,549 @@
|
|||||||
|
{
|
||||||
|
"name": "Republic of Korea",
|
||||||
|
"country_name": "Republic of Korea",
|
||||||
|
"country_code": "KR",
|
||||||
|
"author_name": "SeoyeonBae",
|
||||||
|
"author_url": "https://github.com/bsy0317",
|
||||||
|
"bands": [
|
||||||
|
{
|
||||||
|
"name": "Radio Navigation",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 8300,
|
||||||
|
"end": 14000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Coastal Telegraph",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 14000,
|
||||||
|
"end": 19950
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 19950,
|
||||||
|
"end": 20250
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Coastal Telegraph",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 20250,
|
||||||
|
"end": 70000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radio Navigation",
|
||||||
|
"type": "navigation",
|
||||||
|
"start": 70000,
|
||||||
|
"end": 160000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Radio Navigation",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 160000,
|
||||||
|
"end": 285000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Maritime Radiobeacon",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 285000,
|
||||||
|
"end": 325000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Radio Navigation",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 325000,
|
||||||
|
"end": 472000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 472000,
|
||||||
|
"end": 479000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "International Distress Safety Call",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 479000,
|
||||||
|
"end": 505000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Maritime Telegraph",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 505000,
|
||||||
|
"end": 526500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 526500,
|
||||||
|
"end": 1606500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiobuoy",
|
||||||
|
"type": "navigation",
|
||||||
|
"start": 1606500,
|
||||||
|
"end": 1800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 1800000,
|
||||||
|
"end": 1825000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiobuoy Control LORAN",
|
||||||
|
"type": "radiolocation",
|
||||||
|
"start": 1825000,
|
||||||
|
"end": 2000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiobuoy",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 2000000,
|
||||||
|
"end": 2065000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Distress Call",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 2065000,
|
||||||
|
"end": 2107000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "International Distress Search and Rescue",
|
||||||
|
"type": "mobile",
|
||||||
|
"start": 2173500,
|
||||||
|
"end": 2190500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Road Management",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 2194000,
|
||||||
|
"end": 2495000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 2495000,
|
||||||
|
"end": 2505000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Telephone",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 2505000,
|
||||||
|
"end": 2850000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile R",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 2850000,
|
||||||
|
"end": 3025000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile OR",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 3025000,
|
||||||
|
"end": 3155000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile R",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 3400000,
|
||||||
|
"end": 3500000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 3500000,
|
||||||
|
"end": 3550000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Experimental Station",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 3550000,
|
||||||
|
"end": 3790000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 3790000,
|
||||||
|
"end": 3800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 3900000,
|
||||||
|
"end": 3950000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 3995000,
|
||||||
|
"end": 4005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Telephone",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4005000,
|
||||||
|
"end": 4063000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Oceanographic Data",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4063000,
|
||||||
|
"end": 4065000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Duplex Telephone",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4065000,
|
||||||
|
"end": 4146000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Simplex Telephone",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4146000,
|
||||||
|
"end": 4152000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Wideband Telegraph Fax",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4152000,
|
||||||
|
"end": 4172000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Narrowband",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4172000,
|
||||||
|
"end": 4181750
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station A1A Morse Code Communication",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4186750,
|
||||||
|
"end": 4202250
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiolocation",
|
||||||
|
"type": "radiolocation",
|
||||||
|
"start": 4438000,
|
||||||
|
"end": 4488000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Calling Response",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 4488000,
|
||||||
|
"end": 4650000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile R",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 4650000,
|
||||||
|
"end": 4850000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 4995000,
|
||||||
|
"end": 5005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Search Rescue",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 5480000,
|
||||||
|
"end": 5730000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 5900000,
|
||||||
|
"end": 5950000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 5950000,
|
||||||
|
"end": 6200000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 7000000,
|
||||||
|
"end": 7100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 7100000,
|
||||||
|
"end": 7200000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 7200000,
|
||||||
|
"end": 7450000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 7995000,
|
||||||
|
"end": 8005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 9400000,
|
||||||
|
"end": 9500000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 9500000,
|
||||||
|
"end": 9900000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 9995000,
|
||||||
|
"end": 10005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 10100000,
|
||||||
|
"end": 10150000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 10150000,
|
||||||
|
"end": 11600000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 11600000,
|
||||||
|
"end": 11650000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 11650000,
|
||||||
|
"end": 12050000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 12050000,
|
||||||
|
"end": 12100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 13260000,
|
||||||
|
"end": 13360000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radio Astronomy",
|
||||||
|
"type": "astronomy",
|
||||||
|
"start": 13360000,
|
||||||
|
"end": 13410000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 13570000,
|
||||||
|
"end": 13600000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 13600000,
|
||||||
|
"end": 13800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 13800000,
|
||||||
|
"end": 13870000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 14000000,
|
||||||
|
"end": 14350000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 15010000,
|
||||||
|
"end": 15100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 15100000,
|
||||||
|
"end": 15600000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 15600000,
|
||||||
|
"end": 15800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 15800000,
|
||||||
|
"end": 15995000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 15995000,
|
||||||
|
"end": 16005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 18900000,
|
||||||
|
"end": 19020000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 21000000,
|
||||||
|
"end": 21450000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 21450000,
|
||||||
|
"end": 21850000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 24890000,
|
||||||
|
"end": 24990000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 25670000,
|
||||||
|
"end": 26100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 28000000,
|
||||||
|
"end": 29700000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 50000000,
|
||||||
|
"end": 54000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TV Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 54000000,
|
||||||
|
"end": 72000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Flood Warning",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 72000000,
|
||||||
|
"end": 74800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TV Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 76000000,
|
||||||
|
"end": 88000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FM Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 88000000,
|
||||||
|
"end": 100000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FM Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 100000000,
|
||||||
|
"end": 108000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ILS Localizer VOR",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 108000000,
|
||||||
|
"end": 117975000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 144000000,
|
||||||
|
"end": 146000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "General Communication",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 146000000,
|
||||||
|
"end": 148000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Low Power Device",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 162037500,
|
||||||
|
"end": 174000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TV Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 174000000,
|
||||||
|
"end": 216000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Low Power Device",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 216000000,
|
||||||
|
"end": 230000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Low Power Device",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 273000000,
|
||||||
|
"end": 322000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Personal Radio",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 420000000,
|
||||||
|
"end": 470000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Public Network",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 698000000,
|
||||||
|
"end": 806000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Low Power Device",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 942000000,
|
||||||
|
"end": 960000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Satellite Mobile Communication",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 15250000000,
|
||||||
|
"end": 16605000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mobile Communication",
|
||||||
|
"type": "mobile",
|
||||||
|
"start": 25000000000,
|
||||||
|
"end": 37000000000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -3,7 +3,7 @@ Encoding=UTF-8
|
|||||||
Version=1.0
|
Version=1.0
|
||||||
Type=Application
|
Type=Application
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Exec=@SDRPP_BIN_INSTALL_DIR@/sdrpp
|
Exec=@CMAKE_INSTALL_PREFIX@/bin/sdrpp
|
||||||
Name=SDR++
|
Name=SDR++
|
||||||
Icon=@SDRPP_RES_INSTALL_DIR@/icons/sdrpp.png
|
Icon=@CMAKE_INSTALL_PREFIX@/share/sdrpp/icons/sdrpp.png
|
||||||
Categories=HamRadio
|
Categories=HamRadio
|
@ -6,10 +6,6 @@ if (NOT SDRPP_MODULE_COMPILER_FLAGS)
|
|||||||
set(SDRPP_MODULE_COMPILER_FLAGS @SDRPP_MODULE_COMPILER_FLAGS@)
|
set(SDRPP_MODULE_COMPILER_FLAGS @SDRPP_MODULE_COMPILER_FLAGS@)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Add compiler definitions for the directories
|
|
||||||
add_definitions(-DSDRPP_MODULES_LOAD_DIR="${SDRPP_MODULES_LOAD_DIR}")
|
|
||||||
add_definitions(-DSDRPP_RES_LOAD_DIR="${SDRPP_RES_LOAD_DIR}")
|
|
||||||
|
|
||||||
# Created shared lib and link to core
|
# Created shared lib and link to core
|
||||||
add_library(${PROJECT_NAME} SHARED ${SRC})
|
add_library(${PROJECT_NAME} SHARED ${SRC})
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE sdrpp_core)
|
target_link_libraries(${PROJECT_NAME} PRIVATE sdrpp_core)
|
||||||
@ -20,4 +16,4 @@ set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
|
|||||||
target_compile_options(${PROJECT_NAME} PRIVATE ${SDRPP_MODULE_COMPILER_FLAGS})
|
target_compile_options(${PROJECT_NAME} PRIVATE ${SDRPP_MODULE_COMPILER_FLAGS})
|
||||||
|
|
||||||
# Install directives
|
# Install directives
|
||||||
install(TARGETS ${PROJECT_NAME} DESTINATION ${SDRPP_MODULES_INSTALL_DIR})
|
install(TARGETS ${PROJECT_NAME} DESTINATION lib/sdrpp/plugins)
|
@ -217,6 +217,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void startServer() {
|
void startServer() {
|
||||||
|
try {
|
||||||
if (modeId == SINK_MODE_TCP) {
|
if (modeId == SINK_MODE_TCP) {
|
||||||
listener = net::listen(hostname, port);
|
listener = net::listen(hostname, port);
|
||||||
if (listener) {
|
if (listener) {
|
||||||
@ -227,6 +228,10 @@ private:
|
|||||||
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
|
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
flog::error("Failed to open socket: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void stopServer() {
|
void stopServer() {
|
||||||
if (conn) { conn->close(); }
|
if (conn) { conn->close(); }
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <libbladeRF.h>
|
#include <libbladeRF.h>
|
||||||
#include <gui/smgui.h>
|
#include <gui/smgui.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <utils/optionlist.h>
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
@ -37,6 +38,10 @@ public:
|
|||||||
BladeRFSourceModule(std::string name) {
|
BladeRFSourceModule(std::string name) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
|
|
||||||
|
// Define clocks
|
||||||
|
clocks.define("onboard", "On-Board", CLOCK_SELECT_ONBOARD);
|
||||||
|
clocks.define("external", "External", CLOCK_SELECT_EXTERNAL);
|
||||||
|
|
||||||
sampleRate = 1000000.0;
|
sampleRate = 1000000.0;
|
||||||
|
|
||||||
handler.ctx = this;
|
handler.ctx = this;
|
||||||
@ -267,6 +272,15 @@ public:
|
|||||||
}
|
}
|
||||||
config.release(true);
|
config.release(true);
|
||||||
|
|
||||||
|
// Load clock source
|
||||||
|
clkId = clocks.keyId("onboard");
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("clock")) {
|
||||||
|
std::string clkStr = config.conf["devices"][selectedSerial]["clock"];
|
||||||
|
if (clocks.keyExists(clkStr)) {
|
||||||
|
clkId = clocks.keyId(clkStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load gain mode
|
// Load gain mode
|
||||||
if (config.conf["devices"][selectedSerial].contains("gainMode")) {
|
if (config.conf["devices"][selectedSerial].contains("gainMode")) {
|
||||||
std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
|
std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
|
||||||
@ -364,6 +378,7 @@ private:
|
|||||||
if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
|
if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
|
||||||
|
|
||||||
// Setup device parameters
|
// Setup device parameters
|
||||||
|
_this->setClockSource(_this->clocks[_this->clkId]);
|
||||||
bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL);
|
bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL);
|
||||||
bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
|
bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
|
||||||
bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
|
bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
|
||||||
@ -486,6 +501,19 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Clock Source");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_balderf_clk_sel_", _this->name), &_this->clkId, _this->clocks.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
_this->setClockSource(_this->clocks[_this->clkId]);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerial != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["clock"] = _this->clocks.key(_this->clkId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// General config BS
|
// General config BS
|
||||||
SmGui::LeftLabel("Gain control mode");
|
SmGui::LeftLabel("Gain control mode");
|
||||||
SmGui::FillWidth();
|
SmGui::FillWidth();
|
||||||
@ -537,6 +565,15 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setClockSource(bladerf_clock_select clk) {
|
||||||
|
if (selectedBladeType == BLADERF_TYPE_V1) {
|
||||||
|
bladerf_set_smb_mode(openDev, (clk == CLOCK_SELECT_EXTERNAL) ? BLADERF_SMB_MODE_INPUT : BLADERF_SMB_MODE_DISABLED);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
bladerf_set_clock_select(openDev, clk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void worker() {
|
void worker() {
|
||||||
int16_t* buffer = new int16_t[bufferSize * 2];
|
int16_t* buffer = new int16_t[bufferSize * 2];
|
||||||
bladerf_metadata meta;
|
bladerf_metadata meta;
|
||||||
@ -565,6 +602,7 @@ private:
|
|||||||
int devId = 0;
|
int devId = 0;
|
||||||
int srId = 0;
|
int srId = 0;
|
||||||
int bwId = 0;
|
int bwId = 0;
|
||||||
|
int clkId = 0;
|
||||||
int chanId = 0;
|
int chanId = 0;
|
||||||
int gainMode = 0;
|
int gainMode = 0;
|
||||||
bool streamingEnabled = false;
|
bool streamingEnabled = false;
|
||||||
@ -580,8 +618,8 @@ private:
|
|||||||
std::string sampleRatesTxt;
|
std::string sampleRatesTxt;
|
||||||
std::vector<uint64_t> bandwidths;
|
std::vector<uint64_t> bandwidths;
|
||||||
std::string bandwidthsTxt;
|
std::string bandwidthsTxt;
|
||||||
|
|
||||||
std::string channelNamesTxt;
|
std::string channelNamesTxt;
|
||||||
|
OptionList<std::string, bladerf_clock_select> clocks;
|
||||||
|
|
||||||
int bufferSize;
|
int bufferSize;
|
||||||
struct bladerf_stream* rxStream;
|
struct bladerf_stream* rxStream;
|
||||||
|
21
source_modules/fobossdr_source/CMakeLists.txt
Normal file
21
source_modules/fobossdr_source/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(fobossdr_source)
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Lib path
|
||||||
|
target_link_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/lib/")
|
||||||
|
target_include_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/include/")
|
||||||
|
target_link_libraries(fobossdr_source PRIVATE fobos)
|
||||||
|
else (MSVC)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBFOBOS REQUIRED libfobos)
|
||||||
|
|
||||||
|
target_include_directories(fobossdr_source PRIVATE ${LIBFOBOS_INCLUDE_DIRS})
|
||||||
|
target_link_directories(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARIES})
|
||||||
|
endif ()
|
560
source_modules/fobossdr_source/src/main.cpp
Normal file
560
source_modules/fobossdr_source/src/main.cpp
Normal file
@ -0,0 +1,560 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/smgui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <utils/optionlist.h>
|
||||||
|
#include <atomic>
|
||||||
|
#include <fobos.h>
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "fobossdr_source",
|
||||||
|
/* Description: */ "FobosSDR Source Module",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigManager config;
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
// Work around for the fobos API not including
|
||||||
|
#define FOBOS_LNA_GAIN_MIN 1
|
||||||
|
#define FOBOS_LNA_GAIN_MAX 3
|
||||||
|
#define FOBOS_VGA_GAIN_MIN 0
|
||||||
|
#define FOBOS_VGA_GAIN_MAX 31
|
||||||
|
|
||||||
|
class FobosSDRSourceModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
FobosSDRSourceModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
sampleRate = 50000000.0;
|
||||||
|
|
||||||
|
// Initialize the DDC
|
||||||
|
ddc.init(&ddcIn, 50e6, 50e6, 50e6, 0.0);
|
||||||
|
|
||||||
|
handler.ctx = this;
|
||||||
|
handler.selectHandler = menuSelected;
|
||||||
|
handler.deselectHandler = menuDeselected;
|
||||||
|
handler.menuHandler = menuHandler;
|
||||||
|
handler.startHandler = start;
|
||||||
|
handler.stopHandler = stop;
|
||||||
|
handler.tuneHandler = tune;
|
||||||
|
handler.stream = &ddc.out;
|
||||||
|
|
||||||
|
// Refresh devices
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
// Select device from config
|
||||||
|
config.acquire();
|
||||||
|
std::string devSerial = config.conf["device"];
|
||||||
|
config.release();
|
||||||
|
select(devSerial);
|
||||||
|
|
||||||
|
sigpath::sourceManager.registerSource("FobosSDR", &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
~FobosSDRSourceModule() {
|
||||||
|
// Nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Port {
|
||||||
|
PORT_RF,
|
||||||
|
PORT_HF1,
|
||||||
|
PORT_HF2
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string getBandwdithScaled(double bw) {
|
||||||
|
char buf[1024];
|
||||||
|
if (bw >= 1000000.0) {
|
||||||
|
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
|
||||||
|
}
|
||||||
|
else if (bw >= 1000.0) {
|
||||||
|
sprintf(buf, "%.1lfKHz", bw / 1000.0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(buf, "%.1lfHz", bw);
|
||||||
|
}
|
||||||
|
return std::string(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
devices.clear();
|
||||||
|
|
||||||
|
// Get device list
|
||||||
|
char serials[1024];
|
||||||
|
memset(serials, 0, sizeof(serials));
|
||||||
|
int devCount = fobos_rx_list_devices(serials);
|
||||||
|
if (devCount < 0) {
|
||||||
|
flog::error("Failed to get device list: {}", devCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no device, give up
|
||||||
|
if (!devCount) { return; }
|
||||||
|
|
||||||
|
// Generate device entries
|
||||||
|
const char* _serials = serials;
|
||||||
|
int index = 0;
|
||||||
|
while (*_serials) {
|
||||||
|
// Read serial until space
|
||||||
|
std::string serial = "";
|
||||||
|
while (*_serials) {
|
||||||
|
// Get a character
|
||||||
|
char c = *(_serials++);
|
||||||
|
|
||||||
|
// If it's a space, we're done
|
||||||
|
if (c == ' ') { break; }
|
||||||
|
|
||||||
|
// Otherwise, add it to the string
|
||||||
|
serial += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create entry
|
||||||
|
devices.define(serial, serial, index++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void select(const std::string& serial) {
|
||||||
|
// If there are no devices, give up
|
||||||
|
if (devices.empty()) {
|
||||||
|
selectedSerial.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the serial was not found, select the first available serial
|
||||||
|
if (!devices.keyExists(serial)) {
|
||||||
|
select(devices.key(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the ID in the list
|
||||||
|
int id = devices.keyId(serial);
|
||||||
|
selectedDevId = devices[id];
|
||||||
|
|
||||||
|
// Open the device
|
||||||
|
fobos_dev_t* dev;
|
||||||
|
int err = fobos_rx_open(&dev, selectedDevId);
|
||||||
|
if (err) {
|
||||||
|
flog::error("Failed to open device: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of supported samplerates
|
||||||
|
double srList[128];
|
||||||
|
unsigned int srCount;
|
||||||
|
err = fobos_rx_get_samplerates(dev, srList, &srCount);
|
||||||
|
if (err) {
|
||||||
|
flog::error("Failed to get samplerate list: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate samplerate list
|
||||||
|
samplerates.clear();
|
||||||
|
for (int i = 0; i < srCount; i++) {
|
||||||
|
std::string str = getBandwdithScaled(srList[i]);
|
||||||
|
samplerates.define(srList[i], str, srList[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some custom samplerates
|
||||||
|
samplerates.define(5e6, "5.0MHz", 5e6);
|
||||||
|
samplerates.define(2.5e6, "2.5MHz", 2.5e6);
|
||||||
|
samplerates.define(1.25e6, "1.25MHz", 1.25e6);
|
||||||
|
|
||||||
|
// Define the ports
|
||||||
|
ports.clear();
|
||||||
|
ports.define("rf", "RF", PORT_RF);
|
||||||
|
ports.define("hf1", "HF1", PORT_HF1);
|
||||||
|
ports.define("hf2", "HF2", PORT_HF2);
|
||||||
|
|
||||||
|
// Define clock sources
|
||||||
|
clockSources.clear();
|
||||||
|
clockSources.define("internal", "Internal", 0);
|
||||||
|
clockSources.define("external", "External", 1);
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
fobos_rx_close(dev);
|
||||||
|
|
||||||
|
// Save serial number
|
||||||
|
selectedSerial = serial;
|
||||||
|
devId = id;
|
||||||
|
|
||||||
|
// Load default options
|
||||||
|
sampleRate = 50e6;
|
||||||
|
srId = samplerates.valueId(sampleRate);
|
||||||
|
port = PORT_RF;
|
||||||
|
portId = ports.valueId(port);
|
||||||
|
clkSrcId = clockSources.nameId("Internal");
|
||||||
|
lnaGain = 0;
|
||||||
|
vgaGain = 0;
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
config.acquire();
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("samplerate")) {
|
||||||
|
int desiredSr = config.conf["devices"][selectedSerial]["samplerate"];
|
||||||
|
if (samplerates.keyExists(desiredSr)) {
|
||||||
|
srId = samplerates.keyId(desiredSr);
|
||||||
|
sampleRate = samplerates[srId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("port")) {
|
||||||
|
std::string desiredPort = config.conf["devices"][selectedSerial]["port"];
|
||||||
|
if (ports.keyExists(desiredPort)) {
|
||||||
|
portId = ports.keyId(desiredPort);
|
||||||
|
port = ports[portId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("clkSrc")) {
|
||||||
|
std::string desiredClkSrc = config.conf["devices"][selectedSerial]["clkSrc"];
|
||||||
|
if (clockSources.keyExists(desiredClkSrc)) {
|
||||||
|
clkSrcId = clockSources.keyId(desiredClkSrc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("lnaGain")) {
|
||||||
|
lnaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["lnaGain"], FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX);
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerial].contains("vgaGain")) {
|
||||||
|
vgaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["vgaGain"], FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX);
|
||||||
|
}
|
||||||
|
config.release();
|
||||||
|
|
||||||
|
// Update the samplerate
|
||||||
|
core::setInputSampleRate(sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuSelected(void* ctx) {
|
||||||
|
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
flog::info("FobosSDRSourceModule '{0}': Menu Select!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuDeselected(void* ctx) {
|
||||||
|
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||||
|
flog::info("FobosSDRSourceModule '{0}': Menu Deselect!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(void* ctx) {
|
||||||
|
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||||
|
if (_this->running) { return; }
|
||||||
|
|
||||||
|
// Open the device
|
||||||
|
int err = fobos_rx_open(&_this->openDev, _this->selectedDevId);
|
||||||
|
if (err) {
|
||||||
|
flog::error("Failed to open device: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the selected port
|
||||||
|
_this->port = _this->ports[_this->portId];
|
||||||
|
|
||||||
|
// Configure the device
|
||||||
|
double actualSr, actualFreq;
|
||||||
|
fobos_rx_set_samplerate(_this->openDev, (_this->sampleRate >= 50e6) ? _this->sampleRate : 50e6, &actualSr);
|
||||||
|
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
|
||||||
|
fobos_rx_set_direct_sampling(_this->openDev, _this->port != PORT_RF);
|
||||||
|
fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
|
||||||
|
fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||||
|
|
||||||
|
// Configure the DDC
|
||||||
|
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
|
||||||
|
// Set the frequency
|
||||||
|
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
|
||||||
|
}
|
||||||
|
else if (_this->port == PORT_RF) {
|
||||||
|
// Set the frequency
|
||||||
|
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
|
||||||
|
|
||||||
|
// Configure and start the DDC for decimation only
|
||||||
|
_this->ddc.setInSamplerate(actualSr);
|
||||||
|
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
|
||||||
|
_this->ddc.setOffset(0.0);
|
||||||
|
_this->ddc.start();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Configure and start the DDC
|
||||||
|
_this->ddc.setInSamplerate(actualSr);
|
||||||
|
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
|
||||||
|
_this->ddc.setOffset(_this->freq);
|
||||||
|
_this->ddc.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute buffer size (Lower than usual, but it's a workaround for their API having broken streaming)
|
||||||
|
_this->bufferSize = _this->sampleRate / 400.0;
|
||||||
|
|
||||||
|
// Start streaming
|
||||||
|
err = fobos_rx_start_sync(_this->openDev, _this->bufferSize);
|
||||||
|
if (err) {
|
||||||
|
flog::error("Failed to start stream: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start worker
|
||||||
|
_this->run = true;
|
||||||
|
_this->workerThread = std::thread(&FobosSDRSourceModule::worker, _this);
|
||||||
|
|
||||||
|
_this->running = true;
|
||||||
|
flog::info("FobosSDRSourceModule '{0}': Start!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop(void* ctx) {
|
||||||
|
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||||
|
if (!_this->running) { return; }
|
||||||
|
_this->running = false;
|
||||||
|
|
||||||
|
// Stop worker
|
||||||
|
_this->run = false;
|
||||||
|
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
|
||||||
|
_this->ddc.out.stopWriter();
|
||||||
|
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||||
|
_this->ddc.out.clearWriteStop();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_this->ddcIn.stopWriter();
|
||||||
|
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||||
|
_this->ddcIn.clearWriteStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop streaming
|
||||||
|
fobos_rx_stop_sync(_this->openDev);
|
||||||
|
|
||||||
|
// Stop the DDC
|
||||||
|
_this->ddc.stop();
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
fobos_rx_close(_this->openDev);
|
||||||
|
|
||||||
|
flog::info("FobosSDRSourceModule '{0}': Stop!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune(double freq, void* ctx) {
|
||||||
|
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||||
|
if (_this->running) {
|
||||||
|
if (_this->port == PORT_RF) {
|
||||||
|
double actual; // Dummy, don't care
|
||||||
|
fobos_rx_set_frequency(_this->openDev, freq, &actual);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_this->ddc.setOffset(freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_this->freq = freq;
|
||||||
|
flog::info("FobosSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::BeginDisabled(); }
|
||||||
|
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Combo(CONCAT("##_fobossdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
||||||
|
_this->select(_this->devices.key(_this->devId));
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
config.acquire();
|
||||||
|
config.conf["device"] = _this->selectedSerial;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SmGui::Combo(CONCAT("##_fobossdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
|
||||||
|
_this->sampleRate = _this->samplerates.value(_this->srId);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
if (!_this->selectedSerial.empty()) {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->samplerates.key(_this->srId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::SameLine();
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Button(CONCAT("Refresh##_fobossdr_refr_", _this->name))) {
|
||||||
|
_this->refresh();
|
||||||
|
_this->select(_this->selectedSerial);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Antenna Port");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_fobossdr_port_", _this->name), &_this->portId, _this->ports.txt)) {
|
||||||
|
if (!_this->selectedSerial.empty()) {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["port"] = _this->ports.key(_this->portId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Clock Source");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_fobossdr_clk_", _this->name), &_this->clkSrcId, _this->clockSources.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
|
||||||
|
}
|
||||||
|
if (!_this->selectedSerial.empty()) {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["clkSrc"] = _this->clockSources.key(_this->clkSrcId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->port == PORT_RF) {
|
||||||
|
SmGui::LeftLabel("LNA Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_fobossdr_lna_gain_", _this->name), &_this->lnaGain, FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX)) {
|
||||||
|
if (_this->running) {
|
||||||
|
fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
if (!_this->selectedSerial.empty()) {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["lnaGain"] = _this->lnaGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("VGA Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_fobossdr_vga_gain_", _this->name), &_this->vgaGain, FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX)) {
|
||||||
|
if (_this->running) {
|
||||||
|
fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||||
|
}
|
||||||
|
if (!_this->selectedSerial.empty()) {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerial]["vgaGain"] = _this->vgaGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker() {
|
||||||
|
// Select different processing depending on the mode
|
||||||
|
if (port == PORT_RF && sampleRate >= 50e6) {
|
||||||
|
while (run) {
|
||||||
|
// Read samples
|
||||||
|
unsigned int sampCount = 0;
|
||||||
|
int err = fobos_rx_read_sync(openDev, (float*)ddc.out.writeBuf, &sampCount);
|
||||||
|
if (err) { break; }
|
||||||
|
|
||||||
|
// Send out samples to the core
|
||||||
|
if (!ddc.out.swap(sampCount)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (port == PORT_RF) {
|
||||||
|
while (run) {
|
||||||
|
// Read samples
|
||||||
|
unsigned int sampCount = 0;
|
||||||
|
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
|
||||||
|
if (err) { break; }
|
||||||
|
|
||||||
|
// Send samples to the DDC
|
||||||
|
if (!ddcIn.swap(sampCount)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (port == PORT_HF1) {
|
||||||
|
while (run) {
|
||||||
|
// Read samples
|
||||||
|
unsigned int sampCount = 0;
|
||||||
|
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
|
||||||
|
if (err) { break; }
|
||||||
|
|
||||||
|
// Null out the HF2 samples
|
||||||
|
for (int i = 0; i < sampCount; i++) {
|
||||||
|
ddcIn.writeBuf[i].im = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send samples to the DDC
|
||||||
|
if (!ddcIn.swap(sampCount)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (port == PORT_HF2) {
|
||||||
|
while (run) {
|
||||||
|
// Read samples
|
||||||
|
unsigned int sampCount = 0;
|
||||||
|
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
|
||||||
|
if (err) { break; }
|
||||||
|
|
||||||
|
// Null out the HF2 samples
|
||||||
|
for (int i = 0; i < sampCount; i++) {
|
||||||
|
ddcIn.writeBuf[i].re = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send samples to the DDC
|
||||||
|
if (!ddcIn.swap(sampCount)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
double sampleRate;
|
||||||
|
SourceManager::SourceHandler handler;
|
||||||
|
bool running = false;
|
||||||
|
double freq;
|
||||||
|
|
||||||
|
OptionList<std::string, int> devices;
|
||||||
|
OptionList<int, double> samplerates;
|
||||||
|
OptionList<std::string, Port> ports;
|
||||||
|
OptionList<std::string, int> clockSources;
|
||||||
|
int devId = 0;
|
||||||
|
int srId = 0;
|
||||||
|
int portId = 0;
|
||||||
|
int clkSrcId = 0;
|
||||||
|
Port port;
|
||||||
|
int lnaGain = 0;
|
||||||
|
int vgaGain = 0;
|
||||||
|
std::string selectedSerial;
|
||||||
|
int selectedDevId;
|
||||||
|
|
||||||
|
fobos_dev_t* openDev;
|
||||||
|
|
||||||
|
int bufferSize;
|
||||||
|
std::thread workerThread;
|
||||||
|
std::atomic<bool> run = false;
|
||||||
|
|
||||||
|
dsp::stream<dsp::complex_t> ddcIn;
|
||||||
|
dsp::channel::RxVFO ddc;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
json def = json({});
|
||||||
|
def["devices"] = json({});
|
||||||
|
def["device"] = "";
|
||||||
|
config.setPath(core::args["root"].s() + "/fobossdr_config.json");
|
||||||
|
config.load(def);
|
||||||
|
config.enableAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new FobosSDRSourceModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (FobosSDRSourceModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
config.disableAutoSave();
|
||||||
|
config.save();
|
||||||
|
}
|
1
source_modules/kcsdr_source/.gitignore
vendored
Normal file
1
source_modules/kcsdr_source/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
vendor/*
|
10
source_modules/kcsdr_source/CMakeLists.txt
Normal file
10
source_modules/kcsdr_source/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(kcsdr_source)
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp" "src/*.c")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
target_link_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10/x64/DLL")
|
||||||
|
target_include_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10")
|
||||||
|
target_link_libraries(kcsdr_source PRIVATE FTD3XX)
|
209
source_modules/kcsdr_source/src/kcsdr.c
Normal file
209
source_modules/kcsdr_source/src/kcsdr.c
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
#include "kcsdr.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include "../vendor/FTD3XXLibrary_1.3.0.10/FTD3XX.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define KCSDR_PKT_EMPTY_LEN 0x0C
|
||||||
|
|
||||||
|
#define KCSDR_COMMAND_PIPE 0x02
|
||||||
|
#define KCSDR_RX_DATA_PIPE 0x83
|
||||||
|
#define KCSDR_TX_DATA_PIPE 0x03
|
||||||
|
|
||||||
|
struct kcsdr {
|
||||||
|
FT_HANDLE ft;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct kcsdr_packet {
|
||||||
|
uint8_t zeros0[4];
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t zeros1[2];
|
||||||
|
uint8_t hex_eighty;
|
||||||
|
uint32_t command;
|
||||||
|
uint8_t data[188];
|
||||||
|
};
|
||||||
|
typedef struct kcsdr_packet kcsdr_packet_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
enum kcsdr_command {
|
||||||
|
CMD_NOT_USED_0x00 = 0x00,
|
||||||
|
CMD_SET_PORT = 0x01,
|
||||||
|
CMD_SET_FREQUENCY = 0x02,
|
||||||
|
CMD_SET_ATTENUATION = 0x03,
|
||||||
|
CMD_SET_AMPLIFIER = 0x04,
|
||||||
|
CMD_SET_BANDWIDTH = 0x05,
|
||||||
|
CMD_START = 0x06,
|
||||||
|
CMD_STOP = 0x07,
|
||||||
|
CMD_SET_EXT_AMP = 0x08,
|
||||||
|
CMD_START_REMOTE = 0x09,
|
||||||
|
CMD_STOP_REMOTE = 0x0A
|
||||||
|
};
|
||||||
|
typedef enum kcsdr_command kcsdr_command_t;
|
||||||
|
|
||||||
|
int kcsdr_send_command(kcsdr_t* dev, kcsdr_direction_t dir, kcsdr_command_t cmd, const uint8_t* data, int len) {
|
||||||
|
Sleep(50);
|
||||||
|
|
||||||
|
// Create an empty packet
|
||||||
|
kcsdr_packet_t pkt;
|
||||||
|
memset(&pkt, 0, sizeof(kcsdr_packet_t));
|
||||||
|
|
||||||
|
// Fill out the packet info
|
||||||
|
pkt.length = len + KCSDR_PKT_EMPTY_LEN;
|
||||||
|
pkt.hex_eighty = 0x80; // Whatever the fuck that is
|
||||||
|
pkt.command = (uint32_t)cmd | (uint32_t)dir;
|
||||||
|
|
||||||
|
// Copy the data if there is some
|
||||||
|
if (len) { memcpy(pkt.data, data, len); }
|
||||||
|
|
||||||
|
// Dump the bytes
|
||||||
|
uint8_t* dump = (uint8_t*)&pkt;
|
||||||
|
printf("Sending:");
|
||||||
|
for (int i = 0; i < pkt.length; i++) {
|
||||||
|
printf(" %02X", dump[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
// Send the command to endpoint 0
|
||||||
|
int sent;
|
||||||
|
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_COMMAND_PIPE, (uint8_t*)&pkt, sizeof(kcsdr_packet_t), &sent, NULL);
|
||||||
|
if (err != FT_OK) {
|
||||||
|
return -err;
|
||||||
|
}
|
||||||
|
printf("Sent %d bytes (%d)\n", sent, err);
|
||||||
|
|
||||||
|
Sleep(50);
|
||||||
|
|
||||||
|
// Flush existing commands
|
||||||
|
FT_FlushPipe(dev->ft, KCSDR_COMMAND_PIPE);
|
||||||
|
|
||||||
|
return -(int)err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_list_devices(kcsdr_info_t** devices) {
|
||||||
|
// Generate a list of FTDI devices
|
||||||
|
int ftdiDevCount = 0;
|
||||||
|
FT_STATUS err = FT_CreateDeviceInfoList(&ftdiDevCount);
|
||||||
|
if (err != FT_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no device was found, return nothing
|
||||||
|
if (!ftdiDevCount) {
|
||||||
|
*devices = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get said device list
|
||||||
|
FT_DEVICE_LIST_INFO_NODE* list = malloc(ftdiDevCount * sizeof(FT_DEVICE_LIST_INFO_NODE));
|
||||||
|
err = FT_GetDeviceInfoList(list, &ftdiDevCount);
|
||||||
|
if (err != FT_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate the device info list
|
||||||
|
*devices = malloc(ftdiDevCount * sizeof(kcsdr_info_t));
|
||||||
|
|
||||||
|
// Find all KC908s
|
||||||
|
int kcCount = 0;
|
||||||
|
for (int i = 0; i < ftdiDevCount; i++) {
|
||||||
|
strcpy((*devices)[kcCount++].serial, list[i].SerialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the FTDI list
|
||||||
|
free(list);
|
||||||
|
|
||||||
|
return kcCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void kcsdr_free_device_list(kcsdr_info_t* devices) {
|
||||||
|
// Free the list
|
||||||
|
if (devices) { free(devices); }
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_open(kcsdr_t** dev, const char* serial) {
|
||||||
|
// Attempt to open the device using the serial number
|
||||||
|
FT_HANDLE ft;
|
||||||
|
FT_STATUS err = FT_Create(serial, FT_OPEN_BY_SERIAL_NUMBER, &ft);
|
||||||
|
if (err != FT_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the timeouts for the data pipes
|
||||||
|
FT_SetPipeTimeout(ft, KCSDR_RX_DATA_PIPE, 1000);
|
||||||
|
FT_SetPipeTimeout(ft, KCSDR_TX_DATA_PIPE, 1000);
|
||||||
|
|
||||||
|
// Allocate the device object
|
||||||
|
*dev = malloc(sizeof(kcsdr_t));
|
||||||
|
|
||||||
|
// Fill out the device object
|
||||||
|
(*dev)->ft = ft;
|
||||||
|
|
||||||
|
// Put device into remote control mode
|
||||||
|
return kcsdr_send_command(*dev, KCSDR_DIR_RX, CMD_START_REMOTE, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kcsdr_close(kcsdr_t* dev) {
|
||||||
|
// Put device back in normal mode
|
||||||
|
kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_STOP_REMOTE, NULL, 0);
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
FT_Close(dev->ft);
|
||||||
|
|
||||||
|
// Free the device object
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port) {
|
||||||
|
// Send SET_PORT command
|
||||||
|
return kcsdr_send_command(dev, dir, CMD_SET_PORT, &port, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq) {
|
||||||
|
// Send SET_FREQUENCY command
|
||||||
|
return kcsdr_send_command(dev, dir, CMD_SET_FREQUENCY, (uint8_t*)&freq, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att) {
|
||||||
|
// Send SET_ATTENUATION command
|
||||||
|
return kcsdr_send_command(dev, dir, CMD_SET_ATTENUATION, &att, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain) {
|
||||||
|
// Send SET_AMPLIFIER command
|
||||||
|
return kcsdr_send_command(dev, dir, CMD_SET_AMPLIFIER, &gain, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain) {
|
||||||
|
// Send CMD_SET_EXT_AMP command
|
||||||
|
return kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_SET_EXT_AMP, &gain, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate) {
|
||||||
|
// Set SET_BANDWIDTH command
|
||||||
|
return kcsdr_send_command(dev, dir, CMD_SET_BANDWIDTH, (uint8_t*)&samplerate, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir) {
|
||||||
|
// Send START command
|
||||||
|
return kcsdr_send_command(dev, dir, CMD_START, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir) {
|
||||||
|
// Send STOP command
|
||||||
|
return kcsdr_send_command(dev, dir, CMD_STOP, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count) {
|
||||||
|
// Receive samples (TODO: Endpoint might be 0x81)
|
||||||
|
int received;
|
||||||
|
FT_STATUS err = FT_ReadPipeEx(dev->ft, KCSDR_RX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &received, NULL);
|
||||||
|
return (err == FT_OK) ? received / (2*sizeof(uint16_t)) : -(int)err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count) {
|
||||||
|
// Transmit samples
|
||||||
|
int sent;
|
||||||
|
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_TX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &sent, NULL);
|
||||||
|
return (err == FT_OK) ? sent / (2*sizeof(uint16_t)) : -(int)err;
|
||||||
|
}
|
150
source_modules/kcsdr_source/src/kcsdr.h
Normal file
150
source_modules/kcsdr_source/src/kcsdr.h
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define KCSDR_SERIAL_LEN 16
|
||||||
|
#define KCSDR_MAX_PORTS 6
|
||||||
|
|
||||||
|
// Detect C++
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KCSDR Device.
|
||||||
|
*/
|
||||||
|
struct kcsdr;
|
||||||
|
typedef struct kcsdr kcsdr_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device Information
|
||||||
|
*/
|
||||||
|
struct kcsdr_info {
|
||||||
|
char serial[KCSDR_SERIAL_LEN+1];
|
||||||
|
};
|
||||||
|
typedef struct kcsdr_info kcsdr_info_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RF Direction.
|
||||||
|
*/
|
||||||
|
enum kcsdr_direction {
|
||||||
|
KCSDR_DIR_RX = 0x00,
|
||||||
|
KCSDR_DIR_TX = 0x80
|
||||||
|
};
|
||||||
|
typedef enum kcsdr_direction kcsdr_direction_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of KCSDR devices on the system.
|
||||||
|
* @param devices Pointer to an array of device info.
|
||||||
|
* @return Number of devices found or error code.
|
||||||
|
*/
|
||||||
|
int kcsdr_list_devices(kcsdr_info_t** devices);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a device list returned by `kcsdr_list_devices()`.
|
||||||
|
* @param devices Device list to free.
|
||||||
|
*/
|
||||||
|
void kcsdr_free_device_list(kcsdr_info_t* devices);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a KCSDR device.
|
||||||
|
* @param dev Newly open device.
|
||||||
|
* @param serial Serial number of the device to open as returned in the device list.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_open(kcsdr_t** dev, const char* serial);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close a KCSDR device.
|
||||||
|
* @param dev Device to be closed.
|
||||||
|
*/
|
||||||
|
void kcsdr_close(kcsdr_t* dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the RF port.
|
||||||
|
* @param dev Device to control.
|
||||||
|
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
|
||||||
|
* @param port RF port number to select.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the center frequency.
|
||||||
|
* @param dev Device to control.
|
||||||
|
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
|
||||||
|
* @param freq Frequency in Hz.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the attenuation.
|
||||||
|
* @param dev Device to control.
|
||||||
|
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
|
||||||
|
* @param samplerate Attenuation in dB.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the internal amplifier gain.
|
||||||
|
* @param dev Device to control.
|
||||||
|
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
|
||||||
|
* @param gain Gain in dB.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the external amplifier gain.
|
||||||
|
* @param dev Device to control.
|
||||||
|
* @param gain Gain in dB.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the samplerate.
|
||||||
|
* @param dev Device to control.
|
||||||
|
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
|
||||||
|
* @param samplerate Samplerate in Hz.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start streaming samples.
|
||||||
|
* @param dev Device to control.
|
||||||
|
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop streaming samples.
|
||||||
|
* @param dev Device to control.
|
||||||
|
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
|
||||||
|
* @return 0 on success, error code otherwise.
|
||||||
|
*/
|
||||||
|
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a buffer of samples.
|
||||||
|
* @param samples Sample buffer.
|
||||||
|
* @param count Number of complex samples.
|
||||||
|
* @return Number of samples received.
|
||||||
|
*/
|
||||||
|
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transmit a buffer of samples.
|
||||||
|
* @param samples Sample buffer.
|
||||||
|
* @param count Number of complex samples.
|
||||||
|
* @return Number of samples transmitted.
|
||||||
|
*/
|
||||||
|
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count);
|
||||||
|
|
||||||
|
// Detect C++
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
324
source_modules/kcsdr_source/src/main.cpp
Normal file
324
source_modules/kcsdr_source/src/main.cpp
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/smgui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <utils/optionlist.h>
|
||||||
|
#include "kcsdr.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "kcsdr_source",
|
||||||
|
/* Description: */ "KCSDR Source Module",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
class KCSDRSourceModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
KCSDRSourceModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
sampleRate = 2000000.0;
|
||||||
|
samplerates.define(40e6, "40MHz", 40e6);
|
||||||
|
samplerates.define(35e6, "35MHz", 35e6);
|
||||||
|
samplerates.define(30e6, "30MHz", 30e6);
|
||||||
|
samplerates.define(25e6, "25MHz", 25e6);
|
||||||
|
samplerates.define(20e6, "20MHz", 20e6);
|
||||||
|
samplerates.define(15e6, "15MHz", 15e6);
|
||||||
|
samplerates.define(10e6, "10MHz", 10e6);
|
||||||
|
samplerates.define(5e6, "5MHz", 5e6);
|
||||||
|
|
||||||
|
handler.ctx = this;
|
||||||
|
handler.selectHandler = menuSelected;
|
||||||
|
handler.deselectHandler = menuDeselected;
|
||||||
|
handler.menuHandler = menuHandler;
|
||||||
|
handler.startHandler = start;
|
||||||
|
handler.stopHandler = stop;
|
||||||
|
handler.tuneHandler = tune;
|
||||||
|
handler.stream = &stream;
|
||||||
|
|
||||||
|
// Refresh devices
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
// Select first (TODO: Select from config)
|
||||||
|
select("");
|
||||||
|
|
||||||
|
sigpath::sourceManager.registerSource("KCSDR", &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
~KCSDRSourceModule() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refresh() {
|
||||||
|
devices.clear();
|
||||||
|
|
||||||
|
// Get device list
|
||||||
|
kcsdr_info_t* list;
|
||||||
|
int count = kcsdr_list_devices(&list);
|
||||||
|
if (count < 0) {
|
||||||
|
flog::error("Failed to list devices: {}", count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create list
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
devices.define(list[i].serial, list[i].serial, list[i].serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the device list
|
||||||
|
kcsdr_free_device_list(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void select(const std::string& serial) {
|
||||||
|
// If there are no devices, give up
|
||||||
|
if (devices.empty()) {
|
||||||
|
selectedSerial.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the serial was not found, select the first available serial
|
||||||
|
if (!devices.keyExists(serial)) {
|
||||||
|
select(devices.key(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the menu ID
|
||||||
|
devId = devices.keyId(serial);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Update the samplerate
|
||||||
|
core::setInputSampleRate(sampleRate);
|
||||||
|
|
||||||
|
// Save serial number
|
||||||
|
selectedSerial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuSelected(void* ctx) {
|
||||||
|
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
flog::info("KCSDRSourceModule '{0}': Menu Select!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuDeselected(void* ctx) {
|
||||||
|
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
||||||
|
flog::info("KCSDRSourceModule '{0}': Menu Deselect!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(void* ctx) {
|
||||||
|
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
||||||
|
if (_this->running) { return; }
|
||||||
|
|
||||||
|
// If no serial is given, do nothing
|
||||||
|
if (_this->selectedSerial.empty()) { return; }
|
||||||
|
|
||||||
|
// Open the device
|
||||||
|
int err = kcsdr_open(&_this->openDev, _this->selectedSerial.c_str());
|
||||||
|
if (err) {
|
||||||
|
flog::error("Failed to open device: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the device
|
||||||
|
kcsdr_set_port(_this->openDev, KCSDR_DIR_RX, 0);
|
||||||
|
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, _this->freq);
|
||||||
|
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
|
||||||
|
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
|
||||||
|
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
|
||||||
|
kcsdr_set_samplerate(_this->openDev, KCSDR_DIR_RX, _this->sampleRate);
|
||||||
|
|
||||||
|
// Start the stream
|
||||||
|
kcsdr_start(_this->openDev, KCSDR_DIR_RX);
|
||||||
|
|
||||||
|
// Start worker
|
||||||
|
_this->run = true;
|
||||||
|
_this->workerThread = std::thread(&KCSDRSourceModule::worker, _this);
|
||||||
|
|
||||||
|
_this->running = true;
|
||||||
|
flog::info("KCSDRSourceModule '{0}': Start!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop(void* ctx) {
|
||||||
|
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
||||||
|
if (!_this->running) { return; }
|
||||||
|
_this->running = false;
|
||||||
|
|
||||||
|
// Stop worker
|
||||||
|
_this->run = false;
|
||||||
|
_this->stream.stopWriter();
|
||||||
|
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||||
|
_this->stream.clearWriteStop();
|
||||||
|
|
||||||
|
// Stop streaming
|
||||||
|
kcsdr_stop(_this->openDev, KCSDR_DIR_RX);
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
kcsdr_close(_this->openDev);
|
||||||
|
|
||||||
|
flog::info("KCSDRSourceModule '{0}': Stop!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune(double freq, void* ctx) {
|
||||||
|
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
||||||
|
if (_this->running) {
|
||||||
|
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, freq);
|
||||||
|
}
|
||||||
|
_this->freq = freq;
|
||||||
|
flog::info("KCSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::BeginDisabled(); }
|
||||||
|
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Combo(CONCAT("##_kcsdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
||||||
|
_this->select(_this->devices.key(_this->devId));
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SmGui::Combo(CONCAT("##_kcsdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
|
||||||
|
_this->sampleRate = _this->samplerates.value(_this->srId);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::SameLine();
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Button(CONCAT("Refresh##_kcsdr_refr_", _this->name))) {
|
||||||
|
_this->refresh();
|
||||||
|
_this->select(_this->selectedSerial);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
// SmGui::LeftLabel("RX Port");
|
||||||
|
// SmGui::FillWidth();
|
||||||
|
// if (SmGui::Combo(CONCAT("##_kcsdr_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
|
||||||
|
// if (_this->running) {
|
||||||
|
// // TODO
|
||||||
|
// }
|
||||||
|
// // TODO: Save
|
||||||
|
// }
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Attenuation");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_kcsdr_att_", _this->name), &_this->att, 0, 31)) {
|
||||||
|
if (_this->running) {
|
||||||
|
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_kcsdr_gain_", _this->name), &_this->gain, 0, 31)) {
|
||||||
|
if (_this->running) {
|
||||||
|
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("External Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_kcsdr_ext_gain_", _this->name), &_this->extGain, 0, 31)) {
|
||||||
|
if (_this->running) {
|
||||||
|
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker() {
|
||||||
|
// Compute the buffer size
|
||||||
|
int bufferSize = 0x4000/4;//sampleRate / 200;
|
||||||
|
|
||||||
|
// Allocate the sample buffer
|
||||||
|
int16_t* samps = dsp::buffer::alloc<int16_t>(bufferSize*2);
|
||||||
|
|
||||||
|
// Loop
|
||||||
|
while (run) {
|
||||||
|
// Read samples
|
||||||
|
int count = kcsdr_rx(openDev, samps, bufferSize);
|
||||||
|
if (!count) { continue; }
|
||||||
|
if (count < 0) {
|
||||||
|
flog::debug("Failed to read samples: {}", count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the samples to float
|
||||||
|
volk_16i_s32f_convert_32f((float*)stream.writeBuf, samps, 8192.0f, count*2);
|
||||||
|
|
||||||
|
// Send out the samples
|
||||||
|
if (!stream.swap(count)) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the sample buffer
|
||||||
|
dsp::buffer::free(samps);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
dsp::stream<dsp::complex_t> stream;
|
||||||
|
double sampleRate;
|
||||||
|
SourceManager::SourceHandler handler;
|
||||||
|
bool running = false;
|
||||||
|
double freq;
|
||||||
|
|
||||||
|
OptionList<std::string, std::string> devices;
|
||||||
|
OptionList<int, double> samplerates;
|
||||||
|
int devId = 0;
|
||||||
|
int srId = 0;
|
||||||
|
int att = 0;
|
||||||
|
int gain = 30;
|
||||||
|
int extGain = 1;
|
||||||
|
int portId = 0;
|
||||||
|
std::string selectedSerial;
|
||||||
|
|
||||||
|
kcsdr_t* openDev;
|
||||||
|
|
||||||
|
std::thread workerThread;
|
||||||
|
std::atomic<bool> run = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new KCSDRSourceModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (KCSDRSourceModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
// Nothing here
|
||||||
|
}
|
@ -36,10 +36,10 @@ enum SampleType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const size_t SAMPLE_TYPE_SIZE[] {
|
const size_t SAMPLE_TYPE_SIZE[] {
|
||||||
sizeof(int8_t)*2,
|
2*sizeof(int8_t),
|
||||||
sizeof(int16_t)*2,
|
2*sizeof(int16_t),
|
||||||
sizeof(int32_t)*2,
|
2*sizeof(int32_t),
|
||||||
sizeof(float)*2,
|
2*sizeof(float),
|
||||||
};
|
};
|
||||||
|
|
||||||
class NetworkSourceModule : public ModuleManager::Instance {
|
class NetworkSourceModule : public ModuleManager::Instance {
|
||||||
@ -58,20 +58,6 @@ public:
|
|||||||
handler.tuneHandler = tune;
|
handler.tuneHandler = tune;
|
||||||
handler.stream = &stream;
|
handler.stream = &stream;
|
||||||
|
|
||||||
// Define samplerates
|
|
||||||
for (int i = 3000; i <= 192000; i <<= 1) {
|
|
||||||
samplerates.define(i, getSrScaled(i), i);
|
|
||||||
}
|
|
||||||
for (int i = 250000; i < 1000000; i += 250000) {
|
|
||||||
samplerates.define(i, getSrScaled(i), i);
|
|
||||||
}
|
|
||||||
for (int i = 1000000; i < 10000000; i += 500000) {
|
|
||||||
samplerates.define(i, getSrScaled(i), i);
|
|
||||||
}
|
|
||||||
for (int i = 10000000; i <= 100000000; i += 5000000) {
|
|
||||||
samplerates.define(i, getSrScaled(i), i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define protocols
|
// Define protocols
|
||||||
// protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
|
// protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
|
||||||
protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
|
protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
|
||||||
@ -86,8 +72,8 @@ public:
|
|||||||
// Load config
|
// Load config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
if (config.conf[name].contains("samplerate")) {
|
if (config.conf[name].contains("samplerate")) {
|
||||||
int sr = config.conf[name]["samplerate"];
|
samplerate = config.conf[name]["samplerate"];
|
||||||
if (samplerates.keyExists(sr)) { samplerate = samplerates.value(samplerates.keyId(sr)); }
|
tempSamplerate = samplerate;
|
||||||
}
|
}
|
||||||
if (config.conf[name].contains("protocol")) {
|
if (config.conf[name].contains("protocol")) {
|
||||||
std::string protoStr = config.conf[name]["protocol"];
|
std::string protoStr = config.conf[name]["protocol"];
|
||||||
@ -108,7 +94,6 @@ public:
|
|||||||
config.release();
|
config.release();
|
||||||
|
|
||||||
// Set menu IDs
|
// Set menu IDs
|
||||||
srId = samplerates.valueId(samplerate);
|
|
||||||
protoId = protocols.valueId(proto);
|
protoId = protocols.valueId(proto);
|
||||||
sampTypeId = sampleTypes.valueId(sampType);
|
sampTypeId = sampleTypes.valueId(sampType);
|
||||||
|
|
||||||
@ -228,35 +213,24 @@ private:
|
|||||||
if (_this->running) { SmGui::BeginDisabled(); }
|
if (_this->running) { SmGui::BeginDisabled(); }
|
||||||
|
|
||||||
// Hostname and port field
|
// Hostname and port field
|
||||||
if (ImGui::InputText(("##iq_exporter_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
|
if (SmGui::InputText(("##network_source_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["host"] = _this->hostname;
|
config.conf[_this->name]["host"] = _this->hostname;
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
SmGui::SameLine();
|
||||||
ImGui::FillWidth();
|
SmGui::FillWidth();
|
||||||
if (ImGui::InputInt(("##iq_exporter_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
|
if (SmGui::InputInt(("##network_source_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
|
||||||
_this->port = std::clamp<int>(_this->port, 1, 65535);
|
_this->port = std::clamp<int>(_this->port, 1, 65535);
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["port"] = _this->port;
|
config.conf[_this->name]["port"] = _this->port;
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Samplerate selector
|
|
||||||
ImGui::LeftLabel("Samplerate");
|
|
||||||
ImGui::FillWidth();
|
|
||||||
if (ImGui::Combo(("##iq_exporter_sr_" + _this->name).c_str(), &_this->srId, _this->samplerates.txt)) {
|
|
||||||
_this->samplerate = _this->samplerates.value(_this->srId);
|
|
||||||
core::setInputSampleRate(_this->samplerate);
|
|
||||||
config.acquire();
|
|
||||||
config.conf[_this->name]["samplerate"] = _this->samplerates.key(_this->srId);
|
|
||||||
config.release(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mode protocol selector
|
// Mode protocol selector
|
||||||
ImGui::LeftLabel("Protocol");
|
SmGui::LeftLabel("Protocol");
|
||||||
ImGui::FillWidth();
|
SmGui::FillWidth();
|
||||||
if (ImGui::Combo(("##iq_exporter_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
|
if (SmGui::Combo(("##network_source_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
|
||||||
_this->proto = _this->protocols.value(_this->protoId);
|
_this->proto = _this->protocols.value(_this->protoId);
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
|
config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
|
||||||
@ -264,15 +238,38 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sample type selector
|
// Sample type selector
|
||||||
ImGui::LeftLabel("Sample type");
|
SmGui::LeftLabel("Sample type");
|
||||||
ImGui::FillWidth();
|
SmGui::FillWidth();
|
||||||
if (ImGui::Combo(("##iq_exporter_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
|
if (SmGui::Combo(("##network_source_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
|
||||||
_this->sampType = _this->sampleTypes.value(_this->sampTypeId);
|
_this->sampType = _this->sampleTypes.value(_this->sampTypeId);
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
|
config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Samplerate selector
|
||||||
|
SmGui::LeftLabel("Samplerate");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::InputInt(("##network_source_sr_" + _this->name).c_str(), &_this->tempSamplerate)) {
|
||||||
|
// Prevent silly values from silly users
|
||||||
|
_this->tempSamplerate = std::max<int>(_this->tempSamplerate, 1000);
|
||||||
|
}
|
||||||
|
bool applyEn = (!_this->running && _this->tempSamplerate != _this->samplerate);
|
||||||
|
if (!applyEn) { SmGui::BeginDisabled(); }
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(("Apply##network_source_apply_" + _this->name).c_str())) {
|
||||||
|
_this->samplerate = _this->tempSamplerate;
|
||||||
|
core::setInputSampleRate(_this->samplerate);
|
||||||
|
config.acquire();
|
||||||
|
config.conf[_this->name]["samplerate"] = _this->samplerate;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
if (!applyEn) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
if (_this->tempSamplerate != _this->samplerate) {
|
||||||
|
SmGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Warning: Samplerate not applied yet");
|
||||||
|
}
|
||||||
|
|
||||||
if (_this->running) { SmGui::EndDisabled(); }
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,14 +277,17 @@ private:
|
|||||||
// Compute sizes
|
// Compute sizes
|
||||||
int blockSize = samplerate / 200;
|
int blockSize = samplerate / 200;
|
||||||
int sampleSize = SAMPLE_TYPE_SIZE[sampType];
|
int sampleSize = SAMPLE_TYPE_SIZE[sampType];
|
||||||
int frameSize = blockSize*sampleSize;
|
|
||||||
|
// Chose amount of bytes to attempt to read
|
||||||
|
bool forceSize = (proto != PROTOCOL_UDP);
|
||||||
|
int frameSize = sampleSize * (forceSize ? blockSize : STREAM_BUFFER_SIZE);
|
||||||
|
|
||||||
// Allocate receive buffer
|
// Allocate receive buffer
|
||||||
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
|
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Read samples from socket
|
// Read samples from socket
|
||||||
int bytes = sock->recv(buffer, frameSize, true);
|
int bytes = sock->recv(buffer, frameSize, forceSize);
|
||||||
if (bytes <= 0) { break; }
|
if (bytes <= 0) { break; }
|
||||||
|
|
||||||
// Convert to CF32 (note: problem if partial sample)
|
// Convert to CF32 (note: problem if partial sample)
|
||||||
@ -325,7 +325,7 @@ private:
|
|||||||
double freq;
|
double freq;
|
||||||
|
|
||||||
int samplerate = 1000000;
|
int samplerate = 1000000;
|
||||||
int srId;
|
int tempSamplerate = 1000000;
|
||||||
Protocol proto = PROTOCOL_UDP;
|
Protocol proto = PROTOCOL_UDP;
|
||||||
int protoId;
|
int protoId;
|
||||||
SampleType sampType = SAMPLE_TYPE_INT16;
|
SampleType sampType = SAMPLE_TYPE_INT16;
|
||||||
@ -333,7 +333,6 @@ private:
|
|||||||
char hostname[1024] = "localhost";
|
char hostname[1024] = "localhost";
|
||||||
int port = 1234;
|
int port = 1234;
|
||||||
|
|
||||||
OptionList<int, int> samplerates;
|
|
||||||
OptionList<std::string, Protocol> protocols;
|
OptionList<std::string, Protocol> protocols;
|
||||||
OptionList<std::string, SampleType> sampleTypes;
|
OptionList<std::string, SampleType> sampleTypes;
|
||||||
|
|
||||||
|
@ -23,6 +23,12 @@ SDRPP_MOD_INFO{
|
|||||||
|
|
||||||
ConfigManager config;
|
ConfigManager config;
|
||||||
|
|
||||||
|
const std::vector<const char*> deviceWhiteList = {
|
||||||
|
"PlutoSDR",
|
||||||
|
"ANTSDR",
|
||||||
|
"LibreSDR"
|
||||||
|
};
|
||||||
|
|
||||||
class PlutoSDRSourceModule : public ModuleManager::Instance {
|
class PlutoSDRSourceModule : public ModuleManager::Instance {
|
||||||
public:
|
public:
|
||||||
PlutoSDRSourceModule(std::string name) {
|
PlutoSDRSourceModule(std::string name) {
|
||||||
@ -130,7 +136,14 @@ private:
|
|||||||
std::string duri = iio_context_info_get_uri(info);
|
std::string duri = iio_context_info_get_uri(info);
|
||||||
|
|
||||||
// If the device is not a plutosdr, don't include it
|
// If the device is not a plutosdr, don't include it
|
||||||
if (desc.find("PlutoSDR") == std::string::npos) {
|
bool isPluto = false;
|
||||||
|
for (const auto type : deviceWhiteList) {
|
||||||
|
if (desc.find(type) != std::string::npos) {
|
||||||
|
isPluto = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isPluto) {
|
||||||
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
|
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -523,8 +523,8 @@ private:
|
|||||||
RTLSDRSourceModule* _this = (RTLSDRSourceModule*)ctx;
|
RTLSDRSourceModule* _this = (RTLSDRSourceModule*)ctx;
|
||||||
int sampCount = len / 2;
|
int sampCount = len / 2;
|
||||||
for (int i = 0; i < sampCount; i++) {
|
for (int i = 0; i < sampCount; i++) {
|
||||||
_this->stream.writeBuf[i].re = ((float)buf[i * 2] - 127.4f) / 128.0f;
|
_this->stream.writeBuf[i].re = ((float)buf[i * 2] - 127.4) / 128.0f;
|
||||||
_this->stream.writeBuf[i].im = ((float)buf[(i * 2) + 1] - 127.4f) / 128.0f;
|
_this->stream.writeBuf[i].im = ((float)buf[(i * 2) + 1] - 127.4) / 128.0f;
|
||||||
}
|
}
|
||||||
if (!_this->stream.swap(sampCount)) { return; }
|
if (!_this->stream.swap(sampCount)) { return; }
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user