mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2026-04-20 15:22:42 +00:00
Compare commits
148 Commits
debug
...
65a0e11d3d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65a0e11d3d | ||
|
|
4a48a0c09f | ||
|
|
9c32a68892 | ||
|
|
4658a1ade6 | ||
|
|
b1d94775fe | ||
|
|
2a3fb12f61 | ||
|
|
e82202ea74 | ||
|
|
981f53aa4e | ||
|
|
c216b88366 | ||
|
|
f65e4afb4a | ||
|
|
51ae5628b3 | ||
|
|
9feaf02673 | ||
|
|
5d9f6dc341 | ||
|
|
cf7dd85f92 | ||
|
|
8b8eda301b | ||
|
|
4558e73be5 | ||
|
|
40808c60e4 | ||
|
|
b5d227fecf | ||
|
|
6c00834ddd | ||
|
|
f67fa0c66c | ||
|
|
a94e2d6712 | ||
|
|
981bd1695a | ||
|
|
dd9b8db6c9 | ||
|
|
543c60ccbc | ||
|
|
2dd8c6cea4 | ||
|
|
9457ad9369 | ||
|
|
fccd72b5f8 | ||
|
|
e75cc7be6f | ||
|
|
aa2b4b1c58 | ||
|
|
64315ebc61 | ||
|
|
553204b801 | ||
|
|
2a84ed202c | ||
|
|
f90e2d53a7 | ||
|
|
993bf9128c | ||
|
|
d9ff7eaa12 | ||
|
|
32b289953d | ||
|
|
5178de0849 | ||
|
|
5c355e21f5 | ||
|
|
d020640b7c | ||
|
|
e0f9053229 | ||
|
|
395186ffb0 | ||
|
|
0e77a9f4ab | ||
|
|
4799d0e3a8 | ||
|
|
6c6f4264b2 | ||
|
|
ea3675da47 | ||
|
|
afef9f57ab | ||
|
|
e1de2daca8 | ||
|
|
69bd6b0f3a | ||
|
|
fea3fc2563 | ||
|
|
7bccc67311 | ||
|
|
4310bbb1ea | ||
|
|
d811a839ff | ||
|
|
46bcba7594 | ||
|
|
45e4286f38 | ||
|
|
c266a37a6b | ||
|
|
895199ae94 | ||
|
|
d62426364a | ||
|
|
5ded73ce71 | ||
|
|
cf3e15d285 | ||
|
|
59ca2cf1c0 | ||
|
|
9bb4aeda14 | ||
|
|
304d5c42cc | ||
|
|
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 | ||
|
|
fe9ac6c9a1 | ||
|
|
bfdfa2b30b | ||
|
|
46e98b9b03 | ||
|
|
e674a73771 | ||
|
|
118e1fbff0 | ||
|
|
bcadb36232 | ||
|
|
554ba2f596 | ||
|
|
949fde022d | ||
|
|
123e34d250 | ||
|
|
f1c7010437 | ||
|
|
33a7795de1 | ||
|
|
fe7299c18a | ||
|
|
8a9e0abcc2 | ||
|
|
13abe4860b | ||
|
|
981592fa19 | ||
|
|
582750f79b | ||
|
|
36f2a083ce | ||
|
|
d753135a61 | ||
|
|
07744e5bae | ||
|
|
9ec78da7ac | ||
|
|
0066994899 | ||
|
|
93ab51bf2f | ||
|
|
f9d7d20073 | ||
|
|
e81db5d85c | ||
|
|
5c3a66642b | ||
|
|
36492e799a | ||
|
|
0110dfbef6 | ||
|
|
0b5a2ff786 | ||
|
|
ce0f1f05ae | ||
|
|
46a5ff8ac5 | ||
|
|
03559b928b | ||
|
|
206ce6e8c3 | ||
|
|
d7a1f46af0 | ||
|
|
89e6e4f7ad | ||
|
|
6ced9b15c3 | ||
|
|
0de189a7b7 | ||
|
|
309717b5f8 |
291
.github/workflows/build_all.yml
vendored
291
.github/workflows/build_all.yml
vendored
@@ -36,6 +36,13 @@ jobs:
|
|||||||
working-directory: ${{runner.workspace}}
|
working-directory: ${{runner.workspace}}
|
||||||
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
|
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
|
||||||
|
|
||||||
|
- name: Download librtlsdr
|
||||||
|
run: Invoke-WebRequest -Uri "https://ftp.osmocom.org/binaries/windows/rtl-sdr/rtl-sdr-64bit-20240623.zip" -OutFile ${{runner.workspace}}/rtl-sdr.zip
|
||||||
|
|
||||||
|
- name: Patch Pothos with newer librtlsdr version
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: 7z x rtl-sdr.zip ; rm "C:/Program Files/PothosSDR/bin/rtlsdr.dll" ; cp "rtl-sdr-64bit-20240623/librtlsdr.dll" "C:/Program Files/PothosSDR/bin/rtlsdr.dll"
|
||||||
|
|
||||||
- name: Download SDRPlay API
|
- name: Download SDRPlay API
|
||||||
run: Invoke-WebRequest -Uri "https://www.sdrpp.org/SDRplay.zip" -OutFile ${{runner.workspace}}/SDRplay.zip
|
run: Invoke-WebRequest -Uri "https://www.sdrpp.org/SDRplay.zip" -OutFile ${{runner.workspace}}/SDRplay.zip
|
||||||
|
|
||||||
@@ -43,7 +50,7 @@ jobs:
|
|||||||
run: 7z x ${{runner.workspace}}/SDRplay.zip -o"C:/Program Files/"
|
run: 7z x ${{runner.workspace}}/SDRplay.zip -o"C:/Program Files/"
|
||||||
|
|
||||||
- name: Download codec2
|
- name: Download codec2
|
||||||
run: git clone https://github.com/AlexandreRouma/codec2
|
run: git clone https://github.com/drowe67/codec2
|
||||||
|
|
||||||
- name: Prepare MinGW
|
- name: Prepare MinGW
|
||||||
run: C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -c "pacman --noconfirm -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja"
|
run: C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -c "pacman --noconfirm -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja"
|
||||||
@@ -58,17 +65,26 @@ jobs:
|
|||||||
run: mkdir "C:/Program Files/codec2" ; mkdir "C:/Program Files/codec2/include" ; mkdir "C:/Program Files/codec2/include/codec2" ; mkdir "C:/Program Files/codec2/lib" ; cd "codec2" ; xcopy "src" "C:/Program Files/codec2/include" ; cd "build" ; xcopy "src" "C:/Program Files/codec2/lib" ; xcopy "codec2" "C:/Program Files/codec2/include/codec2"
|
run: mkdir "C:/Program Files/codec2" ; mkdir "C:/Program Files/codec2/include" ; mkdir "C:/Program Files/codec2/include/codec2" ; mkdir "C:/Program Files/codec2/lib" ; cd "codec2" ; xcopy "src" "C:/Program Files/codec2/include" ; cd "build" ; xcopy "src" "C:/Program Files/codec2/lib" ; xcopy "codec2" "C:/Program Files/codec2/include/codec2"
|
||||||
|
|
||||||
- name: Install vcpkg dependencies
|
- name: Install vcpkg dependencies
|
||||||
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows
|
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows spdlog:x64-windows
|
||||||
|
|
||||||
- name: Install rtaudio
|
- name: Install rtaudio
|
||||||
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
|
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
|
||||||
|
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: Install libhydrasdr
|
||||||
|
run: git clone https://github.com/hydrasdr/rfone_host; cd rfone_host/ ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DLIBUSB_LIBRARIES=C:\Program Files\PothosSDR\lib\libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIR=C:\Program Files\PothosSDR\include\libusb-1.0" "-DTHREADS_PTHREADS_WIN32_LIBRARY=C:\Program Files\PothosSDR\lib\pthreadVC2.lib" "-DTHREADS_PTHREADS_INCLUDE_DIR=C:\Program Files\PothosSDR\include" ; cmake --build . --config Release ; cmake --install .
|
||||||
|
|
||||||
- name: Prepare CMake
|
- name: Prepare CMake
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
@@ -85,7 +101,7 @@ jobs:
|
|||||||
path: ${{runner.workspace}}/sdrpp_windows_x64.zip
|
path: ${{runner.workspace}}/sdrpp_windows_x64.zip
|
||||||
|
|
||||||
build_macos_intel:
|
build_macos_intel:
|
||||||
runs-on: macos-12
|
runs-on: macos-15-intel
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -94,7 +110,7 @@ jobs:
|
|||||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
|
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako --break-system-packages
|
||||||
|
|
||||||
- name: Install volk
|
- name: Install volk
|
||||||
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||||
@@ -106,20 +122,29 @@ jobs:
|
|||||||
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||||
|
|
||||||
- name: Install libad9361
|
- name: Install libad9361
|
||||||
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5 .. && make -j3 && sudo make install && cd ../../
|
||||||
|
|
||||||
- name: Install LimeSuite
|
- name: Install LimeSuite
|
||||||
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||||
|
|
||||||
- name: Install libperseus
|
- name: Install libperseus
|
||||||
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
|
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && sudo make install && cd ..
|
||||||
|
|
||||||
|
- name: Install librfnm
|
||||||
|
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||||
|
|
||||||
|
- name: Install libfobos
|
||||||
|
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||||
|
|
||||||
- name: Install more recent librtlsdr
|
- 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: Install libhydrasdr
|
||||||
|
run: git clone https://github.com/hydrasdr/rfone_host && cd rfone_host && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && 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 -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
run: cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -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 -DOPT_BUILD_HYDRASDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
@@ -136,7 +161,7 @@ jobs:
|
|||||||
path: ${{runner.workspace}}/sdrpp_macos_intel.zip
|
path: ${{runner.workspace}}/sdrpp_macos_intel.zip
|
||||||
|
|
||||||
build_macos_arm:
|
build_macos_arm:
|
||||||
runs-on: macos-14
|
runs-on: macos-15
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -145,7 +170,7 @@ jobs:
|
|||||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako --break-system-packages
|
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako --break-system-packages
|
||||||
|
|
||||||
- name: Install volk
|
- name: Install volk
|
||||||
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||||
@@ -157,7 +182,7 @@ jobs:
|
|||||||
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||||
|
|
||||||
- name: Install libad9361
|
- name: Install libad9361
|
||||||
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5 .. && make -j3 && sudo make install && cd ../../
|
||||||
|
|
||||||
- name: Install LimeSuite
|
- name: Install LimeSuite
|
||||||
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||||
@@ -165,12 +190,21 @@ jobs:
|
|||||||
# - name: Install libperseus
|
# - name: Install libperseus
|
||||||
# run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
|
# run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
|
||||||
|
|
||||||
|
- name: Install librfnm
|
||||||
|
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||||
|
|
||||||
|
- name: Install libfobos
|
||||||
|
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||||
|
|
||||||
- name: Install more recent librtlsdr
|
- 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: Install libhydrasdr
|
||||||
|
run: git clone https://github.com/hydrasdr/rfone_host && cd rfone_host && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && 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 -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
run: cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -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 -DOPT_BUILD_HYDRASDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
@@ -186,29 +220,7 @@ jobs:
|
|||||||
name: sdrpp_macos_arm
|
name: sdrpp_macos_arm
|
||||||
path: ${{runner.workspace}}/sdrpp_macos_arm.zip
|
path: ${{runner.workspace}}/sdrpp_macos_arm.zip
|
||||||
|
|
||||||
build_debian_buster:
|
build_debian_bullseye_amd64:
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Create Docker Image
|
|
||||||
run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build
|
|
||||||
|
|
||||||
- name: Run Container
|
|
||||||
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
|
||||||
|
|
||||||
- name: Recover Deb Archive
|
|
||||||
working-directory: ${{runner.workspace}}
|
|
||||||
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
|
||||||
|
|
||||||
- name: Save Deb Archive
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: sdrpp_debian_buster_amd64
|
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
|
||||||
|
|
||||||
build_debian_bullseye:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -230,7 +242,29 @@ jobs:
|
|||||||
name: sdrpp_debian_bullseye_amd64
|
name: sdrpp_debian_bullseye_amd64
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
build_debian_bookworm:
|
build_debian_bullseye_aarch64:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bullseye && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sdrpp_debian_bullseye_aarch64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_debian_bookworm_amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -252,7 +286,73 @@ jobs:
|
|||||||
name: sdrpp_debian_bookworm_amd64
|
name: sdrpp_debian_bookworm_amd64
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
build_debian_sid:
|
build_debian_bookworm_aarch64:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bookworm && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sdrpp_debian_bookworm_aarch64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_debian_trixie_amd64:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/debian_trixie && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sdrpp_debian_trixie_amd64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_debian_trixie_aarch64:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/debian_trixie && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sdrpp_debian_trixie_aarch64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_debian_sid_amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -274,7 +374,29 @@ jobs:
|
|||||||
name: sdrpp_debian_sid_amd64
|
name: sdrpp_debian_sid_amd64
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
build_ubuntu_focal:
|
build_debian_sid_aarch64:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/debian_sid && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sdrpp_debian_sid_aarch64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_ubuntu_focal_amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -296,7 +418,29 @@ jobs:
|
|||||||
name: sdrpp_ubuntu_focal_amd64
|
name: sdrpp_ubuntu_focal_amd64
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
build_ubuntu_jammy:
|
build_ubuntu_focal_aarch64:
|
||||||
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create Docker Image
|
||||||
|
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_focal && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
|
- name: Run Container
|
||||||
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
|
|
||||||
|
- name: Recover Deb Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
|
- name: Save Deb Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sdrpp_ubuntu_focal_aarch64
|
||||||
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
|
build_ubuntu_jammy_amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -318,14 +462,14 @@ jobs:
|
|||||||
name: sdrpp_ubuntu_jammy_amd64
|
name: sdrpp_ubuntu_jammy_amd64
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
build_ubuntu_mantic:
|
build_ubuntu_jammy_aarch64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Create Docker Image
|
- name: Create Docker Image
|
||||||
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_mantic && docker build . --tag sdrpp_build
|
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_jammy && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
- name: Run Container
|
- name: Run Container
|
||||||
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
@@ -337,10 +481,10 @@ jobs:
|
|||||||
- name: Save Deb Archive
|
- name: Save Deb Archive
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: sdrpp_ubuntu_mantic_amd64
|
name: sdrpp_ubuntu_jammy_aarch64
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
build_ubuntu_noble:
|
build_ubuntu_noble_amd64:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -362,32 +506,27 @@ jobs:
|
|||||||
name: sdrpp_ubuntu_noble_amd64
|
name: sdrpp_ubuntu_noble_amd64
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
build_raspios_bullseye_armhf:
|
build_ubuntu_noble_aarch64:
|
||||||
runs-on: ARM
|
runs-on: ubuntu-24.04-arm
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Create Build Environment
|
- name: Create Docker Image
|
||||||
run: rm -rf ${{runner.workspace}}/build && cmake -E make_directory ${{runner.workspace}}/build
|
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_noble && docker build . --tag sdrpp_build
|
||||||
|
|
||||||
- name: Prepare CMake
|
- name: Run Container
|
||||||
working-directory: ${{runner.workspace}}/build
|
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
|
||||||
run: cmake $GITHUB_WORKSPACE -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_USRP_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
|
||||||
|
|
||||||
- name: Build
|
- name: Recover Deb Archive
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: make VERBOSE=1 -j3
|
|
||||||
|
|
||||||
- name: Create Dev Archive
|
|
||||||
working-directory: ${{runner.workspace}}
|
working-directory: ${{runner.workspace}}
|
||||||
run: sh $GITHUB_WORKSPACE/make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev' && mv sdrpp_debian_amd64.deb sdrpp_debian_armhf.deb
|
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
|
||||||
|
|
||||||
- name: Save Deb Archive
|
- name: Save Deb Archive
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: sdrpp_raspios_bullseye_armhf
|
name: sdrpp_ubuntu_noble_aarch64
|
||||||
path: ${{runner.workspace}}/sdrpp_debian_armhf.deb
|
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||||
|
|
||||||
build_android:
|
build_android:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -417,7 +556,26 @@ jobs:
|
|||||||
path: ${{runner.workspace}}/sdrpp.apk
|
path: ${{runner.workspace}}/sdrpp.apk
|
||||||
|
|
||||||
create_full_archive:
|
create_full_archive:
|
||||||
needs: ['build_windows', 'build_macos_intel', 'build_macos_arm', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_ubuntu_mantic', 'build_ubuntu_noble', 'build_raspios_bullseye_armhf', 'build_android']
|
needs: [
|
||||||
|
'build_windows',
|
||||||
|
'build_macos_intel',
|
||||||
|
'build_macos_arm',
|
||||||
|
'build_debian_bullseye_amd64',
|
||||||
|
'build_debian_bullseye_aarch64',
|
||||||
|
'build_debian_bookworm_amd64',
|
||||||
|
'build_debian_bookworm_aarch64',
|
||||||
|
'build_debian_trixie_amd64',
|
||||||
|
'build_debian_trixie_aarch64',
|
||||||
|
'build_debian_sid_amd64',
|
||||||
|
'build_debian_sid_aarch64',
|
||||||
|
'build_ubuntu_focal_amd64',
|
||||||
|
'build_ubuntu_focal_aarch64',
|
||||||
|
'build_ubuntu_jammy_amd64',
|
||||||
|
'build_ubuntu_jammy_aarch64',
|
||||||
|
'build_ubuntu_noble_amd64',
|
||||||
|
'build_ubuntu_noble_aarch64',
|
||||||
|
'build_android'
|
||||||
|
]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -430,15 +588,20 @@ jobs:
|
|||||||
mv sdrpp_windows_x64/sdrpp_windows_x64.zip sdrpp_all/ &&
|
mv sdrpp_windows_x64/sdrpp_windows_x64.zip sdrpp_all/ &&
|
||||||
mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ &&
|
mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ &&
|
||||||
mv sdrpp_macos_arm/sdrpp_macos_arm.zip sdrpp_all/ &&
|
mv sdrpp_macos_arm/sdrpp_macos_arm.zip sdrpp_all/ &&
|
||||||
mv sdrpp_debian_buster_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_amd64.deb &&
|
|
||||||
mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb &&
|
mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb &&
|
||||||
|
mv sdrpp_debian_bullseye_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_aarch64.deb &&
|
||||||
mv sdrpp_debian_bookworm_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_amd64.deb &&
|
mv sdrpp_debian_bookworm_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_amd64.deb &&
|
||||||
|
mv sdrpp_debian_bookworm_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_aarch64.deb &&
|
||||||
|
mv sdrpp_debian_trixie_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_trixie_amd64.deb &&
|
||||||
|
mv sdrpp_debian_trixie_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_trixie_aarch64.deb &&
|
||||||
mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
|
mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
|
||||||
|
mv sdrpp_debian_sid_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_aarch64.deb &&
|
||||||
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
|
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
|
||||||
|
mv sdrpp_ubuntu_focal_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_aarch64.deb &&
|
||||||
mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb &&
|
mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb &&
|
||||||
mv sdrpp_ubuntu_mantic_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_mantic_amd64.deb &&
|
mv sdrpp_ubuntu_jammy_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_aarch64.deb &&
|
||||||
mv sdrpp_ubuntu_noble_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_noble_amd64.deb &&
|
mv sdrpp_ubuntu_noble_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_noble_amd64.deb &&
|
||||||
mv sdrpp_raspios_bullseye_armhf/sdrpp_debian_armhf.deb sdrpp_all/sdrpp_raspios_bullseye_armhf.deb &&
|
mv sdrpp_ubuntu_noble_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_noble_aarch64.deb &&
|
||||||
mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk
|
mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,3 +17,7 @@ m17_decoder/libcorrect
|
|||||||
SDR++.app
|
SDR++.app
|
||||||
android/deps
|
android/deps
|
||||||
android/app/assets
|
android/app/assets
|
||||||
|
source_modules/dragonlabs_source
|
||||||
|
source_modules/badgesdr_source
|
||||||
|
decoder_modules/mystery_decoder
|
||||||
|
decoder_modules/tetra_decoder
|
||||||
@@ -15,8 +15,12 @@ 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_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_HYDRASDR_SOURCE "Build HydraSDR Source Module (Dependencies: libhydrasdr)" OFF)
|
||||||
|
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)
|
||||||
@@ -41,13 +45,16 @@ option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Depend
|
|||||||
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
|
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
|
||||||
|
|
||||||
# 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)" ON)
|
||||||
|
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)
|
||||||
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
|
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
|
||||||
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON)
|
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON)
|
||||||
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
|
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
|
||||||
|
option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF)
|
||||||
|
option(OPT_BUILD_VOR_RECEIVER "VOR beacon receiver" OFF)
|
||||||
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
|
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
@@ -63,6 +70,7 @@ option(OPT_BUILD_SCHEDULER "Build the scheduler" OFF)
|
|||||||
# Other options
|
# Other options
|
||||||
option(USE_INTERNAL_LIBCORRECT "Use an internal version of libcorrect" ON)
|
option(USE_INTERNAL_LIBCORRECT "Use an internal version of libcorrect" ON)
|
||||||
option(USE_BUNDLE_DEFAULTS "Set the default resource and module directories to the right ones for a MacOS .app" OFF)
|
option(USE_BUNDLE_DEFAULTS "Set the default resource and module directories to the right ones for a MacOS .app" OFF)
|
||||||
|
option(COPY_MSVC_REDISTRIBUTABLES "Copy over the Visual C++ Redistributable" OFF)
|
||||||
|
|
||||||
# Module cmake path
|
# Module cmake path
|
||||||
set(SDRPP_MODULE_CMAKE "${CMAKE_SOURCE_DIR}/sdrpp_module.cmake")
|
set(SDRPP_MODULE_CMAKE "${CMAKE_SOURCE_DIR}/sdrpp_module.cmake")
|
||||||
@@ -139,14 +147,30 @@ 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)
|
||||||
|
|
||||||
|
if (OPT_BUILD_HAROGIC_SOURCE)
|
||||||
|
add_subdirectory("source_modules/harogic_source")
|
||||||
|
endif (OPT_BUILD_HAROGIC_SOURCE)
|
||||||
|
|
||||||
if (OPT_BUILD_HERMES_SOURCE)
|
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_HYDRASDR_SOURCE)
|
||||||
|
add_subdirectory("source_modules/hydrasdr_source")
|
||||||
|
endif (OPT_BUILD_HYDRASDR_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)
|
||||||
@@ -179,6 +203,10 @@ if (OPT_BUILD_RTL_TCP_SOURCE)
|
|||||||
add_subdirectory("source_modules/rtl_tcp_source")
|
add_subdirectory("source_modules/rtl_tcp_source")
|
||||||
endif (OPT_BUILD_RTL_TCP_SOURCE)
|
endif (OPT_BUILD_RTL_TCP_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_SDDC_SOURCE)
|
||||||
|
add_subdirectory("source_modules/sddc_source")
|
||||||
|
endif (OPT_BUILD_SDDC_SOURCE)
|
||||||
|
|
||||||
if (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
if (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
||||||
add_subdirectory("source_modules/sdrpp_server_source")
|
add_subdirectory("source_modules/sdrpp_server_source")
|
||||||
endif (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
endif (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
||||||
@@ -235,6 +263,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)
|
||||||
@@ -259,6 +291,14 @@ if (OPT_BUILD_RADIO)
|
|||||||
add_subdirectory("decoder_modules/radio")
|
add_subdirectory("decoder_modules/radio")
|
||||||
endif (OPT_BUILD_RADIO)
|
endif (OPT_BUILD_RADIO)
|
||||||
|
|
||||||
|
if (OPT_BUILD_RYFI_DECODER)
|
||||||
|
add_subdirectory("decoder_modules/ryfi_decoder")
|
||||||
|
endif (OPT_BUILD_RYFI_DECODER)
|
||||||
|
|
||||||
|
if (OPT_BUILD_VOR_RECEIVER)
|
||||||
|
add_subdirectory("decoder_modules/vor_receiver")
|
||||||
|
endif (OPT_BUILD_VOR_RECEIVER)
|
||||||
|
|
||||||
if (OPT_BUILD_WEATHER_SAT_DECODER)
|
if (OPT_BUILD_WEATHER_SAT_DECODER)
|
||||||
add_subdirectory("decoder_modules/weather_sat_decoder")
|
add_subdirectory("decoder_modules/weather_sat_decoder")
|
||||||
endif (OPT_BUILD_WEATHER_SAT_DECODER)
|
endif (OPT_BUILD_WEATHER_SAT_DECODER)
|
||||||
@@ -312,6 +352,21 @@ target_compile_options(sdrpp PRIVATE ${SDRPP_COMPILER_FLAGS})
|
|||||||
if (MSVC)
|
if (MSVC)
|
||||||
add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
||||||
add_custom_target(do_always_volk ALL xcopy /s \"C:/Program Files/PothosSDR/bin\\volk.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
add_custom_target(do_always_volk ALL xcopy /s \"C:/Program Files/PothosSDR/bin\\volk.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
||||||
|
|
||||||
|
if (COPY_MSVC_REDISTRIBUTABLES)
|
||||||
|
# Get the list of Visual C++ runtime DLLs
|
||||||
|
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP True)
|
||||||
|
include(InstallRequiredSystemLibraries)
|
||||||
|
|
||||||
|
# Create a space sperated list
|
||||||
|
set(REDIST_DLLS_STR "")
|
||||||
|
foreach(DLL IN LISTS CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS)
|
||||||
|
set(REDIST_DLLS_STR COMMAND xcopy /F \"${DLL}\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y ${REDIST_DLLS_STR})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Create target
|
||||||
|
add_custom_target(do_always_msvc ALL ${REDIST_DLLS_STR})
|
||||||
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ android {
|
|||||||
minSdkVersion 28
|
minSdkVersion 28
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.2.0"
|
versionName "1.2.1"
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
arguments "-DOPT_BACKEND_GLFW=OFF", "-DOPT_BACKEND_ANDROID=ON", "-DOPT_BUILD_SOAPY_SOURCE=OFF", "-DOPT_BUILD_ANDROID_AUDIO_SINK=ON", "-DOPT_BUILD_AUDIO_SINK=OFF", "-DOPT_BUILD_DISCORD_PRESENCE=OFF", "-DOPT_BUILD_M17_DECODER=ON", "-DOPT_BUILD_PLUTOSDR_SOURCE=ON", "-DOPT_BUILD_AUDIO_SOURCE=OFF"
|
arguments "-DOPT_BACKEND_GLFW=OFF", "-DOPT_BACKEND_ANDROID=ON", "-DOPT_BUILD_SOAPY_SOURCE=OFF", "-DOPT_BUILD_ANDROID_AUDIO_SINK=ON", "-DOPT_BUILD_AUDIO_SINK=OFF", "-DOPT_BUILD_DISCORD_PRESENCE=OFF", "-DOPT_BUILD_M17_DECODER=ON", "-DOPT_BUILD_PLUTOSDR_SOURCE=ON", "-DOPT_BUILD_AUDIO_SOURCE=OFF", "-DOPT_BUILD_HYDRASDR_SOURCE=ON"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
# AI USE IS FORBIDDEN
|
||||||
|
|
||||||
|
Use of AI for the creation of code, pull requests or issues is forbidden.
|
||||||
|
|
||||||
|
Submitting code, pull requests or issues generated by AI will result in a permanent ban from all of my (Alexandre Rouma) repositories.
|
||||||
|
|
||||||
# Pull Requests
|
# Pull Requests
|
||||||
|
|
||||||
Code pull requests are **NOT welcome**. Please open an issue discussing potential bugfixes or feature requests instead.
|
Code pull requests are **NOT welcome**. Please open an issue discussing potential bugfixes or feature requests instead.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ namespace backend {
|
|||||||
extern const std::vector<DevVIDPID> AIRSPY_VIDPIDS;
|
extern const std::vector<DevVIDPID> AIRSPY_VIDPIDS;
|
||||||
extern const std::vector<DevVIDPID> AIRSPYHF_VIDPIDS;
|
extern const std::vector<DevVIDPID> AIRSPYHF_VIDPIDS;
|
||||||
extern const std::vector<DevVIDPID> HACKRF_VIDPIDS;
|
extern const std::vector<DevVIDPID> HACKRF_VIDPIDS;
|
||||||
|
extern const std::vector<DevVIDPID> HYDRASDR_VIDPIDS;
|
||||||
extern const std::vector<DevVIDPID> RTL_SDR_VIDPIDS;
|
extern const std::vector<DevVIDPID> RTL_SDR_VIDPIDS;
|
||||||
|
|
||||||
int getDeviceFD(int& vid, int& pid, const std::vector<DevVIDPID>& allowedVidPids);
|
int getDeviceFD(int& vid, int& pid, const std::vector<DevVIDPID>& allowedVidPids);
|
||||||
|
|||||||
@@ -408,6 +408,11 @@ namespace backend {
|
|||||||
{ 0x1d50, 0xcc15 }
|
{ 0x1d50, 0xcc15 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const std::vector<DevVIDPID> HYDRASDR_VIDPIDS = {
|
||||||
|
{ 0x1d50, 0x60a1 },
|
||||||
|
{ 0x38af, 0x0001 }
|
||||||
|
};
|
||||||
|
|
||||||
const std::vector<DevVIDPID> RTL_SDR_VIDPIDS = {
|
const std::vector<DevVIDPID> RTL_SDR_VIDPIDS = {
|
||||||
{ 0x0bda, 0x2832 },
|
{ 0x0bda, 0x2832 },
|
||||||
{ 0x0bda, 0x2838 },
|
{ 0x0bda, 0x2838 },
|
||||||
|
|||||||
@@ -99,6 +99,9 @@ namespace backend {
|
|||||||
glfwWindowHint(GLFW_CLIENT_API, OPENGL_VERSIONS_IS_ES[i] ? GLFW_OPENGL_ES_API : GLFW_OPENGL_API);
|
glfwWindowHint(GLFW_CLIENT_API, OPENGL_VERSIONS_IS_ES[i] ? GLFW_OPENGL_ES_API : GLFW_OPENGL_API);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_VERSIONS_MAJOR[i]);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_VERSIONS_MAJOR[i]);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_VERSIONS_MINOR[i]);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_VERSIONS_MINOR[i]);
|
||||||
|
#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 4)
|
||||||
|
glfwWindowHintString(GLFW_WAYLAND_APP_ID, "sdrpp");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Create window with graphics context
|
// Create window with graphics context
|
||||||
monitor = glfwGetPrimaryMonitor();
|
monitor = glfwGetPrimaryMonitor();
|
||||||
|
|||||||
@@ -147,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;
|
||||||
|
|
||||||
@@ -173,16 +173,26 @@ 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"]["HydraSDR Source"]["module"] = "hydrasdr_source";
|
||||||
|
defConfig["moduleInstances"]["HydraSDR 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"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
|
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
|
||||||
defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";
|
defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";
|
||||||
@@ -193,8 +203,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";
|
||||||
@@ -220,12 +234,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;
|
||||||
|
|
||||||
@@ -276,6 +297,7 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
core::configManager.conf["modules"][modCount++] = "airspyhf_source.so";
|
core::configManager.conf["modules"][modCount++] = "airspyhf_source.so";
|
||||||
core::configManager.conf["modules"][modCount++] = "hackrf_source.so";
|
core::configManager.conf["modules"][modCount++] = "hackrf_source.so";
|
||||||
core::configManager.conf["modules"][modCount++] = "hermes_source.so";
|
core::configManager.conf["modules"][modCount++] = "hermes_source.so";
|
||||||
|
core::configManager.conf["modules"][modCount++] = "hydrasdr_source.so";
|
||||||
core::configManager.conf["modules"][modCount++] = "plutosdr_source.so";
|
core::configManager.conf["modules"][modCount++] = "plutosdr_source.so";
|
||||||
core::configManager.conf["modules"][modCount++] = "rfspace_source.so";
|
core::configManager.conf["modules"][modCount++] = "rfspace_source.so";
|
||||||
core::configManager.conf["modules"][modCount++] = "rtl_sdr_source.so";
|
core::configManager.conf["modules"][modCount++] = "rtl_sdr_source.so";
|
||||||
@@ -306,12 +328,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"
|
||||||
};
|
};
|
||||||
@@ -66,15 +70,18 @@ namespace sdrpp_credits {
|
|||||||
"Flinger Films",
|
"Flinger Films",
|
||||||
"Frank Werner (HB9FXQ)",
|
"Frank Werner (HB9FXQ)",
|
||||||
"gringogrigio",
|
"gringogrigio",
|
||||||
|
"Jandro",
|
||||||
"Jeff Moe",
|
"Jeff Moe",
|
||||||
"Joe Cupano",
|
"Joe Cupano",
|
||||||
"KD1SQ",
|
"KD1SQ",
|
||||||
"Kezza",
|
"Kezza",
|
||||||
"Krys Kamieniecki",
|
"Krys Kamieniecki",
|
||||||
"Lee Donaghy",
|
"Lee Donaghy",
|
||||||
"Lee KD1SQ",
|
"Lee (KD1SQ)",
|
||||||
".lozenge. (Hank Hill)",
|
".lozenge. (Hank Hill)",
|
||||||
"Martin Herren (HB9FXX)",
|
"Martin Herren (HB9FXX)",
|
||||||
|
"NeoVilsonWong",
|
||||||
|
"Nitin (VU2JEK)",
|
||||||
"ON4MU",
|
"ON4MU",
|
||||||
"Passion-Radio.com",
|
"Passion-Radio.com",
|
||||||
"Paul Maine",
|
"Paul Maine",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <volk/volk.h>
|
#include <volk/volk.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
namespace dsp::buffer {
|
namespace dsp::buffer {
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ namespace dsp {
|
|||||||
template <class T>
|
template <class T>
|
||||||
class Source : public block {
|
class Source : public block {
|
||||||
public:
|
public:
|
||||||
Source() {}
|
|
||||||
|
|
||||||
Source() { init(); }
|
Source() { init(); }
|
||||||
|
|
||||||
virtual ~Source() {}
|
virtual ~Source() {}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <volk/volk.h>
|
#include <volk/volk.h>
|
||||||
|
#include "../buffer/buffer.h"
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
template<class T>
|
template<class T>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ void SourceManager::tune(double freq) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: No need to always retune the hardware in Panadapter mode
|
// TODO: No need to always retune the hardware in Panadapter mode
|
||||||
selectedHandler->tuneHandler(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset, selectedHandler->ctx);
|
selectedHandler->tuneHandler(abs(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset), selectedHandler->ctx);
|
||||||
onRetune.emit(freq);
|
onRetune.emit(freq);
|
||||||
currentFreq = freq;
|
currentFreq = freq;
|
||||||
}
|
}
|
||||||
|
|||||||
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"
|
||||||
66
decoder_modules/atv_decoder/src/amplitude.h
Normal file
66
decoder_modules/atv_decoder/src/amplitude.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/processor.h>
|
||||||
|
#include <dsp/math/fast_atan2.h>
|
||||||
|
#include <dsp/math/hz_to_rads.h>
|
||||||
|
#include <dsp/math/normalize_phase.h>
|
||||||
|
|
||||||
|
namespace dsp::demod {
|
||||||
|
class Amplitude : public Processor<complex_t, float> {
|
||||||
|
using base_type = Processor<complex_t, float>;
|
||||||
|
public:
|
||||||
|
Amplitude() {}
|
||||||
|
|
||||||
|
Amplitude(stream<complex_t>* in, double deviation) { init(in, deviation); }
|
||||||
|
|
||||||
|
Amplitude(stream<complex_t>* in, double deviation, double samplerate) { init(in, deviation, samplerate); }
|
||||||
|
|
||||||
|
|
||||||
|
virtual void init(stream<complex_t>* in, double deviation) {
|
||||||
|
_invDeviation = 1.0 / deviation;
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void init(stream<complex_t>* in, double deviation, double samplerate) {
|
||||||
|
init(in, math::hzToRads(deviation, samplerate));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDeviation(double deviation) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
_invDeviation = 1.0 / deviation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDeviation(double deviation, double samplerate) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
_invDeviation = 1.0 / math::hzToRads(deviation, samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int process(int count, complex_t* in, float* out) {
|
||||||
|
volk_32fc_magnitude_32f(out, (lv_32fc_t*)in, count);
|
||||||
|
volk_32f_s32f_multiply_32f(out, out, -1.0f, count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
phase = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
process(count, base_type::_in->readBuf, base_type::out.writeBuf);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!base_type::out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
float _invDeviation;
|
||||||
|
float phase = 0.0f;
|
||||||
|
};
|
||||||
|
}
|
||||||
176
decoder_modules/atv_decoder/src/burst_blanking.txt
Normal file
176
decoder_modules/atv_decoder/src/burst_blanking.txt
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
617 + 6 -> I
|
||||||
|
|
||||||
|
304 + 6 -> II
|
||||||
|
|
||||||
|
616 + 6 -> III
|
||||||
|
|
||||||
|
305 + 6 -> IV
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
304
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
|
||||||
|
616
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
|
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
313
|
||||||
|
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
|
0
|
||||||
|
|
||||||
|
304
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
616
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
313
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
|
0
|
||||||
|
304
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
616
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
313
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
|
0
|
||||||
|
304
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
616
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
313
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
|
0
|
||||||
|
304
|
||||||
|
305
|
||||||
|
306
|
||||||
|
307
|
||||||
|
308
|
||||||
|
309
|
||||||
|
310
|
||||||
|
311
|
||||||
|
312
|
||||||
|
616
|
||||||
|
617
|
||||||
|
618
|
||||||
|
619
|
||||||
|
620
|
||||||
|
621
|
||||||
|
622
|
||||||
|
623
|
||||||
|
624
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <dsp/loop/pll.h>
|
|
||||||
#include "chrominance_filter.h"
|
|
||||||
|
|
||||||
// TODO: Should be 60 but had to try something
|
|
||||||
#define BURST_START (63+CHROMA_FIR_DELAY)
|
|
||||||
#define BURST_END (BURST_START+28)
|
|
||||||
|
|
||||||
#define A_PHASE ((135.0/180.0)*FL_M_PI)
|
|
||||||
#define B_PHASE ((-135.0/180.0)*FL_M_PI)
|
|
||||||
|
|
||||||
namespace dsp::loop {
|
|
||||||
class ChromaPLL : public PLL {
|
|
||||||
using base_type = PLL;
|
|
||||||
public:
|
|
||||||
ChromaPLL() {}
|
|
||||||
|
|
||||||
ChromaPLL(stream<complex_t>* in, double bandwidth, double initPhase = 0.0, double initFreq = 0.0, double minFreq = -FL_M_PI, double maxFreq = FL_M_PI) {
|
|
||||||
base_type::init(in, bandwidth, initFreq, initPhase, minFreq, maxFreq);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int process(int count, complex_t* in, complex_t* out, bool aphase = false) {
|
|
||||||
// Process the pre-burst section
|
|
||||||
for (int i = 0; i < BURST_START; i++) {
|
|
||||||
out[i] = in[i] * math::phasor(-pcl.phase);
|
|
||||||
pcl.advancePhase();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the burst itself
|
|
||||||
if (aphase) {
|
|
||||||
for (int i = BURST_START; i < BURST_END; i++) {
|
|
||||||
complex_t outVal = in[i] * math::phasor(-pcl.phase);
|
|
||||||
out[i] = outVal;
|
|
||||||
pcl.advance(math::normalizePhase(outVal.phase() - A_PHASE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (int i = BURST_START; i < BURST_END; i++) {
|
|
||||||
complex_t outVal = in[i] * math::phasor(-pcl.phase);
|
|
||||||
out[i] = outVal;
|
|
||||||
pcl.advance(math::normalizePhase(outVal.phase() - B_PHASE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Process the post-burst section
|
|
||||||
for (int i = BURST_END; i < count; i++) {
|
|
||||||
out[i] = in[i] * math::phasor(-pcl.phase);
|
|
||||||
pcl.advancePhase();
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int processBlank(int count, complex_t* in, complex_t* out) {
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
out[i] = in[i] * math::phasor(-pcl.phase);
|
|
||||||
pcl.advancePhase();
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,239 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <dsp/types.h>
|
|
||||||
|
|
||||||
inline const dsp::complex_t CHROMA_FIR[] = {
|
|
||||||
{-0.000005461290583903, -0.000011336784355655},
|
|
||||||
{ 0.000020060944485414, 0.000009851315045203},
|
|
||||||
{-0.000034177222729438, 0.000007245841504981},
|
|
||||||
{ 0.000027694034878705, -0.000033114740542635},
|
|
||||||
{-0.000001217597841648, 0.000039141482370942},
|
|
||||||
{-0.000008324593371228, -0.000011315001355976},
|
|
||||||
{-0.000038085228233509, -0.000010585909953738},
|
|
||||||
{ 0.000114833396071141, -0.000047778708840608},
|
|
||||||
{-0.000115428390169113, 0.000205816198882814},
|
|
||||||
{-0.000055467806072871, -0.000356692479491626},
|
|
||||||
{ 0.000349316846854190, 0.000326162940234916},
|
|
||||||
{-0.000558465829929114, -0.000048001521408724},
|
|
||||||
{ 0.000488176200631416, -0.000319593757302922},
|
|
||||||
{-0.000169437838021935, 0.000501610900725908},
|
|
||||||
{-0.000131793335799502, -0.000373003580727547},
|
|
||||||
{ 0.000166817395492786, 0.000105930895534474},
|
|
||||||
{ 0.000030499908326112, -0.000003048682668943},
|
|
||||||
{-0.000174999505027919, 0.000168008090089458},
|
|
||||||
{ 0.000054431163395030, -0.000385174790951272},
|
|
||||||
{ 0.000215876012859739, 0.000372695852521209},
|
|
||||||
{-0.000325534912280750, -0.000130173041693966},
|
|
||||||
{ 0.000154951430569290, -0.000045395998708328},
|
|
||||||
{ 0.000054324657659002, -0.000076028700470037},
|
|
||||||
{ 0.000015664427565764, 0.000348002612845696},
|
|
||||||
{-0.000345943017888332, -0.000402175417043307},
|
|
||||||
{ 0.000568731727879741, 0.000112347863435682},
|
|
||||||
{-0.000416485880859085, 0.000211750352828909},
|
|
||||||
{ 0.000087462353623011, -0.000188197153014309},
|
|
||||||
{-0.000032082305030264, -0.000136804226080664},
|
|
||||||
{ 0.000379089999045955, 0.000303466839685362},
|
|
||||||
{-0.000726760198519770, -0.000007022279302816},
|
|
||||||
{ 0.000619888661818195, -0.000476871323359809},
|
|
||||||
{-0.000151885493742993, 0.000595641190573181},
|
|
||||||
{-0.000100626407015494, -0.000227947144491108},
|
|
||||||
{-0.000201935458823941, -0.000107628631934340},
|
|
||||||
{ 0.000680260922139900, -0.000120771182888852},
|
|
||||||
{-0.000666108629277491, 0.000744775901128973},
|
|
||||||
{ 0.000067236591919755, -0.001044125966364420},
|
|
||||||
{ 0.000447037274751822, 0.000651912509450913},
|
|
||||||
{-0.000262675893448686, -0.000082499729563337},
|
|
||||||
{-0.000349821460486320, 0.000132102793530818},
|
|
||||||
{ 0.000507024815168287, -0.000837598610490618},
|
|
||||||
{ 0.000163814255478652, 0.001346530693477834},
|
|
||||||
{-0.000970457632383793, -0.000968411010101160},
|
|
||||||
{ 0.000974834882891140, 0.000116507082762032},
|
|
||||||
{-0.000225464280571542, 0.000137131865995708},
|
|
||||||
{-0.000211542240694642, 0.000563783548428947},
|
|
||||||
{-0.000414412310798766, -0.001309793399193736},
|
|
||||||
{ 0.001497010004594478, 0.001021907858926259},
|
|
||||||
{-0.001752019159639658, 0.000116536066154131},
|
|
||||||
{ 0.000872822027879430, -0.000783952720205569},
|
|
||||||
{-0.000032439446797970, 0.000184988059956734},
|
|
||||||
{ 0.000446259382722895, 0.000833040920509238},
|
|
||||||
{-0.001741577737284306, -0.000764423771425237},
|
|
||||||
{ 0.002306569133792772, -0.000593352416441601},
|
|
||||||
{-0.001336084746214192, 0.001744394557524181},
|
|
||||||
{-0.000015810020735495, -0.001342809547658260},
|
|
||||||
{ 0.000007636494885364, 0.000009498318627546},
|
|
||||||
{ 0.001403876768349702, 0.000326101441888391},
|
|
||||||
{-0.002351020828600226, 0.001098649819278302},
|
|
||||||
{ 0.001389314639579544, -0.002746943712072884},
|
|
||||||
{ 0.000526319899588909, 0.002635084366837732},
|
|
||||||
{-0.001109526585744687, -0.000950323796527721},
|
|
||||||
{-0.000307792427984886, -0.000013203419520794},
|
|
||||||
{ 0.001737955094951111, -0.001247368808692850},
|
|
||||||
{-0.000974502437588420, 0.003352512117661680},
|
|
||||||
{-0.001462571137390936, -0.003635296917435679},
|
|
||||||
{ 0.002783459090201693, 0.001604420226187745},
|
|
||||||
{-0.001471518558760170, 0.000211117948702137},
|
|
||||||
{-0.000575340825070194, 0.000601820846100026},
|
|
||||||
{ 0.000302090333345692, -0.003088058972305493},
|
|
||||||
{ 0.002496092353182990, 0.003912508340989065},
|
|
||||||
{-0.004645661091012423, -0.001630427298020200},
|
|
||||||
{ 0.003556824805628799, -0.001209822327859352},
|
|
||||||
{-0.000744999556260706, 0.001143238699138109},
|
|
||||||
{ 0.000144278726929409, 0.001638049051599065},
|
|
||||||
{-0.003025291044450178, -0.003226370992887968},
|
|
||||||
{ 0.006047866290490120, 0.000927406808799887},
|
|
||||||
{-0.005338456415106141, 0.003008811999350399},
|
|
||||||
{ 0.001642959659014839, -0.003972384205231079},
|
|
||||||
{ 0.000273874932822212, 0.000977326273749033},
|
|
||||||
{ 0.002315022846573390, 0.001695671268241410},
|
|
||||||
{-0.006240953957978884, 0.000207330368698293},
|
|
||||||
{ 0.006164252120861735, -0.005177351717451013},
|
|
||||||
{-0.001560310257561104, 0.007437030759707700},
|
|
||||||
{-0.002131333814462852, -0.004317129694157112},
|
|
||||||
{ 0.000280518918541908, 0.000134405998842553},
|
|
||||||
{ 0.004612116481180659, -0.001024468120657814},
|
|
||||||
{-0.005599300279638699, 0.006828277067771868},
|
|
||||||
{ 0.000228879728552504, -0.010675998154712657},
|
|
||||||
{ 0.005692081512980654, 0.007582243186569848},
|
|
||||||
{-0.005100500569859509, -0.001364751685737153},
|
|
||||||
{-0.000902490398043454, 0.000385770160220703},
|
|
||||||
{ 0.003673858819546609, -0.006701685283451640},
|
|
||||||
{ 0.002079056046131593, 0.012568579063417429},
|
|
||||||
{-0.010730008156911677, -0.009826454574016218},
|
|
||||||
{ 0.012092401380903161, 0.000921764172237851},
|
|
||||||
{-0.004714530989129091, 0.003151948807627123},
|
|
||||||
{-0.001055930168838909, 0.003228576712467020},
|
|
||||||
{-0.004343270165991213, -0.011924332879354394},
|
|
||||||
{ 0.016499994418955999, 0.010255324919126899},
|
|
||||||
{-0.021047239750251585, 0.002309419513135448},
|
|
||||||
{ 0.011855513874047341, -0.011604071033866310},
|
|
||||||
{-0.000777842281358575, 0.005916341648175263},
|
|
||||||
{ 0.004380939277688377, 0.007397670455730446},
|
|
||||||
{-0.021891594662401131, -0.008509480947490166},
|
|
||||||
{ 0.032787638290674201, -0.009950745850861956},
|
|
||||||
{-0.021022579272463194, 0.030030850567389102},
|
|
||||||
{-0.001508145650189953, -0.027571914870304640},
|
|
||||||
{ 0.004056649693022923, 0.004624901687718579},
|
|
||||||
{ 0.025728742586666287, 0.004824671348397606},
|
|
||||||
{-0.058002700931665603, 0.030198618296813803},
|
|
||||||
{ 0.043631619628438784, -0.096308304333327280},
|
|
||||||
{ 0.033451363423624300, 0.136687079396426990},
|
|
||||||
{-0.129387018420204200, -0.101540513046619400},
|
|
||||||
{ 0.172881344826560730, -0.000000000000005297},
|
|
||||||
{-0.129387018420198010, 0.101540513046627330},
|
|
||||||
{ 0.033451363423615862, -0.136687079396429050},
|
|
||||||
{ 0.043631619628444723, 0.096308304333324601},
|
|
||||||
{-0.058002700931667456, -0.030198618296810247},
|
|
||||||
{ 0.025728742586665992, -0.004824671348399184},
|
|
||||||
{ 0.004056649693022639, -0.004624901687718827},
|
|
||||||
{-0.001508145650188251, 0.027571914870304734},
|
|
||||||
{-0.021022579272465047, -0.030030850567387805},
|
|
||||||
{ 0.032787638290674812, 0.009950745850859947},
|
|
||||||
{-0.021891594662400610, 0.008509480947491507},
|
|
||||||
{ 0.004380939277687923, -0.007397670455730714},
|
|
||||||
{-0.000777842281358940, -0.005916341648175215},
|
|
||||||
{ 0.011855513874048058, 0.011604071033865578},
|
|
||||||
{-0.021047239750251731, -0.002309419513134139},
|
|
||||||
{ 0.016499994418955360, -0.010255324919127926},
|
|
||||||
{-0.004343270165990471, 0.011924332879354665},
|
|
||||||
{-0.001055930168839110, -0.003228576712466955},
|
|
||||||
{-0.004714530989129287, -0.003151948807626830},
|
|
||||||
{ 0.012092401380903103, -0.000921764172238603},
|
|
||||||
{-0.010730008156911072, 0.009826454574016881},
|
|
||||||
{ 0.002079056046130817, -0.012568579063417559},
|
|
||||||
{ 0.003673858819547020, 0.006701685283451416},
|
|
||||||
{-0.000902490398043478, -0.000385770160220647},
|
|
||||||
{-0.005100500569859424, 0.001364751685737466},
|
|
||||||
{ 0.005692081512980187, -0.007582243186570198},
|
|
||||||
{ 0.000228879728553163, 0.010675998154712643},
|
|
||||||
{-0.005599300279639117, -0.006828277067771524},
|
|
||||||
{ 0.004612116481180722, 0.001024468120657532},
|
|
||||||
{ 0.000280518918541900, -0.000134405998842571},
|
|
||||||
{-0.002131333814462586, 0.004317129694157243},
|
|
||||||
{-0.001560310257561563, -0.007437030759707604},
|
|
||||||
{ 0.006164252120862052, 0.005177351717450635},
|
|
||||||
{-0.006240953957978898, -0.000207330368697911},
|
|
||||||
{ 0.002315022846573286, -0.001695671268241552},
|
|
||||||
{ 0.000273874932822152, -0.000977326273749050},
|
|
||||||
{ 0.001642959659015084, 0.003972384205230976},
|
|
||||||
{-0.005338456415106324, -0.003008811999350072},
|
|
||||||
{ 0.006047866290490063, -0.000927406808800258},
|
|
||||||
{-0.003025291044449980, 0.003226370992888153},
|
|
||||||
{ 0.000144278726929308, -0.001638049051599074},
|
|
||||||
{-0.000744999556260777, -0.001143238699138063},
|
|
||||||
{ 0.003556824805628873, 0.001209822327859134},
|
|
||||||
{-0.004645661091012323, 0.001630427298020484},
|
|
||||||
{ 0.002496092353182751, -0.003912508340989219},
|
|
||||||
{ 0.000302090333345882, 0.003088058972305475},
|
|
||||||
{-0.000575340825070231, -0.000601820846099991},
|
|
||||||
{-0.001471518558760183, -0.000211117948702046},
|
|
||||||
{ 0.002783459090201593, -0.001604420226187919},
|
|
||||||
{-0.001462571137390710, 0.003635296917435769},
|
|
||||||
{-0.000974502437588628, -0.003352512117661619},
|
|
||||||
{ 0.001737955094951189, 0.001247368808692742},
|
|
||||||
{-0.000307792427984885, 0.000013203419520814},
|
|
||||||
{-0.001109526585744628, 0.000950323796527789},
|
|
||||||
{ 0.000526319899588746, -0.002635084366837765},
|
|
||||||
{ 0.001389314639579712, 0.002746943712072799},
|
|
||||||
{-0.002351020828600294, -0.001098649819278158},
|
|
||||||
{ 0.001403876768349682, -0.000326101441888477},
|
|
||||||
{ 0.000007636494885364, -0.000009498318627546},
|
|
||||||
{-0.000015810020735412, 0.001342809547658261},
|
|
||||||
{-0.001336084746214299, -0.001744394557524099},
|
|
||||||
{ 0.002306569133792808, 0.000593352416441460},
|
|
||||||
{-0.001741577737284259, 0.000764423771425344},
|
|
||||||
{ 0.000446259382722843, -0.000833040920509266},
|
|
||||||
{-0.000032439446797982, -0.000184988059956732},
|
|
||||||
{ 0.000872822027879478, 0.000783952720205515},
|
|
||||||
{-0.001752019159639665, -0.000116536066154024},
|
|
||||||
{ 0.001497010004594416, -0.001021907858926351},
|
|
||||||
{-0.000414412310798685, 0.001309793399193761},
|
|
||||||
{-0.000211542240694677, -0.000563783548428934},
|
|
||||||
{-0.000225464280571550, -0.000137131865995694},
|
|
||||||
{ 0.000974834882891133, -0.000116507082762092},
|
|
||||||
{-0.000970457632383734, 0.000968411010101219},
|
|
||||||
{ 0.000163814255478569, -0.001346530693477844},
|
|
||||||
{ 0.000507024815168339, 0.000837598610490586},
|
|
||||||
{-0.000349821460486328, -0.000132102793530797},
|
|
||||||
{-0.000262675893448681, 0.000082499729563353},
|
|
||||||
{ 0.000447037274751782, -0.000651912509450940},
|
|
||||||
{ 0.000067236591919819, 0.001044125966364416},
|
|
||||||
{-0.000666108629277537, -0.000744775901128932},
|
|
||||||
{ 0.000680260922139908, 0.000120771182888810},
|
|
||||||
{-0.000201935458823935, 0.000107628631934352},
|
|
||||||
{-0.000100626407015480, 0.000227947144491114},
|
|
||||||
{-0.000151885493743030, -0.000595641190573172},
|
|
||||||
{ 0.000619888661818225, 0.000476871323359771},
|
|
||||||
{-0.000726760198519770, 0.000007022279302861},
|
|
||||||
{ 0.000379089999045936, -0.000303466839685386},
|
|
||||||
{-0.000032082305030256, 0.000136804226080666},
|
|
||||||
{ 0.000087462353623023, 0.000188197153014303},
|
|
||||||
{-0.000416485880859098, -0.000211750352828883},
|
|
||||||
{ 0.000568731727879734, -0.000112347863435717},
|
|
||||||
{-0.000345943017888307, 0.000402175417043329},
|
|
||||||
{ 0.000015664427565742, -0.000348002612845697},
|
|
||||||
{ 0.000054324657659007, 0.000076028700470034},
|
|
||||||
{ 0.000154951430569292, 0.000045395998708319},
|
|
||||||
{-0.000325534912280742, 0.000130173041693986},
|
|
||||||
{ 0.000215876012859716, -0.000372695852521222},
|
|
||||||
{ 0.000054431163395054, 0.000385174790951269},
|
|
||||||
{-0.000174999505027930, -0.000168008090089447},
|
|
||||||
{ 0.000030499908326113, 0.000003048682668941},
|
|
||||||
{ 0.000166817395492779, -0.000105930895534485},
|
|
||||||
{-0.000131793335799479, 0.000373003580727555},
|
|
||||||
{-0.000169437838021966, -0.000501610900725898},
|
|
||||||
{ 0.000488176200631435, 0.000319593757302892},
|
|
||||||
{-0.000558465829929111, 0.000048001521408758},
|
|
||||||
{ 0.000349316846854170, -0.000326162940234938},
|
|
||||||
{-0.000055467806072849, 0.000356692479491629},
|
|
||||||
{-0.000115428390169126, -0.000205816198882806},
|
|
||||||
{ 0.000114833396071144, 0.000047778708840601},
|
|
||||||
{-0.000038085228233508, 0.000010585909953741},
|
|
||||||
{-0.000008324593371228, 0.000011315001355977},
|
|
||||||
{-0.000001217597841650, -0.000039141482370942},
|
|
||||||
{ 0.000027694034878707, 0.000033114740542633},
|
|
||||||
{-0.000034177222729439, -0.000007245841504979},
|
|
||||||
{ 0.000020060944485413, -0.000009851315045204},
|
|
||||||
{-0.000005461290583903, 0.000011336784355656},
|
|
||||||
};
|
|
||||||
|
|
||||||
#define CHROMA_FIR_SIZE (sizeof(CHROMA_FIR)/sizeof(dsp::complex_t))
|
|
||||||
#define CHROMA_FIR_DELAY ((CHROMA_FIR_SIZE-1)/2)
|
|
||||||
131
decoder_modules/atv_decoder/src/filters.h
Normal file
131
decoder_modules/atv_decoder/src/filters.h
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/types.h>
|
||||||
|
|
||||||
|
const dsp::complex_t CHROMA_BANDPASS[123] = {
|
||||||
|
{ -0.000007675039564594f, -0.000017362992335168f },
|
||||||
|
{ 0.000050180791439308f, -0.000005054021864311f },
|
||||||
|
{ -0.000022529111707761f, 0.000102942513429095f },
|
||||||
|
{ -0.000157609487484146f, -0.000092618697641464f },
|
||||||
|
{ 0.000205649042029007f, -0.000181710515677257f },
|
||||||
|
{ 0.000143445458895462f, 0.000331994546004200f },
|
||||||
|
{ -0.000414693079508517f, 0.000038265188132615f },
|
||||||
|
{ 0.000090081630021837f, -0.000395731646002122f },
|
||||||
|
{ 0.000257705918065856f, 0.000154354504676150f },
|
||||||
|
{ -0.000064051192147575f, 0.000055648228186439f },
|
||||||
|
{ 0.000089938060647145f, 0.000213032074676941f },
|
||||||
|
{ -0.000604775098099200f, 0.000050706635726124f },
|
||||||
|
{ 0.000223309865890358f, -0.000944433958755193f },
|
||||||
|
{ 0.001049943574694384f, 0.000640863688898729f },
|
||||||
|
{ -0.000983491651119595f, 0.000840133365053179f },
|
||||||
|
{ -0.000417178588714773f, -0.001011686459999295f },
|
||||||
|
{ 0.000616677332283103f, -0.000046513429902547f },
|
||||||
|
{ 0.000018549463752019f, -0.000075619948809012f },
|
||||||
|
{ 0.000734408386201158f, 0.000456742966201638f },
|
||||||
|
{ -0.001192460562555901f, 0.001001510577200253f },
|
||||||
|
{ -0.000729137747758392f, -0.001811046261815935f },
|
||||||
|
{ 0.001878272869910273f, -0.000125879189667096f },
|
||||||
|
{ -0.000312873903977849f, 0.001230889889574772f },
|
||||||
|
{ -0.000142534831707354f, -0.000090307321579771f },
|
||||||
|
{ -0.000942796972567241f, 0.000778470227412111f },
|
||||||
|
{ -0.000945381510920278f, -0.002406055808135091f },
|
||||||
|
{ 0.003537159230775561f, -0.000207350791625892f },
|
||||||
|
{ -0.000956199555190230f, 0.003634225577771235f },
|
||||||
|
{ -0.002543835202533561f, -0.001641705037372486f },
|
||||||
|
{ 0.001064108471592447f, -0.000863770138941644f },
|
||||||
|
{ -0.000335799601479829f, -0.000876091753216939f },
|
||||||
|
{ 0.003390761989356699f, -0.000170321604912419f },
|
||||||
|
{ -0.001408130728751909f, 0.005175554625981795f },
|
||||||
|
{ -0.005203055300834108f, -0.003419861284250694f },
|
||||||
|
{ 0.004342719678657084f, -0.003465264906764298f },
|
||||||
|
{ 0.001143432997855297f, 0.003059520699490539f },
|
||||||
|
{ 0.000304096484476364f, -0.000012725974706621f },
|
||||||
|
{ -0.001193870642975282f, 0.004247469277548632f },
|
||||||
|
{ -0.006681021498855877f, -0.004471771356204969f },
|
||||||
|
{ 0.007965721969864534f, -0.006247895626072559f },
|
||||||
|
{ 0.003365883969059717f, 0.009241201835481184f },
|
||||||
|
{ -0.006835562188141396f, 0.000228798228738161f },
|
||||||
|
{ 0.000409900284971528f, -0.001412838961851673f },
|
||||||
|
{ -0.004331406608345981f, -0.002951876085350234f },
|
||||||
|
{ 0.009290089917766562f, -0.007161958719089258f },
|
||||||
|
{ 0.005418326020709935f, 0.015272361365960607f },
|
||||||
|
{ -0.017077565432843410f, 0.000428641984774326f },
|
||||||
|
{ 0.003850771342644978f, -0.012869517593577566f },
|
||||||
|
{ 0.004380859690202961f, 0.003039552423897447f },
|
||||||
|
{ 0.004761181766399753f, -0.003607421240356480f },
|
||||||
|
{ 0.005926935731028822f, 0.017160134858844222f },
|
||||||
|
{ -0.028153584885925551f, 0.000471042980325370f },
|
||||||
|
{ 0.009655944938035437f, -0.031314555422639050f },
|
||||||
|
{ 0.023930146568136038f, 0.016901617811072800f },
|
||||||
|
{ -0.012998853255109976f, 0.009678807314399702f },
|
||||||
|
{ 0.002043176559434885f, 0.006079907699564680f },
|
||||||
|
{ -0.036686455817128191f, 0.000306882557812233f },
|
||||||
|
{ 0.021529138474771701f, -0.067800343150283604f },
|
||||||
|
{ 0.085421344938160879f, 0.061409588050754214f },
|
||||||
|
{ -0.108166660998898100f, 0.079141989828113088f },
|
||||||
|
{ -0.047617308971534079f, -0.145721049254261960f },
|
||||||
|
{ 0.160079041453427080f, -0.000000000000000427f },
|
||||||
|
{ -0.047617308971533295f, 0.145721049254262240f },
|
||||||
|
{ -0.108166660998898530f, -0.079141989828112505f },
|
||||||
|
{ 0.085421344938160546f, -0.061409588050754672f },
|
||||||
|
{ 0.021529138474772065f, 0.067800343150283493f },
|
||||||
|
{ -0.036686455817128191f, -0.000306882557812037f },
|
||||||
|
{ 0.002043176559434853f, -0.006079907699564691f },
|
||||||
|
{ -0.012998853255110026f, -0.009678807314399631f },
|
||||||
|
{ 0.023930146568135951f, -0.016901617811072928f },
|
||||||
|
{ 0.009655944938035604f, 0.031314555422638994f },
|
||||||
|
{ -0.028153584885925554f, -0.000471042980325220f },
|
||||||
|
{ 0.005926935731028730f, -0.017160134858844253f },
|
||||||
|
{ 0.004761181766399772f, 0.003607421240356455f },
|
||||||
|
{ 0.004380859690202943f, -0.003039552423897470f },
|
||||||
|
{ 0.003850771342645046f, 0.012869517593577545f },
|
||||||
|
{ -0.017077565432843413f, -0.000428641984774235f },
|
||||||
|
{ 0.005418326020709854f, -0.015272361365960637f },
|
||||||
|
{ 0.009290089917766600f, 0.007161958719089209f },
|
||||||
|
{ -0.004331406608345964f, 0.002951876085350257f },
|
||||||
|
{ 0.000409900284971536f, 0.001412838961851670f },
|
||||||
|
{ -0.006835562188141398f, -0.000228798228738125f },
|
||||||
|
{ 0.003365883969059667f, -0.009241201835481201f },
|
||||||
|
{ 0.007965721969864567f, 0.006247895626072517f },
|
||||||
|
{ -0.006681021498855855f, 0.004471771356205005f },
|
||||||
|
{ -0.001193870642975304f, -0.004247469277548626f },
|
||||||
|
{ 0.000304096484476364f, 0.000012725974706619f },
|
||||||
|
{ 0.001143432997855281f, -0.003059520699490545f },
|
||||||
|
{ 0.004342719678657102f, 0.003465264906764274f },
|
||||||
|
{ -0.005203055300834089f, 0.003419861284250722f },
|
||||||
|
{ -0.001408130728751936f, -0.005175554625981787f },
|
||||||
|
{ 0.003390761989356700f, 0.000170321604912401f },
|
||||||
|
{ -0.000335799601479825f, 0.000876091753216940f },
|
||||||
|
{ 0.001064108471592452f, 0.000863770138941638f },
|
||||||
|
{ -0.002543835202533552f, 0.001641705037372499f },
|
||||||
|
{ -0.000956199555190250f, -0.003634225577771230f },
|
||||||
|
{ 0.003537159230775563f, 0.000207350791625874f },
|
||||||
|
{ -0.000945381510920265f, 0.002406055808135096f },
|
||||||
|
{ -0.000942796972567245f, -0.000778470227412106f },
|
||||||
|
{ -0.000142534831707354f, 0.000090307321579771f },
|
||||||
|
{ -0.000312873903977856f, -0.001230889889574770f },
|
||||||
|
{ 0.001878272869910274f, 0.000125879189667086f },
|
||||||
|
{ -0.000729137747758382f, 0.001811046261815939f },
|
||||||
|
{ -0.001192460562555906f, -0.001001510577200246f },
|
||||||
|
{ 0.000734408386201156f, -0.000456742966201642f },
|
||||||
|
{ 0.000018549463752019f, 0.000075619948809012f },
|
||||||
|
{ 0.000616677332283103f, 0.000046513429902543f },
|
||||||
|
{ -0.000417178588714767f, 0.001011686459999298f },
|
||||||
|
{ -0.000983491651119600f, -0.000840133365053174f },
|
||||||
|
{ 0.001049943574694380f, -0.000640863688898734f },
|
||||||
|
{ 0.000223309865890363f, 0.000944433958755192f },
|
||||||
|
{ -0.000604775098099200f, -0.000050706635726121f },
|
||||||
|
{ 0.000089938060647144f, -0.000213032074676941f },
|
||||||
|
{ -0.000064051192147576f, -0.000055648228186438f },
|
||||||
|
{ 0.000257705918065856f, -0.000154354504676151f },
|
||||||
|
{ 0.000090081630021839f, 0.000395731646002121f },
|
||||||
|
{ -0.000414693079508517f, -0.000038265188132613f },
|
||||||
|
{ 0.000143445458895461f, -0.000331994546004200f },
|
||||||
|
{ 0.000205649042029008f, 0.000181710515677256f },
|
||||||
|
{ -0.000157609487484145f, 0.000092618697641465f },
|
||||||
|
{ -0.000022529111707761f, -0.000102942513429094f },
|
||||||
|
{ 0.000050180791439308f, 0.000005054021864311f },
|
||||||
|
{ -0.000007675039564594f, 0.000017362992335168f }
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CHROMA_BANDPASS_SIZE (sizeof(CHROMA_BANDPASS)/sizeof(dsp::complex_t))
|
||||||
|
#define CHROMA_BANDPASS_DELAY (CHROMA_BANDPASS_SIZE/2)
|
||||||
@@ -5,6 +5,33 @@
|
|||||||
#include <dsp/multirate/polyphase_bank.h>
|
#include <dsp/multirate/polyphase_bank.h>
|
||||||
#include <dsp/math/step.h>
|
#include <dsp/math/step.h>
|
||||||
|
|
||||||
|
#define LINE_SIZE 945
|
||||||
|
|
||||||
|
#define SYNC_LEN 70
|
||||||
|
#define SYNC_SIDE_LEN 17
|
||||||
|
#define SYNC_L_START (LINE_SIZE - SYNC_SIDE_LEN)
|
||||||
|
#define SYNC_R_START (SYNC_LEN/2)
|
||||||
|
#define SYNC_R_END (SYNC_R_START + (SYNC_LEN/2) + SYNC_SIDE_LEN)
|
||||||
|
#define SYNC_HALF_LEN ((SYNC_LEN/2) + SYNC_SIDE_LEN)
|
||||||
|
|
||||||
|
#define EQUAL_LEN 35
|
||||||
|
|
||||||
|
#define HBLANK_START SYNC_LEN
|
||||||
|
#define HBLANK_END 155
|
||||||
|
#define HBLANK_LEN (HBLANK_END - HBLANK_START + 1)
|
||||||
|
|
||||||
|
#define SYNC_LEVEL (-0.428)
|
||||||
|
|
||||||
|
#define COLORBURST_START 84
|
||||||
|
#define COLORBURST_LEN 33
|
||||||
|
|
||||||
|
#define MAX_LOCK 1000
|
||||||
|
|
||||||
|
dsp::complex_t PHASE_REF[2] = {
|
||||||
|
{ -0.707106781186547f, 0.707106781186547f },
|
||||||
|
{ -0.707106781186547f, -0.707106781186547f }
|
||||||
|
};
|
||||||
|
|
||||||
class LineSync : public dsp::Processor<float, float> {
|
class LineSync : public dsp::Processor<float, float> {
|
||||||
using base_type = dsp::Processor<float, float>;
|
using base_type = dsp::Processor<float, float>;
|
||||||
public:
|
public:
|
||||||
@@ -27,41 +54,17 @@ public:
|
|||||||
_interpPhaseCount = interpPhaseCount;
|
_interpPhaseCount = interpPhaseCount;
|
||||||
_interpTapCount = interpTapCount;
|
_interpTapCount = interpTapCount;
|
||||||
|
|
||||||
pcl.init(_muGain, _omegaGain, 0.0, 0.0, 1.0, _omega, _omega * (1.0 - omegaRelLimit), _omega * (1.0 + omegaRelLimit));
|
|
||||||
generateInterpTaps();
|
generateInterpTaps();
|
||||||
buffer = dsp::buffer::alloc<float>(STREAM_BUFFER_SIZE + _interpTapCount);
|
buffer = dsp::buffer::alloc<float>(STREAM_BUFFER_SIZE + _interpTapCount);
|
||||||
bufStart = &buffer[_interpTapCount - 1];
|
bufStart = &buffer[_interpTapCount - 1];
|
||||||
|
|
||||||
|
// TODO: Needs tuning, so do the gains
|
||||||
|
maxPeriod = (int32_t)(1.0001 * (float)(1 << 30));
|
||||||
|
minPeriod = (int32_t)(0.9999 * (float)(1 << 30));
|
||||||
|
|
||||||
base_type::init(in);
|
base_type::init(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setOmegaGain(double omegaGain) {
|
|
||||||
assert(base_type::_block_init);
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
|
||||||
_omegaGain = omegaGain;
|
|
||||||
pcl.setCoefficients(_muGain, _omegaGain);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMuGain(double muGain) {
|
|
||||||
assert(base_type::_block_init);
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
|
||||||
_muGain = muGain;
|
|
||||||
pcl.setCoefficients(_muGain, _omegaGain);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setOmegaRelLimit(double omegaRelLimit) {
|
|
||||||
assert(base_type::_block_init);
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
|
||||||
_omegaRelLimit = omegaRelLimit;
|
|
||||||
pcl.setFreqLimits(_omega * (1.0 - _omegaRelLimit), _omega * (1.0 + _omegaRelLimit));
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSyncLevel(float level) {
|
|
||||||
assert(base_type::_block_init);
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
|
||||||
syncLevel = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setInterpParams(int interpPhaseCount, int interpTapCount) {
|
void setInterpParams(int interpPhaseCount, int interpTapCount) {
|
||||||
assert(base_type::_block_init);
|
assert(base_type::_block_init);
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
@@ -81,8 +84,7 @@ public:
|
|||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
base_type::tempStop();
|
base_type::tempStop();
|
||||||
offset = 0;
|
offset = 0;
|
||||||
pcl.phase = 0.0f;
|
phase = 0;
|
||||||
pcl.freq = _omega;
|
|
||||||
base_type::tempStart();
|
base_type::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,61 +95,120 @@ public:
|
|||||||
// Copy data to work buffer
|
// Copy data to work buffer
|
||||||
memcpy(bufStart, base_type::_in->readBuf, count * sizeof(float));
|
memcpy(bufStart, base_type::_in->readBuf, count * sizeof(float));
|
||||||
|
|
||||||
if (test2) {
|
// Process samples while they are available
|
||||||
test2 = false;
|
|
||||||
offset += 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process all samples
|
|
||||||
while (offset < count) {
|
while (offset < count) {
|
||||||
// Calculate new output value
|
// While the offset is negative, out put zeros
|
||||||
int phase = std::clamp<int>(floorf(pcl.phase * (float)_interpPhaseCount), 0, _interpPhaseCount - 1);
|
while (offset < 0 && pixel < LINE_SIZE) {
|
||||||
float outVal;
|
// Output a zero
|
||||||
volk_32f_x2_dot_prod_32f(&outVal, &buffer[offset], interpBank.phases[phase], _interpTapCount);
|
base_type::out.writeBuf[pixel++] = 0.0f;
|
||||||
base_type::out.writeBuf[outCount++] = outVal;
|
|
||||||
|
|
||||||
// If the end of the line is reached, process it and determin error
|
// Increment the phase
|
||||||
float error = 0;
|
phase += period;
|
||||||
if (outCount >= 720) {
|
offset += (phase >> 30);
|
||||||
// Compute averages.
|
phase &= 0x3FFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process as much of a line as possible
|
||||||
|
while (offset < count && pixel < LINE_SIZE) {
|
||||||
|
// Compute the output sample
|
||||||
|
volk_32f_x2_dot_prod_32f(&base_type::out.writeBuf[pixel++], &buffer[offset], interpBank.phases[(phase >> 23) & 0x7F], _interpTapCount);
|
||||||
|
|
||||||
|
// Increment the phase
|
||||||
|
phase += period;
|
||||||
|
offset += (phase >> 30);
|
||||||
|
phase &= 0x3FFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the line is done, process it
|
||||||
|
if (pixel == LINE_SIZE) {
|
||||||
|
// Compute averages. (TODO: Try faster method)
|
||||||
float left = 0.0f, right = 0.0f;
|
float left = 0.0f, right = 0.0f;
|
||||||
for (int i = (720-17); i < 720; i++) {
|
int lc = 0, rc = 0;
|
||||||
|
for (int i = SYNC_L_START; i < LINE_SIZE; i++) {
|
||||||
left += base_type::out.writeBuf[i];
|
left += base_type::out.writeBuf[i];
|
||||||
|
lc++;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 27; i++) {
|
for (int i = 0; i < SYNC_R_START; i++) {
|
||||||
left += base_type::out.writeBuf[i];
|
left += base_type::out.writeBuf[i];
|
||||||
|
lc++;
|
||||||
}
|
}
|
||||||
for (int i = 27; i < (54+17); i++) {
|
for (int i = SYNC_R_START; i < SYNC_R_END; i++) {
|
||||||
right += base_type::out.writeBuf[i];
|
right += base_type::out.writeBuf[i];
|
||||||
}
|
rc++;
|
||||||
left *= (1.0f/44.0f);
|
|
||||||
right *= (1.0f/44.0f);
|
|
||||||
|
|
||||||
// If the sync is present, compute error
|
|
||||||
if ((left < syncLevel && right < syncLevel) && !forceLock) {
|
|
||||||
error = (left + syncBias - right);
|
|
||||||
locked = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
locked = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++counter >= 100) {
|
// Compute the error
|
||||||
counter = 0;
|
float error = (left - right) * (1.0f/((float)SYNC_HALF_LEN));
|
||||||
//flog::warn("Left: {}, Right: {}, Error: {}, Freq: {}, Phase: {}", left, right, error, pcl.freq, pcl.phase);
|
|
||||||
|
// Compute the change in phase and frequency due to the error
|
||||||
|
float periodDelta = error * _omegaGain;
|
||||||
|
float phaseDelta = error * _muGain;
|
||||||
|
|
||||||
|
// Normalize the phase delta (TODO: Make faster)
|
||||||
|
while (phaseDelta <= -1.0f) {
|
||||||
|
phaseDelta += 1.0f;
|
||||||
|
offset--;
|
||||||
|
}
|
||||||
|
while (phaseDelta >= 1.0f) {
|
||||||
|
phaseDelta -= 1.0f;
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the period (TODO: Clamp error*omegaGain to prevent weird shit with corrupt samples)
|
||||||
|
period += (int32_t)(periodDelta * (float)(1 << 30));
|
||||||
|
period = std::clamp<uint32_t>(period, minPeriod, maxPeriod);
|
||||||
|
|
||||||
|
// Update the phase
|
||||||
|
phase += (int32_t)(phaseDelta * (float)(1 << 30));
|
||||||
|
|
||||||
|
// Normalize the phase
|
||||||
|
uint32_t overflow = phase >> 30;
|
||||||
|
if (overflow) {
|
||||||
|
if (error < 0) {
|
||||||
|
offset -= 4 - overflow;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
offset += overflow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
phase &= 0x3FFFFFFF;
|
||||||
|
|
||||||
|
// Find the lowest value
|
||||||
|
float lowest = INFINITY;
|
||||||
|
int lowestId = -1;
|
||||||
|
for (int i = 0; i < LINE_SIZE; i++) {
|
||||||
|
float val = base_type::out.writeBuf[i];
|
||||||
|
if (val < lowest) {
|
||||||
|
lowest = val;
|
||||||
|
lowestId = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the the line is in lock
|
||||||
|
bool lineLocked = (lowestId < SYNC_R_END || lowestId >= SYNC_L_START);
|
||||||
|
|
||||||
|
// Update the lock status based on the line lock
|
||||||
|
if (!lineLocked && locked) {
|
||||||
|
locked--;
|
||||||
|
}
|
||||||
|
else if (lineLocked && locked < MAX_LOCK) {
|
||||||
|
locked++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not locked, attempt to lock by forcing the sync to happen at the right spot
|
||||||
|
// TODO: This triggers waaaay too easily at low SNR
|
||||||
|
if (!locked && fastLock) {
|
||||||
|
offset += lowestId - SYNC_R_START;
|
||||||
|
locked = MAX_LOCK / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output line
|
// Output line
|
||||||
if (!base_type::out.swap(outCount)) { break; }
|
if (!base_type::out.swap(LINE_SIZE)) { break; }
|
||||||
outCount = 0;
|
pixel = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance symbol offset and phase
|
|
||||||
pcl.advance(error);
|
|
||||||
float delta = floorf(pcl.phase);
|
|
||||||
offset += delta;
|
|
||||||
pcl.phase -= delta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the offset ready for the next buffer
|
||||||
offset -= count;
|
offset -= count;
|
||||||
|
|
||||||
// Update delay buffer
|
// Update delay buffer
|
||||||
@@ -155,16 +216,15 @@ public:
|
|||||||
|
|
||||||
// Swap if some data was generated
|
// Swap if some data was generated
|
||||||
base_type::_in->flush();
|
base_type::_in->flush();
|
||||||
return outCount;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool locked = false;
|
float syncBias = 0;
|
||||||
bool test2 = false;
|
|
||||||
|
|
||||||
float syncBias = 0.0f;
|
uint32_t period = (0x800072F3 >> 1);//(1 << 31) + 1;
|
||||||
bool forceLock = false;
|
|
||||||
|
|
||||||
int counter = 0;
|
int locked = 0;
|
||||||
|
bool fastLock = true;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void generateInterpTaps() {
|
void generateInterpTaps() {
|
||||||
@@ -175,7 +235,6 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
dsp::multirate::PolyphaseBank<float> interpBank;
|
dsp::multirate::PolyphaseBank<float> interpBank;
|
||||||
dsp::loop::PhaseControlLoop<double, false> pcl;
|
|
||||||
|
|
||||||
double _omega;
|
double _omega;
|
||||||
double _omegaGain;
|
double _omegaGain;
|
||||||
@@ -183,11 +242,14 @@ protected:
|
|||||||
double _omegaRelLimit;
|
double _omegaRelLimit;
|
||||||
int _interpPhaseCount;
|
int _interpPhaseCount;
|
||||||
int _interpTapCount;
|
int _interpTapCount;
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
int outCount = 0;
|
|
||||||
float* buffer;
|
float* buffer;
|
||||||
float* bufStart;
|
float* bufStart;
|
||||||
|
|
||||||
|
uint32_t phase = 0;
|
||||||
|
uint32_t maxPeriod;
|
||||||
|
uint32_t minPeriod;
|
||||||
float syncLevel = -0.03f;
|
float syncLevel = -0.03f;
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
int pixel = 0;
|
||||||
};
|
};
|
||||||
@@ -16,9 +16,13 @@
|
|||||||
#include <dsp/filter/fir.h>
|
#include <dsp/filter/fir.h>
|
||||||
#include <dsp/taps/from_array.h>
|
#include <dsp/taps/from_array.h>
|
||||||
|
|
||||||
#include "chrominance_filter.h"
|
#include "amplitude.h"
|
||||||
|
#include <dsp/demod/am.h>
|
||||||
|
#include <dsp/loop/fast_agc.h>
|
||||||
|
|
||||||
#include "chroma_pll.h"
|
#include "filters.h"
|
||||||
|
#include <dsp/math/normalize_phase.h>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
@@ -29,24 +33,25 @@ SDRPP_MOD_INFO{/* Name: */ "atv_decoder",
|
|||||||
/* Max instances */ -1
|
/* Max instances */ -1
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SAMPLE_RATE (625.0f * 720.0f * 25.0f)
|
#define SAMPLE_RATE (625.0f * (float)LINE_SIZE * 25.0f)
|
||||||
|
|
||||||
class ATVDecoderModule : public ModuleManager::Instance {
|
class ATVDecoderModule : public ModuleManager::Instance {
|
||||||
public:
|
public:
|
||||||
ATVDecoderModule(std::string name) : img(720, 625) {
|
ATVDecoderModule(std::string name) : img(768, 576) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
|
|
||||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 8000000.0f, SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE, true);
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 7000000.0f, SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE, true);
|
||||||
|
|
||||||
demod.init(vfo->output, SAMPLE_RATE, SAMPLE_RATE / 2.0f);
|
agc.init(vfo->output, 1.0f, 1e6, 0.001f, 1.0f);
|
||||||
|
demod.init(&agc.out, SAMPLE_RATE, SAMPLE_RATE / 2.0f);
|
||||||
|
// demod.init(vfo->output, dsp::demod::AM<float>::CARRIER, 8000000.0f, 50.0 / SAMPLE_RATE, 50.0 / SAMPLE_RATE, 0.0f, SAMPLE_RATE);
|
||||||
|
// demod.init(vfo->output, SAMPLE_RATE, SAMPLE_RATE / 2.0f);
|
||||||
sync.init(&demod.out, 1.0f, 1e-6, 1.0, 0.05);
|
sync.init(&demod.out, 1.0f, 1e-6, 1.0, 0.05);
|
||||||
sink.init(&sync.out, handler, this);
|
sink.init(&sync.out, handler, this);
|
||||||
|
|
||||||
r2c.init(NULL);
|
r2c.init(NULL);
|
||||||
chromaTaps = dsp::taps::fromArray(CHROMA_FIR_SIZE, CHROMA_FIR);
|
|
||||||
fir.init(NULL, chromaTaps);
|
|
||||||
pll.init(NULL, 0.01, 0.0, dsp::math::hzToRads(4433618.75, SAMPLE_RATE), dsp::math::hzToRads(4433618.75*0.90, SAMPLE_RATE), dsp::math::hzToRads(4433618.75*1.1, SAMPLE_RATE));
|
|
||||||
|
|
||||||
|
agc.start();
|
||||||
demod.start();
|
demod.start();
|
||||||
sync.start();
|
sync.start();
|
||||||
sink.start();
|
sink.start();
|
||||||
@@ -58,7 +63,10 @@ class ATVDecoderModule : public ModuleManager::Instance {
|
|||||||
if (vfo) {
|
if (vfo) {
|
||||||
sigpath::vfoManager.deleteVFO(vfo);
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
}
|
}
|
||||||
|
agc.stop();
|
||||||
demod.stop();
|
demod.stop();
|
||||||
|
sync.stop();
|
||||||
|
sink.stop();
|
||||||
gui::menu.removeEntry(name);
|
gui::menu.removeEntry(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,142 +90,218 @@ class ATVDecoderModule : public ModuleManager::Instance {
|
|||||||
style::beginDisabled();
|
style::beginDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ideal width for testing: 750pixels
|
|
||||||
|
|
||||||
ImGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
_this->img.draw();
|
_this->img.draw();
|
||||||
|
|
||||||
ImGui::LeftLabel("Sync");
|
ImGui::TextUnformatted("Horizontal Sync:");
|
||||||
ImGui::FillWidth();
|
ImGui::SameLine();
|
||||||
ImGui::SliderFloat("##syncLvl", &_this->sync_level, -2, 2);
|
if (_this->sync.locked > 750) {
|
||||||
|
|
||||||
ImGui::LeftLabel("Min");
|
|
||||||
ImGui::FillWidth();
|
|
||||||
ImGui::SliderFloat("##minLvl", &_this->minLvl, -1.0, 1.0);
|
|
||||||
|
|
||||||
ImGui::LeftLabel("Span");
|
|
||||||
ImGui::FillWidth();
|
|
||||||
ImGui::SliderFloat("##spanLvl", &_this->spanLvl, 0, 1.0);
|
|
||||||
|
|
||||||
ImGui::LeftLabel("Sync Bias");
|
|
||||||
ImGui::FillWidth();
|
|
||||||
ImGui::SliderFloat("##syncBias", &_this->sync.syncBias,-0.1, 0.1);
|
|
||||||
|
|
||||||
if (ImGui::Button("Test2")) {
|
|
||||||
_this->sync.test2 = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::Button("Switch frame")) {
|
|
||||||
std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
|
|
||||||
_this->evenFrame = !_this->evenFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_this->sync.locked) {
|
|
||||||
ImGui::TextColored(ImVec4(0, 1, 0, 1), "Locked");
|
ImGui::TextColored(ImVec4(0, 1, 0, 1), "Locked");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ImGui::TextUnformatted("Not locked");
|
ImGui::TextUnformatted("Not locked");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Checkbox("Force Lock", &_this->sync.forceLock);
|
ImGui::TextUnformatted("Vertical Sync:");
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (_this->vlock > 15) {
|
||||||
|
ImGui::TextColored(ImVec4(0, 1, 0, 1), "Locked");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::TextUnformatted("Not locked");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Checkbox("Fast Lock", &_this->sync.fastLock);
|
||||||
|
ImGui::Checkbox("Color Mode", &_this->colorMode);
|
||||||
|
|
||||||
if (!_this->enabled) {
|
if (!_this->enabled) {
|
||||||
style::endDisabled();
|
style::endDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::Text("Gain: %f", _this->gain);
|
||||||
|
ImGui::Text("Offset: %f", _this->offset);
|
||||||
|
ImGui::Text("Subcarrier: %f", _this->subcarrierFreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t pp = 0;
|
||||||
|
|
||||||
static void handler(float *data, int count, void *ctx) {
|
static void handler(float *data, int count, void *ctx) {
|
||||||
ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
|
ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
|
||||||
|
|
||||||
// Convert line to complex
|
// Correct the offset
|
||||||
_this->r2c.process(720, data, _this->r2c.out.writeBuf);
|
#if VOLK_VERSION_MAJOR > 2 || (VOLK_VERSION_MAJOR == 2 && VOLK_VERSION_MINOR >= 3)
|
||||||
|
volk_32f_s32f_add_32f(data, data, _this->offset, count);
|
||||||
// Isolate the chroma subcarrier
|
#else
|
||||||
_this->fir.process(720, _this->r2c.out.writeBuf, _this->fir.out.writeBuf);
|
const float ofs = _this->offset;
|
||||||
|
|
||||||
// Run chroma carrier through the PLL
|
|
||||||
_this->pll.process(720, _this->fir.out.writeBuf, _this->pll.out.writeBuf, ((_this->ypos%2)==1) ^ _this->evenFrame);
|
|
||||||
|
|
||||||
// Render line to the image without color
|
|
||||||
int lypos = _this->ypos - 1;
|
|
||||||
if (lypos < 0) { lypos = 624; }
|
|
||||||
uint32_t* lastLine = &((uint32_t *)_this->img.buffer)[(lypos < 313) ? (lypos*720*2) : ((((lypos - 313)*2)+1)*720) ];
|
|
||||||
uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[(_this->ypos < 313) ? (_this->ypos*720*2) : ((((_this->ypos - 313)*2)+1)*720) ];
|
|
||||||
|
|
||||||
//uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[_this->ypos*720];
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
int imval = std::clamp<float>((data[i] - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255);
|
data[i] += ofs;
|
||||||
// uint32_t re = std::clamp<float>((_this->pll.out.writeBuf[i].re - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255);
|
}
|
||||||
// uint32_t im = std::clamp<float>((_this->pll.out.writeBuf[i].im - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255);
|
#endif
|
||||||
// currentLine[i] = 0xFF000000 | (im << 8) | re;
|
|
||||||
currentLine[i] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
|
// Correct the gain
|
||||||
|
volk_32f_s32f_multiply_32f(data, data, _this->gain, count);
|
||||||
|
|
||||||
|
// Compute the sync levels
|
||||||
|
float syncLLevel = 0.0f;
|
||||||
|
float syncRLevel = 0.0f;
|
||||||
|
volk_32f_accumulator_s32f(&syncLLevel, data, EQUAL_LEN);
|
||||||
|
volk_32f_accumulator_s32f(&syncRLevel, &data[EQUAL_LEN], SYNC_LEN - EQUAL_LEN);
|
||||||
|
syncLLevel *= 1.0f / EQUAL_LEN;
|
||||||
|
syncRLevel *= 1.0f / (SYNC_LEN - EQUAL_LEN);
|
||||||
|
float syncLevel = (syncLLevel + syncRLevel) * 0.5f; // TODO: It's technically correct but if the sizes were different it wouldn't be
|
||||||
|
|
||||||
|
// Compute the blanking level
|
||||||
|
float blankLevel = 0.0f;
|
||||||
|
volk_32f_accumulator_s32f(&blankLevel, &data[HBLANK_START], HBLANK_LEN);
|
||||||
|
blankLevel /= (float)HBLANK_LEN;
|
||||||
|
|
||||||
|
// Run the offset control loop
|
||||||
|
_this->offset -= (blankLevel / _this->gain)*0.001;
|
||||||
|
_this->offset = std::clamp<float>(_this->offset, -1.0f, 1.0f);
|
||||||
|
_this->gain -= (blankLevel - syncLevel + SYNC_LEVEL)*0.01f;
|
||||||
|
_this->gain = std::clamp<float>(_this->gain, 0.1f, 10.0f);
|
||||||
|
|
||||||
|
// Detect the sync type
|
||||||
|
uint16_t shortSync = (syncLLevel < 0.5f*SYNC_LEVEL) && (syncRLevel > 0.5f*SYNC_LEVEL) && (blankLevel > 0.5f*SYNC_LEVEL);
|
||||||
|
uint16_t longSync = (syncLLevel < 0.5f*SYNC_LEVEL) && (syncRLevel < 0.5f*SYNC_LEVEL) && (blankLevel < 0.5f*SYNC_LEVEL);
|
||||||
|
|
||||||
|
// Save sync type to history
|
||||||
|
_this->syncHistory = (_this->syncHistory << 2) | (longSync << 1) | shortSync;
|
||||||
|
|
||||||
|
// // If the line has a colorburst, decode it
|
||||||
|
// dsp::complex_t* buf1 = _this->r2c.out.readBuf;
|
||||||
|
// dsp::complex_t* buf2 = _this->r2c.out.writeBuf;
|
||||||
|
// if (true) {
|
||||||
|
// // Convert the line into complex
|
||||||
|
// _this->r2c.process(count, data, buf1);
|
||||||
|
|
||||||
|
// // Extract the chroma subcarrier (TODO: Optimise by running only where needed)
|
||||||
|
// for (int i = COLORBURST_START; i < count-(CHROMA_BANDPASS_DELAY+1); i++) {
|
||||||
|
// volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&buf2[i], (lv_32fc_t*)&buf1[i - CHROMA_BANDPASS_DELAY], (lv_32fc_t*)CHROMA_BANDPASS, CHROMA_BANDPASS_SIZE);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Down convert the chroma subcarrier (TODO: Optimise by running only where needed)
|
||||||
|
// lv_32fc_t startPhase = { 1.0f, 0.0f };
|
||||||
|
// lv_32fc_t phaseDelta = { sinf(_this->subcarrierFreq), cosf(_this->subcarrierFreq) };
|
||||||
|
// #if VOLK_VERSION >= 030100
|
||||||
|
// volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], &phaseDelta, &startPhase, count - COLORBURST_START);
|
||||||
|
// #else
|
||||||
|
// volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], phaseDelta, &startPhase, count - COLORBURST_START);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// // Compute the phase of the burst
|
||||||
|
// dsp::complex_t burstAvg = { 0.0f, 0.0f };
|
||||||
|
// volk_32fc_accumulator_s32fc((lv_32fc_t*)&burstAvg, (lv_32fc_t*)&buf2[COLORBURST_START], COLORBURST_LEN);
|
||||||
|
// float burstAmp = burstAvg.amplitude();
|
||||||
|
// if (burstAmp*(1.0f/(float)COLORBURST_LEN) < 0.02f) {
|
||||||
|
// printf("%d\n", _this->line);
|
||||||
|
// }
|
||||||
|
// burstAvg *= (1.0f / (burstAmp*burstAmp));
|
||||||
|
// burstAvg = burstAvg.conj();
|
||||||
|
|
||||||
|
// // Normalize the chroma data (TODO: Optimise by running only where needed)
|
||||||
|
// volk_32fc_s32fc_multiply_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], *((lv_32fc_t*)&burstAvg), count - COLORBURST_START);
|
||||||
|
|
||||||
|
// // Compute the frequency error of the burst
|
||||||
|
// float phase = buf2[COLORBURST_START].phase();
|
||||||
|
// float error = 0.0f;
|
||||||
|
// for (int i = COLORBURST_START+1; i < COLORBURST_START+COLORBURST_LEN; i++) {
|
||||||
|
// float cphase = buf2[i].phase();
|
||||||
|
// error += dsp::math::normalizePhase(cphase - phase);
|
||||||
|
// phase = cphase;
|
||||||
|
// }
|
||||||
|
// error *= (1.0f / (float)(COLORBURST_LEN-1));
|
||||||
|
|
||||||
|
// // Update the subcarrier freq
|
||||||
|
// _this->subcarrierFreq += error*0.0001f;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Render the line if it's visible
|
||||||
|
if (_this->ypos >= 34 && _this->ypos <= 34+576-1) {
|
||||||
|
uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[(_this->ypos - 34)*768];
|
||||||
|
if (_this->colorMode) {
|
||||||
|
// for (int i = 155; i < (155+768); i++) {
|
||||||
|
// int imval1 = std::clamp<float>(fabsf(buf2[i-155+COLORBURST_START].re*5.0f) * 255.0f, 0, 255);
|
||||||
|
// int imval2 = std::clamp<float>(fabsf(buf2[i-155+COLORBURST_START].im*5.0f) * 255.0f, 0, 255);
|
||||||
|
// currentLine[i-155] = 0xFF000000 | (imval2 << 8) | imval1;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (int i = 155; i < (155+768); i++) {
|
||||||
|
int imval = std::clamp<float>(data[i] * 255.0f, 0, 255);
|
||||||
|
currentLine[i-155] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertical scan logic
|
// Compute whether to rollover
|
||||||
_this->ypos++;
|
bool rollToOdd = (_this->ypos == 624);
|
||||||
bool rollover = _this->ypos >= 625;
|
bool rollToEven = (_this->ypos == 623);
|
||||||
if (rollover) {
|
|
||||||
{
|
// Compute the field sync
|
||||||
std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
|
bool syncToOdd = (_this->syncHistory == 0b0101011010010101);
|
||||||
_this->evenFrame = !_this->evenFrame;
|
bool syncToEven = (_this->syncHistory == 0b0001011010100101);
|
||||||
|
|
||||||
|
// Process the sync (NOTE: should start with 0b01, but for some reason I don't see a sync?)
|
||||||
|
if (rollToOdd || syncToOdd) {
|
||||||
|
// Update the vertical lock state
|
||||||
|
bool disagree = (rollToOdd ^ syncToOdd);
|
||||||
|
if (disagree && _this->vlock > 0) {
|
||||||
|
_this->vlock--;
|
||||||
}
|
}
|
||||||
|
else if (!disagree && _this->vlock < 20) {
|
||||||
|
_this->vlock++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the odd field
|
||||||
|
_this->ypos = 1;
|
||||||
|
_this->line++;
|
||||||
|
}
|
||||||
|
else if (rollToEven || syncToEven) {
|
||||||
|
// Update the vertical lock state
|
||||||
|
bool disagree = (rollToEven ^ syncToEven);
|
||||||
|
if (disagree && _this->vlock > 0) {
|
||||||
|
_this->vlock--;
|
||||||
|
}
|
||||||
|
else if (!disagree && _this->vlock < 20) {
|
||||||
|
_this->vlock++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the even field
|
||||||
_this->ypos = 0;
|
_this->ypos = 0;
|
||||||
|
_this->line = 0;
|
||||||
|
|
||||||
|
// Swap the video buffer
|
||||||
_this->img.swap();
|
_this->img.swap();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
// Measure vsync levels
|
_this->ypos += 2;
|
||||||
float sync0 = 0.0f, sync1 = 0.0f;
|
_this->line++;
|
||||||
for (int i = 0; i < 306; i++) {
|
|
||||||
sync0 += data[i];
|
|
||||||
}
|
|
||||||
for (int i = (720/2); i < ((720/2)+306); i++) {
|
|
||||||
sync1 += data[i];
|
|
||||||
}
|
|
||||||
sync0 *= (1.0f/305.0f);
|
|
||||||
sync1 *= (1.0f/305.0f);
|
|
||||||
|
|
||||||
// Save sync detection to history
|
|
||||||
_this->syncHistory >>= 2;
|
|
||||||
_this->syncHistory |= (((uint16_t)(sync1 < _this->sync_level)) << 9) | (((uint16_t)(sync0 < _this->sync_level)) << 8);
|
|
||||||
|
|
||||||
// Trigger vsync in case one is detected
|
|
||||||
// TODO: Also sync with odd field
|
|
||||||
if (!rollover && _this->syncHistory == 0b0000011111) {
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
|
|
||||||
_this->evenFrame = !_this->evenFrame;
|
|
||||||
}
|
|
||||||
_this->ypos = 0;
|
|
||||||
_this->img.swap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEW SYNC:
|
||||||
|
float offset = 0.0f;
|
||||||
|
float gain = 1.0f;
|
||||||
|
uint16_t syncHistory = 0;
|
||||||
|
int line = 0;
|
||||||
|
int ypos = 0;
|
||||||
|
int vlock = 0;
|
||||||
|
float subcarrierFreq = 0.0f;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
|
|
||||||
VFOManager::VFO *vfo = NULL;
|
VFOManager::VFO *vfo = NULL;
|
||||||
dsp::demod::Quadrature demod;
|
// dsp::demod::Quadrature demod;
|
||||||
|
dsp::loop::FastAGC<dsp::complex_t> agc;
|
||||||
|
dsp::demod::Amplitude demod;
|
||||||
|
//dsp::demod::AM<float> demod;
|
||||||
LineSync sync;
|
LineSync sync;
|
||||||
dsp::sink::Handler<float> sink;
|
dsp::sink::Handler<float> sink;
|
||||||
dsp::convert::RealToComplex r2c;
|
dsp::convert::RealToComplex r2c;
|
||||||
dsp::tap<dsp::complex_t> chromaTaps;
|
|
||||||
dsp::filter::FIR<dsp::complex_t, dsp::complex_t> fir;
|
|
||||||
dsp::loop::ChromaPLL pll;
|
|
||||||
int ypos = 0;
|
|
||||||
|
|
||||||
bool evenFrame = false;
|
bool colorMode = false;
|
||||||
std::mutex evenFrameMtx;
|
|
||||||
|
|
||||||
float sync_level = -0.06f;
|
|
||||||
int sync_count = 0;
|
|
||||||
int short_sync = 0;
|
|
||||||
|
|
||||||
float minLvl = 0.0f;
|
|
||||||
float spanLvl = 1.0f;
|
|
||||||
|
|
||||||
bool lockedLines = 0;
|
|
||||||
uint16_t syncHistory = 0;
|
|
||||||
|
|
||||||
ImGui::ImageDisplay img;
|
ImGui::ImageDisplay img;
|
||||||
};
|
};
|
||||||
|
|||||||
37
decoder_modules/dab_decoder/CMakeLists.txt
Normal file
37
decoder_modules/dab_decoder/CMakeLists.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(dab_decoder)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
target_include_directories(dab_decoder PRIVATE "src/")
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Lib path
|
||||||
|
target_include_directories(dab_decoder PRIVATE "C:/Program Files/codec2/include/")
|
||||||
|
target_link_directories(dab_decoder PRIVATE "C:/Program Files/codec2/lib")
|
||||||
|
|
||||||
|
target_link_libraries(dab_decoder PRIVATE libcodec2)
|
||||||
|
elseif (ANDROID)
|
||||||
|
target_include_directories(dab_decoder PUBLIC
|
||||||
|
/sdr-kit/${ANDROID_ABI}/include/codec2
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(dab_decoder PUBLIC
|
||||||
|
/sdr-kit/${ANDROID_ABI}/lib/libcodec2.so
|
||||||
|
)
|
||||||
|
else ()
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBCODEC2 REQUIRED codec2)
|
||||||
|
|
||||||
|
target_include_directories(dab_decoder PRIVATE ${LIBCODEC2_INCLUDE_DIRS})
|
||||||
|
target_link_directories(dab_decoder PRIVATE ${LIBCODEC2_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(dab_decoder PRIVATE ${LIBCODEC2_LIBRARIES})
|
||||||
|
|
||||||
|
# Include it because for some reason pkgconfig doesn't look here?
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
target_include_directories(dab_decoder PRIVATE "/usr/local/include")
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
280
decoder_modules/dab_decoder/src/dab_dsp.h
Normal file
280
decoder_modules/dab_decoder/src/dab_dsp.h
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/processor.h>
|
||||||
|
#include <utils/flog.h>
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include "dab_phase_sym.h"
|
||||||
|
|
||||||
|
namespace dab {
|
||||||
|
class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||||
|
public:
|
||||||
|
CyclicSync() {}
|
||||||
|
|
||||||
|
// TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
|
||||||
|
CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
|
||||||
|
|
||||||
|
void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
|
||||||
|
// Computer the number of samples for the symbol and its cyclic prefix
|
||||||
|
symbolSamps = round(samplerate * symbolLength);
|
||||||
|
prefixSamps = round(samplerate * cyclicPrefixLength);
|
||||||
|
|
||||||
|
// Allocate and clear the delay buffer
|
||||||
|
delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
|
||||||
|
dsp::buffer::clear(delayBuf, symbolSamps);
|
||||||
|
|
||||||
|
// Allocate and clear the history buffer
|
||||||
|
histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
|
||||||
|
dsp::buffer::clear(histBuf, prefixSamps);
|
||||||
|
|
||||||
|
// Compute the delay input addresses
|
||||||
|
delayBufInput = &delayBuf[symbolSamps];
|
||||||
|
|
||||||
|
// Compute the correlation AGC configuration
|
||||||
|
this->agcRate = agcRate;
|
||||||
|
agcRateInv = 1.0f - agcRate;
|
||||||
|
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
base_type::tempStop();
|
||||||
|
|
||||||
|
base_type::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// Copy the data into the normal delay buffer
|
||||||
|
memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
// Flush the input stream
|
||||||
|
base_type::_in->flush();
|
||||||
|
|
||||||
|
// Do cross-correlation
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Get the current history slot
|
||||||
|
dsp::complex_t* slot = &histBuf[histId++];
|
||||||
|
|
||||||
|
// Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
|
||||||
|
histId %= prefixSamps;
|
||||||
|
|
||||||
|
// Kick out last value from the correlation
|
||||||
|
corr -= *slot;
|
||||||
|
|
||||||
|
// Save input value and compute the new prodct
|
||||||
|
dsp::complex_t val = delayBuf[i];
|
||||||
|
dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
|
||||||
|
|
||||||
|
// Add the new value to the correlation
|
||||||
|
*slot = prod;
|
||||||
|
|
||||||
|
// Add the new value to the history buffer
|
||||||
|
corr += prod;
|
||||||
|
|
||||||
|
// Compute sample amplitude
|
||||||
|
float rcorr = corr.amplitude();
|
||||||
|
|
||||||
|
// If a high enough peak is reached, reset the symbol counter
|
||||||
|
if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
|
||||||
|
peakCorr = rcorr;
|
||||||
|
peakLCorr = lastCorr;
|
||||||
|
samplesSincePeak = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the sample right after the peak, save it
|
||||||
|
if (samplesSincePeak == 1) {
|
||||||
|
peakRCorr = rcorr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the sample to the output
|
||||||
|
out.writeBuf[samplesSincePeak++] = val;
|
||||||
|
|
||||||
|
// If the end of the symbol is reached, send it off
|
||||||
|
if (samplesSincePeak >= symbolSamps) {
|
||||||
|
if (!out.swap(symbolSamps)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
samplesSincePeak = 0;
|
||||||
|
peakCorr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the average correlation
|
||||||
|
lastCorr = rcorr;
|
||||||
|
|
||||||
|
// Update the average correlation value
|
||||||
|
avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move unused data
|
||||||
|
memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int symbolSamps;
|
||||||
|
int prefixSamps;
|
||||||
|
|
||||||
|
int histId = 0;
|
||||||
|
dsp::complex_t* histBuf;
|
||||||
|
|
||||||
|
dsp::complex_t* delayBuf;
|
||||||
|
dsp::complex_t* delayBufInput;
|
||||||
|
|
||||||
|
dsp::complex_t corr = { 0.0f, 0.0f };
|
||||||
|
|
||||||
|
int samplesSincePeak = 0;
|
||||||
|
float lastCorr = 0.0f;
|
||||||
|
float peakCorr = 0.0f;
|
||||||
|
float peakLCorr = 0.0f;
|
||||||
|
float peakRCorr = 0.0f;
|
||||||
|
|
||||||
|
// Note only required for DAB
|
||||||
|
float avgCorr = 0.0f;
|
||||||
|
float agcRate;
|
||||||
|
float agcRateInv;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||||
|
public:
|
||||||
|
FrameFreqSync() {}
|
||||||
|
|
||||||
|
FrameFreqSync(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) { init(in, agcRate); }
|
||||||
|
|
||||||
|
void init(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) {
|
||||||
|
// Allocate buffers
|
||||||
|
amps = dsp::buffer::alloc<float>(2048);
|
||||||
|
conjRef = dsp::buffer::alloc<dsp::complex_t>(2048);
|
||||||
|
corrIn = (dsp::complex_t*)fftwf_alloc_complex(2048);
|
||||||
|
corrOut = (dsp::complex_t*)fftwf_alloc_complex(2048);
|
||||||
|
|
||||||
|
// Copy the phase reference
|
||||||
|
memcpy(conjRef, DAB_PHASE_SYM_CONJ, 2048 * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
// Plan the FFT computation
|
||||||
|
plan = fftwf_plan_dft_1d(2048, (fftwf_complex*)corrIn, (fftwf_complex*)corrOut, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||||
|
|
||||||
|
// Compute the correlation AGC configuration
|
||||||
|
this->agcRate = agcRate;
|
||||||
|
agcRateInv = 1.0f - agcRate;
|
||||||
|
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
base_type::tempStop();
|
||||||
|
|
||||||
|
base_type::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// Apply frequency shift
|
||||||
|
lv_32fc_t phase = lv_cmake(1.0f, 0.0f);
|
||||||
|
lv_32fc_t phaseDelta = lv_cmake(cos(offset), sin(offset));
|
||||||
|
#if VOLK_VERSION >= 030100
|
||||||
|
volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||||
|
#else
|
||||||
|
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Compute the amplitude amplitude of all samples
|
||||||
|
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)_in->readBuf, 2048);
|
||||||
|
|
||||||
|
// Compute the average signal level by adding up all values
|
||||||
|
float level = 0.0f;
|
||||||
|
volk_32f_accumulator_s32f(&level, amps, 2048);
|
||||||
|
|
||||||
|
// Detect a frame sync condition
|
||||||
|
if (level < avgLvl * 0.5f) {
|
||||||
|
// Reset symbol counter
|
||||||
|
sym = 1;
|
||||||
|
|
||||||
|
// Update the average level
|
||||||
|
avgLvl = agcRate*level + agcRateInv*avgLvl;
|
||||||
|
|
||||||
|
// Flush the input stream and return
|
||||||
|
base_type::_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the average level
|
||||||
|
avgLvl = agcRate*level + agcRateInv*avgLvl;
|
||||||
|
|
||||||
|
// Handle phase reference
|
||||||
|
if (sym == 1) {
|
||||||
|
// Output the symbols (DEBUG ONLY)
|
||||||
|
memcpy(corrIn, _in->readBuf, 2048 * sizeof(dsp::complex_t));
|
||||||
|
fftwf_execute(plan);
|
||||||
|
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
|
||||||
|
int outCount = 0;
|
||||||
|
dsp::complex_t pi4 = { cos(3.1415926535*0.25), sin(3.1415926535*0.25) };
|
||||||
|
for (int i = -767; i < 768; i++) {
|
||||||
|
if (!i) { continue; }
|
||||||
|
int cid0 = ((i-1) >= 0) ? (i-1) : 2048+(i-1);
|
||||||
|
int cid1 = (i >= 0) ? i : 2048+i;;
|
||||||
|
out.writeBuf[outCount++] = pi4 * (corrOut[cid1] * corrOut[cid0].conj()) * (1.0f/(amps[cid0]*amps[cid0]));
|
||||||
|
}
|
||||||
|
out.swap(outCount);
|
||||||
|
|
||||||
|
// Multiply the samples with the conjugated phase reference signal
|
||||||
|
volk_32fc_x2_multiply_32fc((lv_32fc_t*)corrIn, (lv_32fc_t*)_in->readBuf, (lv_32fc_t*)conjRef, 2048);
|
||||||
|
|
||||||
|
// Compute the FFT of the product
|
||||||
|
fftwf_execute(plan);
|
||||||
|
|
||||||
|
// Compute the amplitude of the bins
|
||||||
|
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
|
||||||
|
|
||||||
|
// Locate highest power bin
|
||||||
|
uint32_t peakId;
|
||||||
|
volk_32f_index_max_32u(&peakId, amps, 2048);
|
||||||
|
|
||||||
|
// Obtain the value of the bins next to the peak
|
||||||
|
float peakL = amps[(peakId + 2047) % 2048];
|
||||||
|
float peakR = amps[(peakId + 1) % 2048];
|
||||||
|
|
||||||
|
// Compute the integer frequency offset
|
||||||
|
float offInt = (peakId < 1024) ? (float)peakId : ((float)peakId - 2048.0f);
|
||||||
|
|
||||||
|
// Compute the frequency offset in rad/samp
|
||||||
|
float off = 3.1415926535f * (offInt + ((peakR - peakL) / (peakR + peakL))) * (1.0f / 1024.0f);
|
||||||
|
|
||||||
|
// Run control loop
|
||||||
|
offset -= 0.1f*off;
|
||||||
|
flog::debug("Offset: {} Hz, Error: {} Hz, Avg Level: {}", offset * (0.5f/3.1415926535f)*2.048e6, off * (0.5f/3.1415926535f)*2.048e6, avgLvl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment the symbol counter
|
||||||
|
sym++;
|
||||||
|
|
||||||
|
// Flush the input stream and return
|
||||||
|
base_type::_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
fftwf_plan plan;
|
||||||
|
|
||||||
|
float* amps;
|
||||||
|
dsp::complex_t* conjRef;
|
||||||
|
dsp::complex_t* corrIn;
|
||||||
|
dsp::complex_t* corrOut;
|
||||||
|
|
||||||
|
int sym;
|
||||||
|
float offset = 0.0f;
|
||||||
|
|
||||||
|
float avgLvl = 0.0f;
|
||||||
|
float agcRate;
|
||||||
|
float agcRateInv;
|
||||||
|
};
|
||||||
|
}
|
||||||
2053
decoder_modules/dab_decoder/src/dab_phase_sym.h
Normal file
2053
decoder_modules/dab_decoder/src/dab_phase_sym.h
Normal file
File diff suppressed because it is too large
Load Diff
163
decoder_modules/dab_decoder/src/main.cpp
Normal file
163
decoder_modules/dab_decoder/src/main.cpp
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <gui/style.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <dsp/stream.h>
|
||||||
|
#include <dsp/buffer/reshaper.h>
|
||||||
|
#include <dsp/multirate/rational_resampler.h>
|
||||||
|
#include <dsp/sink/handler_sink.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <chrono>
|
||||||
|
#include "dab_dsp.h"
|
||||||
|
#include <gui/widgets/constellation_diagram.h>
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "dab_decoder",
|
||||||
|
/* Description: */ "DAB/DAB+ Decoder for SDR++",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigManager config;
|
||||||
|
|
||||||
|
#define INPUT_SAMPLE_RATE 2.048e6
|
||||||
|
#define VFO_BANDWIDTH 1.6e6
|
||||||
|
|
||||||
|
class M17DecoderModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
M17DecoderModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
config.acquire();
|
||||||
|
|
||||||
|
config.release(true);
|
||||||
|
|
||||||
|
// Initialize VFO
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
|
||||||
|
vfo->setSnapInterval(250);
|
||||||
|
|
||||||
|
// Initialize DSP here
|
||||||
|
csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE);
|
||||||
|
ffsync.init(&csync.out);
|
||||||
|
ns.init(&ffsync.out, handler, this);
|
||||||
|
|
||||||
|
// Start DSO Here
|
||||||
|
csync.start();
|
||||||
|
ffsync.start();
|
||||||
|
ns.start();
|
||||||
|
|
||||||
|
gui::menu.registerEntry(name, menuHandler, this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~M17DecoderModule() {
|
||||||
|
gui::menu.removeEntry(name);
|
||||||
|
// Stop DSP Here
|
||||||
|
if (enabled) {
|
||||||
|
csync.stop();
|
||||||
|
ffsync.stop();
|
||||||
|
ns.stop();
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
sigpath::sinkManager.unregisterStream(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
double bw = gui::waterfall.getBandwidth();
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
|
||||||
|
vfo->setSnapInterval(250);
|
||||||
|
|
||||||
|
// Set Input of demod here
|
||||||
|
csync.setInput(vfo->output);
|
||||||
|
|
||||||
|
// Start DSP here
|
||||||
|
csync.start();
|
||||||
|
ffsync.start();
|
||||||
|
ns.start();
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
// Stop DSP here
|
||||||
|
csync.stop();
|
||||||
|
ffsync.stop();
|
||||||
|
ns.stop();
|
||||||
|
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
M17DecoderModule* _this = (M17DecoderModule*)ctx;
|
||||||
|
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::beginDisabled(); }
|
||||||
|
|
||||||
|
_this->constDiagram.draw();
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::endDisabled(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream file;
|
||||||
|
|
||||||
|
static void handler(dsp::complex_t* data, int count, void* ctx) {
|
||||||
|
M17DecoderModule* _this = (M17DecoderModule*)ctx;
|
||||||
|
//_this->file.write((char*)data, count * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
|
||||||
|
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
|
||||||
|
_this->constDiagram.releaseBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
|
||||||
|
dab::CyclicSync csync;
|
||||||
|
dab::FrameFreqSync ffsync;
|
||||||
|
dsp::sink::Handler<dsp::complex_t> ns;
|
||||||
|
|
||||||
|
ImGui::ConstellationDiagram constDiagram;
|
||||||
|
|
||||||
|
// DSP Chain
|
||||||
|
VFOManager::VFO* vfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
// Create default recording directory
|
||||||
|
json def = json({});
|
||||||
|
config.setPath(core::args["root"].s() + "/dab_decoder_config.json");
|
||||||
|
config.load(def);
|
||||||
|
config.enableAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new M17DecoderModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (M17DecoderModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
config.disableAutoSave();
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
34
decoder_modules/dab_decoder/src/optimized_algo.txt
Normal file
34
decoder_modules/dab_decoder/src/optimized_algo.txt
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
0123456789
|
||||||
|
--- ---
|
||||||
|
|
||||||
|
0*4
|
||||||
|
1*5
|
||||||
|
2*6
|
||||||
|
|
||||||
|
1*5
|
||||||
|
2*6 = L + 3*7 - 0*4
|
||||||
|
3*7
|
||||||
|
|
||||||
|
2*6
|
||||||
|
3*7 = L + 4*8 - 1*5
|
||||||
|
4*8
|
||||||
|
|
||||||
|
3*7
|
||||||
|
4*8 = L + 5*9 - 2*6
|
||||||
|
5*9
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
0*5
|
||||||
|
1*6
|
||||||
|
2*7
|
||||||
|
|
||||||
|
1*6
|
||||||
|
2*7
|
||||||
|
3*8
|
||||||
|
|
||||||
|
2*7
|
||||||
|
3*8
|
||||||
|
4*9
|
||||||
|
|
||||||
|
=> Use same technique to cache the interpolation results
|
||||||
8
decoder_modules/ryfi_decoder/CMakeLists.txt
Normal file
8
decoder_modules/ryfi_decoder/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(ryfi_decoder)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
target_include_directories(ryfi_decoder PRIVATE "src/")
|
||||||
139
decoder_modules/ryfi_decoder/src/main.cpp
Normal file
139
decoder_modules/ryfi_decoder/src/main.cpp
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <gui/style.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <dsp/routing/splitter.h>
|
||||||
|
#include <dsp/buffer/reshaper.h>
|
||||||
|
#include <dsp/sink/handler_sink.h>
|
||||||
|
#include <gui/widgets/folder_select.h>
|
||||||
|
#include <gui/widgets/constellation_diagram.h>
|
||||||
|
#include "ryfi/receiver.h"
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "ryfi_decoder",
|
||||||
|
/* Description: */ "RyFi decoder for SDR++",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
#define INPUT_BANDWIDTH 600e3
|
||||||
|
#define INPUT_SAMPLE_RATE 1000e3
|
||||||
|
#define INPUT_BAUDRATE 500e3
|
||||||
|
|
||||||
|
#define SYMBOL_DIAG_RATE 30
|
||||||
|
#define SYMBOL_DIAG_COUNT 1024
|
||||||
|
|
||||||
|
class RyFiDecoderModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
RyFiDecoderModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
|
||||||
|
rx.init(vfo->output, INPUT_BAUDRATE, INPUT_SAMPLE_RATE);
|
||||||
|
reshape.init(rx.softOut, SYMBOL_DIAG_COUNT, (INPUT_BAUDRATE / SYMBOL_DIAG_RATE) - SYMBOL_DIAG_COUNT);
|
||||||
|
symSink.init(&reshape.out, symSinkHandler, this);
|
||||||
|
rx.onPacket.bind(&RyFiDecoderModule::packetHandler, this);
|
||||||
|
|
||||||
|
rx.start();
|
||||||
|
reshape.start();
|
||||||
|
symSink.start();
|
||||||
|
|
||||||
|
gui::menu.registerEntry(name, menuHandler, this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RyFiDecoderModule() {
|
||||||
|
rx.stop();
|
||||||
|
reshape.stop();
|
||||||
|
symSink.stop();
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
gui::menu.removeEntry(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
double bw = gui::waterfall.getBandwidth();
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
|
||||||
|
|
||||||
|
rx.setInput(vfo->output);
|
||||||
|
|
||||||
|
rx.start();
|
||||||
|
reshape.start();
|
||||||
|
symSink.start();
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
rx.stop();
|
||||||
|
reshape.stop();
|
||||||
|
symSink.stop();
|
||||||
|
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void packetHandler(ryfi::Packet pkt) {
|
||||||
|
flog::debug("Got a {} byte packet!", pkt.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx;
|
||||||
|
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::beginDisabled(); }
|
||||||
|
|
||||||
|
ImGui::SetNextItemWidth(menuWidth);
|
||||||
|
_this->constDiagram.draw();
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::endDisabled(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void symSinkHandler(dsp::complex_t* data, int count, void* ctx) {
|
||||||
|
RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx;
|
||||||
|
|
||||||
|
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
|
||||||
|
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
|
||||||
|
_this->constDiagram.releaseBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
|
||||||
|
// DSP Chain
|
||||||
|
VFOManager::VFO* vfo;
|
||||||
|
ryfi::Receiver rx;
|
||||||
|
dsp::buffer::Reshaper<dsp::complex_t> reshape;
|
||||||
|
dsp::sink::Handler<dsp::complex_t> symSink;
|
||||||
|
|
||||||
|
ImGui::ConstellationDiagram constDiagram;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new RyFiDecoderModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (RyFiDecoderModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
|
||||||
|
}
|
||||||
74
decoder_modules/ryfi_decoder/src/ryfi/conv_codec.cpp
Normal file
74
decoder_modules/ryfi_decoder/src/ryfi/conv_codec.cpp
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#include "conv_codec.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
ConvEncoder::ConvEncoder(dsp::stream<uint8_t>* in) {
|
||||||
|
// Create the convolutional encoder instance
|
||||||
|
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
|
||||||
|
|
||||||
|
// Init the base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvEncoder::~ConvEncoder() {
|
||||||
|
// Destroy the convolutional encoder instance
|
||||||
|
correct_convolutional_destroy(conv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConvEncoder::encode(const uint8_t* in, uint8_t* out, int count) {
|
||||||
|
// Run convolutional encoder on the data
|
||||||
|
return correct_convolutional_encode(conv, in, count, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConvEncoder::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvDecoder::ConvDecoder(dsp::stream<dsp::complex_t>* in) {
|
||||||
|
// Create the convolutional encoder instance
|
||||||
|
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
|
||||||
|
|
||||||
|
// Allocate the soft symbol buffer
|
||||||
|
soft = dsp::buffer::alloc<uint8_t>(STREAM_BUFFER_SIZE);
|
||||||
|
|
||||||
|
// Init the base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConvDecoder::~ConvDecoder() {
|
||||||
|
// Destroy the convolutional encoder instance
|
||||||
|
correct_convolutional_destroy(conv);
|
||||||
|
|
||||||
|
// Free the soft symbol buffer
|
||||||
|
dsp::buffer::free(soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConvDecoder::decode(const dsp::complex_t* in, uint8_t* out, int count) {
|
||||||
|
// Convert to uint8
|
||||||
|
const float* _in = (const float*)in;
|
||||||
|
count *= 2;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
soft[i] = std::clamp<int>((_in[i] * 127.0f) + 128.0f, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run convolutional decoder on the data
|
||||||
|
return correct_convolutional_decode_soft(conv, soft, count, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConvDecoder::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
71
decoder_modules/ryfi_decoder/src/ryfi/conv_codec.h
Normal file
71
decoder_modules/ryfi_decoder/src/ryfi/conv_codec.h
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "dsp/processor.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "correct.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
/**
|
||||||
|
* RyFi Convolutional Encoder.
|
||||||
|
*/
|
||||||
|
class ConvEncoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||||
|
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a convolutional encoder specifying an input stream.
|
||||||
|
* @param in Input stream.
|
||||||
|
*/
|
||||||
|
ConvEncoder(dsp::stream<uint8_t>* in = NULL);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~ConvEncoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode data.
|
||||||
|
* @param in Input bytes.
|
||||||
|
* @param out Output bits.
|
||||||
|
* @param count Number of input bytes.
|
||||||
|
* @return Number of output bits.
|
||||||
|
*/
|
||||||
|
int encode(const uint8_t* in, uint8_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
correct_convolutional* conv;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RyFi Convolutional Decoder.
|
||||||
|
*/
|
||||||
|
class ConvDecoder : public dsp::Processor<dsp::complex_t, uint8_t> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, uint8_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a convolutional encoder specifying an input stream.
|
||||||
|
* @param in Input stream.
|
||||||
|
*/
|
||||||
|
ConvDecoder(dsp::stream<dsp::complex_t>* in = NULL);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~ConvDecoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode soft symbols.
|
||||||
|
* @param in Input soft symbols.
|
||||||
|
* @param out Output bytes.
|
||||||
|
* @param count Number of input bytes.
|
||||||
|
* @return Number of output bits.
|
||||||
|
*/
|
||||||
|
int decode(const dsp::complex_t* in, uint8_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
correct_convolutional* conv;
|
||||||
|
uint8_t* soft = NULL;
|
||||||
|
};
|
||||||
|
}
|
||||||
37
decoder_modules/ryfi_decoder/src/ryfi/frame.cpp
Normal file
37
decoder_modules/ryfi_decoder/src/ryfi/frame.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include "frame.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
int Frame::serialize(uint8_t* bytes) const {
|
||||||
|
// Write the counter
|
||||||
|
bytes[0] = (counter >> 8) & 0xFF;
|
||||||
|
bytes[1] = counter & 0xFF;
|
||||||
|
|
||||||
|
// Write the first packet pointer
|
||||||
|
bytes[2] = (firstPacket >> 8) & 0xFF;
|
||||||
|
bytes[3] = firstPacket & 0xFF;
|
||||||
|
|
||||||
|
// Write the last packet pointer
|
||||||
|
bytes[4] = (lastPacket >> 8) & 0xFF;
|
||||||
|
bytes[5] = lastPacket & 0xFF;
|
||||||
|
|
||||||
|
// Write the data
|
||||||
|
memcpy(&bytes[6], content, FRAME_DATA_SIZE);
|
||||||
|
|
||||||
|
// Return the length of a serialized frame
|
||||||
|
return FRAME_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frame::deserialize(const uint8_t* bytes, Frame& frame) {
|
||||||
|
// Read the counter
|
||||||
|
frame.counter = (((uint16_t)bytes[0]) << 8) | ((uint16_t)bytes[1]);
|
||||||
|
|
||||||
|
// Read the first packet pointer
|
||||||
|
frame.firstPacket = (((uint16_t)bytes[2]) << 8) | ((uint16_t)bytes[3]);
|
||||||
|
|
||||||
|
// Read the last packet pointer
|
||||||
|
frame.lastPacket = (((uint16_t)bytes[4]) << 8) | ((uint16_t)bytes[5]);
|
||||||
|
|
||||||
|
// Read the data
|
||||||
|
memcpy(frame.content, &bytes[6], FRAME_DATA_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
decoder_modules/ryfi_decoder/src/ryfi/frame.h
Normal file
42
decoder_modules/ryfi_decoder/src/ryfi/frame.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "rs_codec.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
enum PacketOffset {
|
||||||
|
PKT_OFFS_NONE = 0xFFFF
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Frame {
|
||||||
|
/**
|
||||||
|
* Serialize the frame to bytes.
|
||||||
|
* @param bytes Buffer to write the serialized frame to.
|
||||||
|
*/
|
||||||
|
int serialize(uint8_t* bytes) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize a frame from bytes.
|
||||||
|
* @param bytes Buffer to deserialize the frame from.
|
||||||
|
* @param frame Object that will contain the deserialize frame.
|
||||||
|
*/
|
||||||
|
static void deserialize(const uint8_t* bytes, Frame& frame);
|
||||||
|
|
||||||
|
// Size of a serialized frame
|
||||||
|
static inline const int FRAME_SIZE = RS_BLOCK_DEC_SIZE*RS_BLOCK_COUNT;
|
||||||
|
|
||||||
|
// Size of the data area of the frame
|
||||||
|
static inline const int FRAME_DATA_SIZE = FRAME_SIZE - 6;
|
||||||
|
|
||||||
|
// Steadily increasing counter.
|
||||||
|
uint16_t counter = 0;
|
||||||
|
|
||||||
|
// Byte offset of the first packet in the frame.
|
||||||
|
uint16_t firstPacket = 0;
|
||||||
|
|
||||||
|
// Byte offset of the last packet in the frame.
|
||||||
|
uint16_t lastPacket = 0;
|
||||||
|
|
||||||
|
// Data area of the frame.
|
||||||
|
uint8_t content[FRAME_DATA_SIZE];
|
||||||
|
};
|
||||||
|
}
|
||||||
137
decoder_modules/ryfi_decoder/src/ryfi/framing.cpp
Normal file
137
decoder_modules/ryfi_decoder/src/ryfi/framing.cpp
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#include "framing.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
dsp::complex_t QPSK_SYMBOLS[4] = {
|
||||||
|
{ -0.070710678118f, -0.070710678118f },
|
||||||
|
{ -0.070710678118f, 0.070710678118f },
|
||||||
|
{ 0.070710678118f, -0.070710678118f },
|
||||||
|
{ 0.070710678118f, 0.070710678118f },
|
||||||
|
};
|
||||||
|
|
||||||
|
Framer::Framer(dsp::stream<uint8_t>* in) {
|
||||||
|
// Generate the sync symbols
|
||||||
|
int k = 0;
|
||||||
|
for (int i = 62; i >= 0; i -= 2) {
|
||||||
|
syncSyms[k++] = QPSK_SYMBOLS[(SYNC_WORD >> i) & 0b11];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Framer::encode(const uint8_t* in, dsp::complex_t* out, int count) {
|
||||||
|
// Copy sync symbols
|
||||||
|
memcpy(out, syncSyms, SYNC_SYMS*sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
// Modulate the rest of the bits
|
||||||
|
dsp::complex_t* dataOut = &out[SYNC_SYMS];
|
||||||
|
int dataSyms = count / 2;
|
||||||
|
for (int i = 0; i < dataSyms; i++) {
|
||||||
|
uint8_t bits = (in[i >> 2] >> (6 - 2*(i & 0b11))) & 0b11;
|
||||||
|
dataOut[i] = QPSK_SYMBOLS[bits];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute and return the total number of symbols
|
||||||
|
return SYNC_SYMS + dataSyms;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Framer::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
Deframer::Deframer(dsp::stream<dsp::complex_t> *in) {
|
||||||
|
// Compute sync word rotations
|
||||||
|
// 0: 00 01 11 10
|
||||||
|
// 90: 10 00 01 11
|
||||||
|
// 180: 11 10 00 01
|
||||||
|
// 270: 01 11 10 00
|
||||||
|
|
||||||
|
// For 0 and 180 it's the sync and its complement
|
||||||
|
syncRots[ROT_0_DEG] = SYNC_WORD;
|
||||||
|
syncRots[ROT_180_DEG] = ~SYNC_WORD;
|
||||||
|
|
||||||
|
// For 90 and 270 its the quadrature and its complement
|
||||||
|
uint64_t quad;
|
||||||
|
for (int i = 62; i >= 0; i -= 2) {
|
||||||
|
// Get the symbol
|
||||||
|
uint8_t sym = (SYNC_WORD >> i) & 0b11;
|
||||||
|
|
||||||
|
// Rotate it 90 degrees
|
||||||
|
uint8_t rsym;
|
||||||
|
switch (sym) {
|
||||||
|
case 0b00: rsym = 0b10; break;
|
||||||
|
case 0b01: rsym = 0b00; break;
|
||||||
|
case 0b11: rsym = 0b01; break;
|
||||||
|
case 0b10: rsym = 0b11; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push it into the quadrature
|
||||||
|
quad = (quad << 2) | rsym;
|
||||||
|
}
|
||||||
|
syncRots[ROT_90_DEG] = quad;
|
||||||
|
syncRots[ROT_270_DEG] = ~quad;
|
||||||
|
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Deframer::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
dsp::complex_t* in = base_type::_in->readBuf;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (recv) {
|
||||||
|
// Copy the symbol to the output and rotate it approprieate
|
||||||
|
base_type::out.writeBuf[outCount++] = in[i] * symRot;
|
||||||
|
|
||||||
|
// Check if we're done receiving the frame, send it out
|
||||||
|
if (!(--recv)) {
|
||||||
|
if (!base_type::out.swap(outCount)) {
|
||||||
|
base_type::_in->flush();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Get the raw symbol
|
||||||
|
dsp::complex_t fsym = in[i];
|
||||||
|
|
||||||
|
// Decode the symbol
|
||||||
|
uint8_t sym = ((fsym.re > 0) ? 0b10 : 0b00) | ((fsym.im > 0) ? 0b01 : 0b00);
|
||||||
|
|
||||||
|
// Push it to the shift register
|
||||||
|
shift = (shift << 2) | sym;
|
||||||
|
|
||||||
|
// Find the rotation starting with the last known one
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
// Get the test rotation
|
||||||
|
int testRot = (knownRot+i) & 0b11;
|
||||||
|
|
||||||
|
// Check if the hamming distance is close enough
|
||||||
|
int dist;
|
||||||
|
if (distance(shift, syncRots[testRot]) < 6) {
|
||||||
|
// Save the new rotation
|
||||||
|
knownRot = testRot;
|
||||||
|
|
||||||
|
// Start reading in symbols for the frame
|
||||||
|
symRot = symRots[knownRot];
|
||||||
|
recv = 8168; // TODO: Don't hardcode!
|
||||||
|
outCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
87
decoder_modules/ryfi_decoder/src/ryfi/framing.h
Normal file
87
decoder_modules/ryfi_decoder/src/ryfi/framing.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "dsp/processor.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
// Synchronization word.
|
||||||
|
inline const uint64_t SYNC_WORD = 0x341CC540819D8963;
|
||||||
|
|
||||||
|
// Number of synchronization bits.
|
||||||
|
inline const int SYNC_BITS = 64;
|
||||||
|
|
||||||
|
// Number of synchronization symbols.
|
||||||
|
inline const int SYNC_SYMS = SYNC_BITS / 2;
|
||||||
|
|
||||||
|
// Possible constellation rotations
|
||||||
|
enum {
|
||||||
|
ROT_0_DEG = 0,
|
||||||
|
ROT_90_DEG = 1,
|
||||||
|
ROT_180_DEG = 2,
|
||||||
|
ROT_270_DEG = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RyFi Framer.
|
||||||
|
*/
|
||||||
|
class Framer : public dsp::Processor<uint8_t, dsp::complex_t> {
|
||||||
|
using base_type = dsp::Processor<uint8_t, dsp::complex_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a framer specifying an input stream.
|
||||||
|
* @param in Input stream.
|
||||||
|
*/
|
||||||
|
Framer(dsp::stream<uint8_t>* in = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a frame to symbols adding a sync word.
|
||||||
|
*/
|
||||||
|
int encode(const uint8_t* in, dsp::complex_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
dsp::complex_t syncSyms[SYNC_SYMS];
|
||||||
|
};
|
||||||
|
|
||||||
|
class Deframer : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a deframer specifying an input stream.
|
||||||
|
* @param in Input stream.
|
||||||
|
*/
|
||||||
|
Deframer(dsp::stream<dsp::complex_t> *in = NULL);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
inline static constexpr int distance(uint64_t a, uint64_t b) {
|
||||||
|
int dist = 0;
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
dist += ((a & 1) != (b & 1));
|
||||||
|
a >>= 1;
|
||||||
|
b >>= 1;
|
||||||
|
}
|
||||||
|
return dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame reading counters
|
||||||
|
int recv = 0;
|
||||||
|
int outCount = 0;
|
||||||
|
|
||||||
|
// Rotation handling
|
||||||
|
int knownRot = 0;
|
||||||
|
uint64_t syncRots[4];
|
||||||
|
dsp::complex_t symRot;
|
||||||
|
const dsp::complex_t symRots[4] = {
|
||||||
|
{ 1.0f, 0.0f }, // 0 deg
|
||||||
|
{ 0.0f, -1.0f }, // 90 deg
|
||||||
|
{ -1.0f, 0.0f }, // 180 deg
|
||||||
|
{ 0.0f, 1.0f }, // 270 deg
|
||||||
|
};
|
||||||
|
|
||||||
|
// Shift register
|
||||||
|
uint64_t shift;
|
||||||
|
};
|
||||||
|
}
|
||||||
126
decoder_modules/ryfi_decoder/src/ryfi/packet.cpp
Normal file
126
decoder_modules/ryfi_decoder/src/ryfi/packet.cpp
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#include "packet.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
Packet::Packet() {}
|
||||||
|
|
||||||
|
Packet::Packet(uint8_t* content, int size) {
|
||||||
|
// Check that the size isn't too large
|
||||||
|
if (size > MAX_CONTENT_SIZE) {
|
||||||
|
throw std::runtime_error("Content size is too large to fit in a packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Allocate the buffer
|
||||||
|
allocate(size);
|
||||||
|
|
||||||
|
// Copy over the content
|
||||||
|
memcpy(_content, content, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::Packet(const Packet& b) {
|
||||||
|
// Reallocate the buffer
|
||||||
|
allocate(b._size);
|
||||||
|
|
||||||
|
// Copy over the content
|
||||||
|
memcpy(_content, b._content, b._size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::Packet(Packet&& b) {
|
||||||
|
// Move members
|
||||||
|
_content = b._content;
|
||||||
|
_size = b._size;
|
||||||
|
|
||||||
|
// Destroy old object
|
||||||
|
b._content = NULL;
|
||||||
|
b._size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::~Packet() {
|
||||||
|
// Delete the content
|
||||||
|
if (_content) { delete[] _content; }
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet& Packet::operator=(const Packet& b) {
|
||||||
|
// Reallocate the buffer
|
||||||
|
allocate(b._size);
|
||||||
|
|
||||||
|
// Copy over the content
|
||||||
|
memcpy(_content, b._content, b._size);
|
||||||
|
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet& Packet::operator=(Packet&& b) {
|
||||||
|
// Move members
|
||||||
|
_content = b._content;
|
||||||
|
_size = b._size;
|
||||||
|
|
||||||
|
// Destroy old object
|
||||||
|
b._content = NULL;
|
||||||
|
b._size = 0;
|
||||||
|
|
||||||
|
// Return self
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet::operator bool() const {
|
||||||
|
return _size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Packet::size() const {
|
||||||
|
// Return the size
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* Packet::data() const {
|
||||||
|
// Return the size
|
||||||
|
return _content;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Packet::setContent(uint8_t* content, int size) {
|
||||||
|
// Check that the size isn't too large
|
||||||
|
if (size > MAX_CONTENT_SIZE) {
|
||||||
|
throw std::runtime_error("Content size is too large to fit in a packet");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reallocate the buffer
|
||||||
|
allocate(size);
|
||||||
|
|
||||||
|
// Copy over the content
|
||||||
|
memcpy(_content, content, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Packet::serializedSize() const {
|
||||||
|
// Two size bytes + Size of the content
|
||||||
|
return _size + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Packet::serialize(uint8_t* bytes) const {
|
||||||
|
// Write the size in big-endian
|
||||||
|
bytes[0] = (_size >> 8) & 0xFF;
|
||||||
|
bytes[1] = _size & 0xFF;
|
||||||
|
|
||||||
|
// Copy the content of the packet
|
||||||
|
memcpy(&bytes[2], _content, _size);
|
||||||
|
|
||||||
|
// Return the serialized size
|
||||||
|
return serializedSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Packet::allocate(int newSize) {
|
||||||
|
// If the size hasn't changed, do nothing
|
||||||
|
if (newSize == _size) { return; }
|
||||||
|
|
||||||
|
// Free the old buffer
|
||||||
|
if (_content) { delete[] _content; };
|
||||||
|
|
||||||
|
// Update the size
|
||||||
|
_size = newSize;
|
||||||
|
|
||||||
|
// Allocate the buffer
|
||||||
|
_content = new uint8_t[newSize];
|
||||||
|
}
|
||||||
|
}
|
||||||
89
decoder_modules/ryfi_decoder/src/ryfi/packet.h
Normal file
89
decoder_modules/ryfi_decoder/src/ryfi/packet.h
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
/**
|
||||||
|
* RyFi Protocol Packet.
|
||||||
|
*/
|
||||||
|
class Packet {
|
||||||
|
public:
|
||||||
|
// Default constructor
|
||||||
|
Packet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a packet from its content.
|
||||||
|
* @param content Content of the packet.
|
||||||
|
* @param size Number of bytes of content.
|
||||||
|
*/
|
||||||
|
Packet(uint8_t* content, int size);
|
||||||
|
|
||||||
|
// Copy constructor
|
||||||
|
Packet(const Packet& b);
|
||||||
|
|
||||||
|
// Move constructor
|
||||||
|
Packet(Packet&& b);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~Packet();
|
||||||
|
|
||||||
|
// Copy assignment operator
|
||||||
|
Packet& operator=(const Packet& b);
|
||||||
|
|
||||||
|
// Move assignment operator
|
||||||
|
Packet& operator=(Packet&& b);
|
||||||
|
|
||||||
|
// Cast to bool operator
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the content of the packet.
|
||||||
|
* @return Size of the content of the packet.
|
||||||
|
*/
|
||||||
|
int size() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the content of the packet. The pointer is only valid until reallocation or deletion.
|
||||||
|
* @return Content of the packet.
|
||||||
|
*/
|
||||||
|
const uint8_t* data() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the content of the packet.
|
||||||
|
* @param content Content of the packet.
|
||||||
|
* @param size Number of bytes of content.
|
||||||
|
*/
|
||||||
|
void setContent(uint8_t* content, int size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the size of the serialized packet.
|
||||||
|
* @return Size of the serialized packet.
|
||||||
|
*/
|
||||||
|
int serializedSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the packet to bytes.
|
||||||
|
* @param bytes Buffer to which to write the serialized packet.
|
||||||
|
* @return Size of the serialized packet.
|
||||||
|
*/
|
||||||
|
int serialize(uint8_t* bytes) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserialize a packet from bytes.
|
||||||
|
* TODO
|
||||||
|
*/
|
||||||
|
static bool deserialize(uint8_t* bytes, int size, Packet& pkt);
|
||||||
|
|
||||||
|
// Maximum size of the content of the packet.
|
||||||
|
static inline const int MAX_CONTENT_SIZE = 0xFFFF;
|
||||||
|
|
||||||
|
// Maximum size of the serialized packet.
|
||||||
|
static inline const int MAX_SERIALIZED_SIZE = MAX_CONTENT_SIZE + 2;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void allocate(int newSize);
|
||||||
|
|
||||||
|
uint8_t* _content = NULL;
|
||||||
|
int _size = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
194
decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp
Normal file
194
decoder_modules/ryfi_decoder/src/ryfi/receiver.cpp
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
#include "receiver.h"
|
||||||
|
|
||||||
|
#include "utils/flog.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
Receiver::Receiver() {}
|
||||||
|
|
||||||
|
Receiver::Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) {
|
||||||
|
init(in, baudrate, samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Receiver::~Receiver() {
|
||||||
|
// Stop everything
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) {
|
||||||
|
// Initialize the DSP
|
||||||
|
demod.init(in, baudrate, samplerate, 31, 0.6, 0.1f, 0.005f, 1e-6, 0.01);
|
||||||
|
doubler.init(&demod.out);
|
||||||
|
softOut = &doubler.outA;
|
||||||
|
deframer.setInput(&doubler.outB);
|
||||||
|
conv.setInput(&deframer.out);
|
||||||
|
rs.setInput(&conv.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::setInput(dsp::stream<dsp::complex_t>* in) {
|
||||||
|
demod.setInput(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::start() {
|
||||||
|
// Do nothing if already running
|
||||||
|
if (running) { return; }
|
||||||
|
|
||||||
|
// Start the worker thread
|
||||||
|
workerThread = std::thread(&Receiver::worker, this);
|
||||||
|
|
||||||
|
// Start the DSP
|
||||||
|
demod.start();
|
||||||
|
doubler.start();
|
||||||
|
deframer.start();
|
||||||
|
conv.start();
|
||||||
|
rs.start();
|
||||||
|
|
||||||
|
// Update the running state
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::stop() {
|
||||||
|
// Do nothing if not running
|
||||||
|
if (!running) { return; }
|
||||||
|
|
||||||
|
// Stop the worker thread
|
||||||
|
rs.out.stopReader();
|
||||||
|
if (workerThread.joinable()) { workerThread.join(); }
|
||||||
|
rs.out.clearReadStop();
|
||||||
|
|
||||||
|
// Stop the DSP
|
||||||
|
demod.stop();
|
||||||
|
doubler.stop();
|
||||||
|
deframer.stop();
|
||||||
|
conv.stop();
|
||||||
|
rs.stop();
|
||||||
|
|
||||||
|
// Update the running state
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Receiver::worker() {
|
||||||
|
Frame frame;
|
||||||
|
uint16_t lastCounter = 0;
|
||||||
|
uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE];
|
||||||
|
int pktExpected = 0;
|
||||||
|
int pktRead = 0;
|
||||||
|
int valid = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Read a frame
|
||||||
|
int count = rs.out.read();
|
||||||
|
if (count <= 0) { break; }
|
||||||
|
|
||||||
|
// Deserialize the frame
|
||||||
|
Frame::deserialize(rs.out.readBuf, frame);
|
||||||
|
valid++;
|
||||||
|
|
||||||
|
// Flush the stream
|
||||||
|
rs.out.flush();
|
||||||
|
|
||||||
|
//flog::info("Frame[{}]: FirstPacket={}, LastPacket={}", frame.counter, frame.firstPacket, frame.lastPacket);
|
||||||
|
|
||||||
|
// Compute the expected frame counter
|
||||||
|
uint16_t expectedCounter = lastCounter + 1;
|
||||||
|
lastCounter = frame.counter;
|
||||||
|
|
||||||
|
// If the frames aren't consecutive
|
||||||
|
int frameRead = 0;
|
||||||
|
if (frame.counter != expectedCounter) {
|
||||||
|
flog::warn("Lost at least {} frames after {} valid frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000, valid);
|
||||||
|
|
||||||
|
// Cancel the partial packet if there was one
|
||||||
|
pktExpected = 0;
|
||||||
|
pktRead = 0;
|
||||||
|
valid = 1;
|
||||||
|
|
||||||
|
// If this frame is not an idle frame or continuation frame
|
||||||
|
if (frame.firstPacket != PKT_OFFS_NONE) {
|
||||||
|
// If the offset of the first packet is not plausible
|
||||||
|
if (frame.firstPacket > Frame::FRAME_DATA_SIZE-2) {
|
||||||
|
flog::warn("Packet had non-plausible offset: {}", frameRead);
|
||||||
|
|
||||||
|
// Skip the frame
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip to the end of the packet
|
||||||
|
frameRead = frame.firstPacket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no partial packet and the frame doesn't contain a packet start, skip it
|
||||||
|
if (!pktExpected && frame.firstPacket == PKT_OFFS_NONE) { continue; }
|
||||||
|
|
||||||
|
// Extract packets from the frame
|
||||||
|
bool firstPacket = true;
|
||||||
|
bool lastPacket = false;
|
||||||
|
while (frameRead < Frame::FRAME_DATA_SIZE) {
|
||||||
|
// If there is a partial packet read as much as possible from it
|
||||||
|
if (pktExpected) {
|
||||||
|
// Compute how many bytes of the packet are available in the frame
|
||||||
|
int readable = std::min<int>(pktExpected - pktRead, Frame::FRAME_DATA_SIZE - frameRead);
|
||||||
|
//flog::debug("Reading {} bytes", readable);
|
||||||
|
|
||||||
|
// Write them to the packet
|
||||||
|
memcpy(&pktBuffer[pktRead], &frame.content[frameRead], readable);
|
||||||
|
pktRead += readable;
|
||||||
|
frameRead += readable;
|
||||||
|
|
||||||
|
// If the packet is read entirely
|
||||||
|
if (pktRead >= pktExpected) {
|
||||||
|
// Create the packet object
|
||||||
|
Packet pkt(pktBuffer, pktExpected);
|
||||||
|
|
||||||
|
// Send off the packet
|
||||||
|
onPacket(pkt);
|
||||||
|
|
||||||
|
// Prepare for the next packet
|
||||||
|
pktRead = 0;
|
||||||
|
pktExpected = 0;
|
||||||
|
|
||||||
|
// If this was the last packet of the frame
|
||||||
|
if (lastPacket || frame.firstPacket == PKT_OFFS_NONE) {
|
||||||
|
// Skip the rest of the frame
|
||||||
|
frameRead = Frame::FRAME_DATA_SIZE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go to next packet
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the packet offset is not plausible
|
||||||
|
if (Frame::FRAME_DATA_SIZE - frameRead < 2) {
|
||||||
|
flog::warn("Packet had non-plausible offset: {}", frameRead);
|
||||||
|
|
||||||
|
// Skip the rest of the frame and the packet
|
||||||
|
frameRead = Frame::FRAME_DATA_SIZE;
|
||||||
|
pktExpected = 0;
|
||||||
|
pktRead = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the first packet, use the frame info to skip possible left over data
|
||||||
|
if (firstPacket) {
|
||||||
|
frameRead = frame.firstPacket;
|
||||||
|
firstPacket = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is the last packet
|
||||||
|
lastPacket = (frameRead == frame.lastPacket);
|
||||||
|
|
||||||
|
// Parse the packet size
|
||||||
|
pktExpected = ((uint16_t)frame.content[frameRead]) << 8;
|
||||||
|
pktExpected |= (uint16_t)frame.content[frameRead+1];
|
||||||
|
//flog::debug("Starting to read a {} byte packet at offset {}", pktExpected, frameRead);
|
||||||
|
|
||||||
|
// Skip to the packet content
|
||||||
|
frameRead += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] pktBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
69
decoder_modules/ryfi_decoder/src/ryfi/receiver.h
Normal file
69
decoder_modules/ryfi_decoder/src/ryfi/receiver.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "utils/new_event.h"
|
||||||
|
#include "dsp/demod/psk.h"
|
||||||
|
#include "dsp/routing/doubler.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "rs_codec.h"
|
||||||
|
#include "conv_codec.h"
|
||||||
|
#include "framing.h"
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
class Receiver {
|
||||||
|
public:
|
||||||
|
Receiver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a transmitter.
|
||||||
|
* @param in Baseband input.
|
||||||
|
* @param baudrate Baudrate to use over the air.
|
||||||
|
* @param samplerate Samplerate of the baseband.
|
||||||
|
*/
|
||||||
|
Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a transmitter.
|
||||||
|
* @param in Baseband input.
|
||||||
|
* @param baudrate Baudrate to use over the air.
|
||||||
|
* @param samplerate Samplerate of the baseband.
|
||||||
|
*/
|
||||||
|
void init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the input stream.
|
||||||
|
* @param in Baseband input.
|
||||||
|
*/
|
||||||
|
void setInput(dsp::stream<dsp::complex_t>* in);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~Receiver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the transmitter's DSP.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the transmitter's DSP.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
dsp::stream<dsp::complex_t>* softOut;
|
||||||
|
|
||||||
|
NewEvent<Packet> onPacket;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void worker();
|
||||||
|
|
||||||
|
// DSP
|
||||||
|
dsp::demod::PSK<4> demod;
|
||||||
|
dsp::routing::Doubler<dsp::complex_t> doubler;
|
||||||
|
Deframer deframer;
|
||||||
|
ConvDecoder conv;
|
||||||
|
RSDecoder rs;
|
||||||
|
|
||||||
|
bool running = false;
|
||||||
|
std::thread workerThread;
|
||||||
|
};
|
||||||
|
}
|
||||||
169
decoder_modules/ryfi_decoder/src/ryfi/rs_codec.cpp
Normal file
169
decoder_modules/ryfi_decoder/src/ryfi/rs_codec.cpp
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
#include "rs_codec.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
RSEncoder::RSEncoder(dsp::stream<uint8_t>* in) {
|
||||||
|
// Create the convolutional encoder instance
|
||||||
|
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32);
|
||||||
|
|
||||||
|
// Init the base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
RSEncoder::~RSEncoder() {
|
||||||
|
// Destroy the convolutional encoder instance
|
||||||
|
correct_reed_solomon_destroy(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RSEncoder::encode(const uint8_t* in, uint8_t* out, int count) {
|
||||||
|
// Check the size
|
||||||
|
assert(count == RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE);
|
||||||
|
|
||||||
|
// Go through each block
|
||||||
|
uint8_t block[RS_BLOCK_ENC_SIZE];
|
||||||
|
for (int i = 0; i < RS_BLOCK_COUNT; i++) {
|
||||||
|
// Encode block
|
||||||
|
correct_reed_solomon_encode(rs, &in[i*RS_BLOCK_DEC_SIZE], RS_BLOCK_DEC_SIZE, block);
|
||||||
|
|
||||||
|
// Interleave into the frame
|
||||||
|
int k = 0;
|
||||||
|
for (int j = i; j < RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT; j += RS_BLOCK_COUNT) {
|
||||||
|
out[j] = block[k++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scramble
|
||||||
|
for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) {
|
||||||
|
out[i] ^= RS_SCRAMBLER_SEQ[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RSEncoder::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (!out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
RSDecoder::RSDecoder(dsp::stream<uint8_t>* in) {
|
||||||
|
// Create the convolutional encoder instance
|
||||||
|
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32);
|
||||||
|
|
||||||
|
// Init the base class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
RSDecoder::~RSDecoder() {
|
||||||
|
// Destroy the convolutional encoder instance
|
||||||
|
correct_reed_solomon_destroy(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RSDecoder::decode(uint8_t* in, uint8_t* out, int count) {
|
||||||
|
// Check the size
|
||||||
|
assert(count == RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE);
|
||||||
|
|
||||||
|
// Descramble (TODO: Don't do it in-place)
|
||||||
|
for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) {
|
||||||
|
in[i] ^= RS_SCRAMBLER_SEQ[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through each block
|
||||||
|
uint8_t block[RS_BLOCK_ENC_SIZE];
|
||||||
|
for (int i = 0; i < RS_BLOCK_COUNT; i++) {
|
||||||
|
// Deinterleave out of the frame
|
||||||
|
int k = 0;
|
||||||
|
for (int j = i; j < count; j += RS_BLOCK_COUNT) {
|
||||||
|
block[k++] = in[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode block and return if decoding fails
|
||||||
|
int res = correct_reed_solomon_decode(rs, block, RS_BLOCK_ENC_SIZE, &out[i*RS_BLOCK_DEC_SIZE]);
|
||||||
|
if (res < 0) { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RSDecoder::run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (count && !out.swap(count)) { return -1; }
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT] = {
|
||||||
|
0x75, 0x05, 0x7C, 0xCE, 0xF1, 0xD0, 0x6C, 0xF6, 0xFA, 0x65, 0xF6, 0xFC, 0xE0, 0x0A, 0x82, 0x17,
|
||||||
|
0x6C, 0xBE, 0x76, 0xA0, 0xD6, 0x46, 0x12, 0x2E, 0xDE, 0xB5, 0xF7, 0xAD, 0xCB, 0x51, 0x63, 0x47,
|
||||||
|
0x27, 0x30, 0x7E, 0x43, 0xD1, 0xA1, 0xCB, 0x10, 0x08, 0x49, 0xDF, 0x86, 0xD4, 0xC4, 0xD7, 0x3C,
|
||||||
|
0x6D, 0x03, 0x07, 0x37, 0x5B, 0xB3, 0xCD, 0x79, 0x6F, 0x1E, 0xBA, 0xC5, 0x6E, 0xC3, 0x8C, 0x7A,
|
||||||
|
0x25, 0x99, 0x61, 0x54, 0x5A, 0x96, 0x57, 0x9B, 0xE0, 0x60, 0x5B, 0x09, 0x6D, 0x8B, 0x2D, 0x9D,
|
||||||
|
0x15, 0x9D, 0x0E, 0xBF, 0x57, 0xFB, 0x9C, 0x49, 0x82, 0x2C, 0x48, 0x59, 0x92, 0x47, 0x79, 0x17,
|
||||||
|
0x16, 0x74, 0xEA, 0xEA, 0xBB, 0xC5, 0x72, 0x32, 0x17, 0xD1, 0xB3, 0xDE, 0xEB, 0x15, 0xC7, 0x55,
|
||||||
|
0x8A, 0xF2, 0x88, 0xC2, 0x33, 0xA6, 0x17, 0x8B, 0xD4, 0x77, 0x22, 0x00, 0x63, 0x47, 0x45, 0x5F,
|
||||||
|
0x36, 0x35, 0x58, 0x8B, 0x88, 0xEC, 0xCA, 0xC4, 0x60, 0x53, 0x9E, 0xBD, 0xB2, 0xF5, 0x51, 0x46,
|
||||||
|
0x34, 0x9A, 0x07, 0x25, 0x3F, 0xF5, 0x65, 0x63, 0x77, 0x3C, 0x5A, 0xFA, 0x4E, 0x0C, 0xF7, 0x1B,
|
||||||
|
0x82, 0xAB, 0x73, 0x06, 0x7F, 0xB7, 0xC6, 0x6B, 0xBF, 0xB1, 0x46, 0xF3, 0x01, 0x91, 0xB1, 0xFF,
|
||||||
|
0x5C, 0x6F, 0xF9, 0x43, 0x0E, 0x6A, 0x70, 0x89, 0x0B, 0xEA, 0x8C, 0xD4, 0x1B, 0x51, 0x01, 0x31,
|
||||||
|
0x71, 0x2E, 0xDF, 0x24, 0xC1, 0xD5, 0xDB, 0x0E, 0xF5, 0xEB, 0x78, 0x79, 0x39, 0x5B, 0xAD, 0xC3,
|
||||||
|
0xA9, 0xA6, 0x60, 0x30, 0xA2, 0x9A, 0x7B, 0xA0, 0xF4, 0xAA, 0xC5, 0x57, 0xB3, 0x16, 0xF9, 0xB5,
|
||||||
|
0x79, 0x20, 0xC1, 0x88, 0x9A, 0x00, 0x43, 0xB2, 0xC6, 0x84, 0x8D, 0x03, 0xF2, 0xD8, 0x90, 0x7A,
|
||||||
|
0x21, 0x37, 0x7E, 0xF7, 0x75, 0xE5, 0xFB, 0xC9, 0xDC, 0xAB, 0x4B, 0xBC, 0x35, 0x38, 0xB9, 0x3A,
|
||||||
|
0x53, 0x89, 0x7E, 0xD5, 0x94, 0x12, 0x2D, 0x9B, 0x91, 0x90, 0x1D, 0x4D, 0x0E, 0xE0, 0x93, 0xF3,
|
||||||
|
0xC1, 0xA1, 0x9B, 0x73, 0x27, 0x22, 0x41, 0x27, 0xEE, 0x2A, 0xD7, 0x45, 0xBC, 0x8F, 0x9B, 0xA2,
|
||||||
|
0x36, 0x11, 0x16, 0x37, 0x1A, 0xF1, 0x2E, 0x71, 0xCF, 0x86, 0x89, 0x83, 0x5A, 0xF1, 0x24, 0x6C,
|
||||||
|
0x56, 0x71, 0x53, 0xE4, 0xD2, 0xCB, 0xCA, 0x86, 0x1E, 0xA0, 0xD5, 0x83, 0x3B, 0xEF, 0x09, 0x09,
|
||||||
|
0xC2, 0x07, 0x53, 0x86, 0xE6, 0x8A, 0xC6, 0x70, 0xFB, 0x91, 0x43, 0xCB, 0x91, 0x6E, 0xA9, 0xBC,
|
||||||
|
0x31, 0x42, 0x61, 0x0C, 0x88, 0xB8, 0x2C, 0xED, 0xD8, 0xE6, 0xA3, 0xEC, 0xAC, 0xB9, 0x45, 0x5E,
|
||||||
|
0x2C, 0x73, 0x3F, 0x2E, 0x06, 0xE0, 0xBF, 0x73, 0xDD, 0x2E, 0x45, 0x50, 0x6C, 0x53, 0x55, 0xF0,
|
||||||
|
0x7F, 0x6E, 0x61, 0xFA, 0xA0, 0x7A, 0x1C, 0xF0, 0xBD, 0xAC, 0x48, 0x61, 0x03, 0x6B, 0xED, 0x54,
|
||||||
|
0x2A, 0x27, 0x94, 0xF6, 0xF9, 0x6A, 0x04, 0x08, 0x0B, 0x3C, 0xC3, 0x30, 0x66, 0x01, 0xFB, 0xDC,
|
||||||
|
0xC9, 0x65, 0x03, 0x83, 0x7D, 0x0A, 0xDF, 0xA5, 0x04, 0x14, 0xE4, 0xF2, 0x4C, 0x01, 0xDF, 0x04,
|
||||||
|
0xD2, 0x80, 0xB9, 0x9B, 0xD9, 0x5E, 0xF8, 0x2A, 0x93, 0x8D, 0x8C, 0x09, 0x9B, 0x38, 0xEC, 0x3B,
|
||||||
|
0xC4, 0x29, 0x90, 0x7C, 0x65, 0x3A, 0xF2, 0x4B, 0x69, 0xD3, 0x63, 0x9B, 0x40, 0x95, 0xC3, 0xFB,
|
||||||
|
0x67, 0x54, 0x40, 0x9B, 0x26, 0x9F, 0x52, 0xFE, 0xD8, 0xD0, 0x24, 0x9C, 0x5C, 0xD4, 0xEF, 0xDE,
|
||||||
|
0x28, 0x66, 0x75, 0x04, 0xCB, 0xA4, 0xC0, 0xB9, 0x4B, 0xC9, 0x20, 0x4B, 0x56, 0xC7, 0x86, 0xC5,
|
||||||
|
0x39, 0x45, 0x18, 0xA7, 0x48, 0x14, 0x1A, 0x51, 0xCA, 0xD0, 0xC0, 0x15, 0xDD, 0xC1, 0x28, 0x4A,
|
||||||
|
0x7A, 0xD2, 0x10, 0xEA, 0x83, 0xD3, 0x3A, 0xEF, 0x48, 0x29, 0x41, 0xA4, 0xD4, 0x57, 0xA6, 0x1D,
|
||||||
|
0x76, 0x24, 0x93, 0x58, 0x7E, 0xB7, 0xDD, 0x0B, 0xF2, 0xCE, 0x71, 0x55, 0xF5, 0xAB, 0x8C, 0xC8,
|
||||||
|
0x70, 0x59, 0x73, 0x69, 0x9D, 0x29, 0x5E, 0x59, 0xF4, 0xB2, 0xC4, 0x97, 0x75, 0xF0, 0x65, 0x1B,
|
||||||
|
0x66, 0x5F, 0xA4, 0x33, 0x5C, 0xC7, 0xBF, 0x45, 0xE6, 0x20, 0xC0, 0xBD, 0xAD, 0xAE, 0x9F, 0x97,
|
||||||
|
0x05, 0xD8, 0x04, 0x2B, 0x0A, 0x46, 0xE8, 0xB8, 0xCB, 0x00, 0xE2, 0x7C, 0x70, 0x1B, 0x49, 0xDE,
|
||||||
|
0x81, 0xEB, 0x24, 0xAC, 0x1B, 0x3E, 0x09, 0xFB, 0xAC, 0xB7, 0xF2, 0xD1, 0xB2, 0x78, 0xF3, 0xAC,
|
||||||
|
0xC7, 0x6A, 0xA2, 0x07, 0x4C, 0xED, 0x61, 0xAD, 0x04, 0x7F, 0x45, 0x83, 0x59, 0x31, 0x27, 0xF0,
|
||||||
|
0x16, 0x6B, 0x0C, 0xAA, 0xD4, 0xD1, 0xCB, 0x1C, 0x51, 0x41, 0x0D, 0x2F, 0x8F, 0xF9, 0xF9, 0x7F,
|
||||||
|
0x22, 0x89, 0x46, 0xF4, 0xB8, 0x93, 0x98, 0x9E, 0x3E, 0x23, 0xF1, 0x6E, 0x64, 0x08, 0xB6, 0xC9,
|
||||||
|
0x6E, 0x53, 0x53, 0xED, 0xAD, 0x21, 0xCD, 0x1A, 0xF0, 0x45, 0xFC, 0x14, 0x00, 0xEA, 0xF7, 0x42,
|
||||||
|
0xEE, 0xDA, 0x58, 0x0D, 0x85, 0xBC, 0x74, 0xFB, 0x73, 0x78, 0xB5, 0x5E, 0x5E, 0x6F, 0x6F, 0x7E,
|
||||||
|
0x39, 0xC2, 0x05, 0x50, 0xDB, 0x3D, 0xB8, 0xF3, 0x8F, 0x80, 0xEC, 0x46, 0x29, 0x39, 0x89, 0xF3,
|
||||||
|
0x55, 0x9C, 0x6A, 0x5F, 0x7C, 0xD9, 0x7C, 0x13, 0xE4, 0x56, 0x5E, 0xE9, 0x60, 0x19, 0xE2, 0x7D,
|
||||||
|
0xC4, 0x41, 0x92, 0x8D, 0xDA, 0x21, 0x58, 0x20, 0xE9, 0xA8, 0x4C, 0x16, 0x34, 0x99, 0xAC, 0xB7,
|
||||||
|
0x30, 0xBD, 0x39, 0x19, 0xAC, 0x9B, 0x4B, 0x27, 0xFA, 0x32, 0xC1, 0x48, 0xA1, 0x80, 0x34, 0x36,
|
||||||
|
0x1E, 0xFB, 0x92, 0x43, 0x35, 0x72, 0x2D, 0xEF, 0xD2, 0xF2, 0xFC, 0xC2, 0x85, 0xAB, 0x59, 0x40,
|
||||||
|
0x8D, 0x9D, 0x1A, 0x1F, 0xE2, 0x92, 0x87, 0xA2, 0xF9, 0x2C, 0x78, 0xE4, 0xC3, 0x26, 0x56, 0x07,
|
||||||
|
0xB3, 0x78, 0xAF, 0x79, 0x3D, 0x88, 0xF4, 0xAD, 0x66, 0x7C, 0x07, 0x58, 0x98, 0x82, 0x1A, 0x26,
|
||||||
|
0xF7, 0xFD, 0xCE, 0xFF, 0x75, 0xED, 0xAB, 0xBD, 0xAE, 0x6D, 0x5C, 0x28, 0x91, 0xF3, 0xB7, 0x5C,
|
||||||
|
0x27, 0x05, 0xEC, 0x3B, 0xE3, 0xDD, 0x93, 0x24, 0x7F, 0xAD, 0x14, 0xAA, 0x49, 0x61, 0x8F, 0x96,
|
||||||
|
0x1F, 0xAA, 0xB2, 0xEE, 0xA8, 0x24, 0x41, 0x7C, 0xDC, 0xF1, 0x28, 0x26, 0xE6, 0x7F, 0x98, 0x20,
|
||||||
|
0x50, 0x5F, 0x90, 0x21, 0x8A, 0x09, 0x26, 0x59, 0xD0, 0x07, 0x2F, 0xE1, 0x35, 0x4D, 0x0B, 0x20,
|
||||||
|
0xB2, 0xD5, 0xDD, 0xB5, 0xAC, 0x1B, 0xFE, 0xD9, 0xE3, 0x35, 0xF1, 0xB8, 0x3F, 0x3D, 0xFC, 0x0B,
|
||||||
|
0x5A, 0x57, 0xA9, 0x92, 0x2B, 0xC8, 0x3E, 0xC2, 0xAA, 0xEF, 0xB9, 0x98, 0x2C, 0xA8, 0xAB, 0xF6,
|
||||||
|
0xA1, 0xBF, 0xBC, 0x8D, 0x97, 0xA2, 0x74, 0xD9, 0xE5, 0x99, 0x85, 0x81, 0x15, 0xB0, 0xE7, 0x8B,
|
||||||
|
0x48, 0x86, 0xF4, 0x94, 0x9C, 0x62, 0x82, 0xD1, 0x2C, 0x24, 0x4B, 0xAC, 0x7A, 0xB8, 0x4E, 0x4A,
|
||||||
|
0xD2, 0xF6, 0xAA, 0xED, 0xE0, 0x9C, 0x98, 0xD2, 0xDF, 0xC1, 0xBC, 0xBF, 0x55, 0x7D, 0x40, 0xB5,
|
||||||
|
0xDE, 0xD4, 0x25, 0xBB, 0x81, 0xF4, 0x07, 0x1D, 0xE7, 0x3C, 0xB4, 0x62, 0xC9, 0x55, 0x0A, 0x3A,
|
||||||
|
0xD5, 0xCE, 0x97, 0xED, 0x30, 0x76, 0x76, 0x51, 0xBC, 0x8C, 0xE4, 0x54, 0xBE, 0xB7, 0xB5, 0xCD,
|
||||||
|
0xF8, 0x76, 0x37, 0x53, 0x2C, 0x9F, 0xE4, 0xC7, 0xEB, 0xF5, 0x8D, 0x23, 0x8A, 0xDA, 0xD1, 0xA9,
|
||||||
|
0xD8, 0x4C, 0x53, 0xF3, 0x49, 0xA7, 0x1A, 0x5D, 0xE5, 0x03, 0x49, 0x52, 0xD3, 0xE2, 0x1F, 0xA5,
|
||||||
|
0x35, 0x9C, 0xBB, 0x0B, 0xC7, 0x0D, 0xA4, 0x65, 0x54, 0x8B, 0x39, 0xF1, 0x3B, 0x67, 0x21, 0x71,
|
||||||
|
0x10, 0xE7, 0x76, 0xC4, 0xA8, 0xC2, 0x9D, 0x93, 0xC6, 0x51, 0xBA, 0x23
|
||||||
|
};
|
||||||
|
}
|
||||||
82
decoder_modules/ryfi_decoder/src/ryfi/rs_codec.h
Normal file
82
decoder_modules/ryfi_decoder/src/ryfi/rs_codec.h
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "dsp/processor.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "correct.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
// Size of an encoded reed-solomon block.
|
||||||
|
inline const int RS_BLOCK_ENC_SIZE = 255;
|
||||||
|
|
||||||
|
// Size of a decoded reed-solomon block.
|
||||||
|
inline const int RS_BLOCK_DEC_SIZE = 223;
|
||||||
|
|
||||||
|
// Number of reed-solomon blocks.
|
||||||
|
inline const int RS_BLOCK_COUNT = 4;
|
||||||
|
|
||||||
|
// Scrambler sequence
|
||||||
|
extern const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RyFi Reed-Solomon Encoder.
|
||||||
|
*/
|
||||||
|
class RSEncoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||||
|
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a reed-solomon encoder specifying an input stream.
|
||||||
|
* @param in Input stream
|
||||||
|
*/
|
||||||
|
RSEncoder(dsp::stream<uint8_t>* in = NULL);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~RSEncoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode data.
|
||||||
|
* @param in Input bytes.
|
||||||
|
* @param out Output bytes.
|
||||||
|
* @param count Number of input bytes.
|
||||||
|
* @return Number of output bytes.
|
||||||
|
*/
|
||||||
|
int encode(const uint8_t* in, uint8_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
correct_reed_solomon* rs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RyFi Reed-Solomon Decoder.
|
||||||
|
*/
|
||||||
|
class RSDecoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||||
|
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a reed-solomon decoder specifying an input stream.
|
||||||
|
* @param in Input stream
|
||||||
|
*/
|
||||||
|
RSDecoder(dsp::stream<uint8_t>* in = NULL);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~RSDecoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode data.
|
||||||
|
* @param in Input bytes.
|
||||||
|
* @param out Output bytes.
|
||||||
|
* @param count Number of input bytes.
|
||||||
|
* @return Number of output bytes.
|
||||||
|
*/
|
||||||
|
int decode(uint8_t* in, uint8_t* out, int count);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int run();
|
||||||
|
|
||||||
|
correct_reed_solomon* rs;
|
||||||
|
};
|
||||||
|
}
|
||||||
177
decoder_modules/ryfi_decoder/src/ryfi/transmitter.cpp
Normal file
177
decoder_modules/ryfi_decoder/src/ryfi/transmitter.cpp
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
#include "transmitter.h"
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
Transmitter::Transmitter(double baudrate, double samplerate) {
|
||||||
|
// Initialize the DSP
|
||||||
|
rs.setInput(&in);
|
||||||
|
conv.setInput(&rs.out);
|
||||||
|
framer.setInput(&conv.out);
|
||||||
|
resamp.init(&framer.out, baudrate, samplerate);
|
||||||
|
|
||||||
|
rrcTaps = dsp::taps::rootRaisedCosine<float>(511, 0.6, baudrate, samplerate);
|
||||||
|
// Normalize the taps
|
||||||
|
float tot = 0.0f;
|
||||||
|
for (int i = 0; i < rrcTaps.size; i++) {
|
||||||
|
tot += rrcTaps.taps[i];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < rrcTaps.size; i++) {
|
||||||
|
rrcTaps.taps[i] /= tot;
|
||||||
|
}
|
||||||
|
|
||||||
|
rrc.init(&resamp.out, rrcTaps);
|
||||||
|
out = &rrc.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transmitter::~Transmitter() {
|
||||||
|
// Stop everything
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transmitter::start() {
|
||||||
|
// Do nothing if already running
|
||||||
|
if (running) { return; }
|
||||||
|
|
||||||
|
// Start the worker thread
|
||||||
|
workerThread = std::thread(&Transmitter::worker, this);
|
||||||
|
|
||||||
|
// Start the DSP
|
||||||
|
rs.start();
|
||||||
|
conv.start();
|
||||||
|
framer.start();
|
||||||
|
resamp.start();
|
||||||
|
rrc.start();
|
||||||
|
|
||||||
|
// Update the running state
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transmitter::stop() {
|
||||||
|
// Do nothing if not running
|
||||||
|
if (!running) { return; }
|
||||||
|
|
||||||
|
// Stop the worker thread
|
||||||
|
in.stopWriter();
|
||||||
|
if (workerThread.joinable()) { workerThread.join(); }
|
||||||
|
in.clearWriteStop();
|
||||||
|
|
||||||
|
// Stop the DSP
|
||||||
|
rs.stop();
|
||||||
|
conv.stop();
|
||||||
|
framer.stop();
|
||||||
|
resamp.stop();
|
||||||
|
rrc.stop();
|
||||||
|
|
||||||
|
// Update the running state
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Transmitter::send(const Packet& pkt) {
|
||||||
|
// Acquire the packet queue
|
||||||
|
std::lock_guard<std::mutex> lck(packetsMtx);
|
||||||
|
|
||||||
|
// If there are too many packets queued up, drop the packet
|
||||||
|
if (packets.size() >= MAX_QUEUE_SIZE) { return false; }
|
||||||
|
|
||||||
|
// Push the packet onto the queue
|
||||||
|
packets.push(pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Transmitter::txFrame(const Frame& frame) {
|
||||||
|
// Serialize the frame
|
||||||
|
int count = frame.serialize(in.writeBuf);
|
||||||
|
|
||||||
|
// Send it off
|
||||||
|
return in.swap(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
Packet Transmitter::popPacket() {
|
||||||
|
// Acquire the packet queue
|
||||||
|
std::unique_lock<std::mutex> lck(packetsMtx);
|
||||||
|
|
||||||
|
// If no packets are available, return empty packet
|
||||||
|
if (!packets.size()) { return Packet(); }
|
||||||
|
|
||||||
|
// Pop the front packet and return it
|
||||||
|
Packet pkt = packets.front();
|
||||||
|
packets.pop();
|
||||||
|
return pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Transmitter::worker() {
|
||||||
|
Frame frame;
|
||||||
|
Packet pkt;
|
||||||
|
uint16_t counter = 0;
|
||||||
|
int pktToWrite = 0;
|
||||||
|
int pktWritten = 0;
|
||||||
|
uint8_t* pktBuffer = new uint8_t[Packet::MAX_SERIALIZED_SIZE];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
// Initialize the frame
|
||||||
|
frame.counter = counter++;
|
||||||
|
frame.firstPacket = PKT_OFFS_NONE;
|
||||||
|
frame.lastPacket = PKT_OFFS_NONE;
|
||||||
|
int frameOffset = 0;
|
||||||
|
|
||||||
|
// Fill the frame with as much packet data as possible
|
||||||
|
while (frameOffset < sizeof(Frame::content)) {
|
||||||
|
// If there is no packet in the process of being sent
|
||||||
|
if (!pktWritten) {
|
||||||
|
// If there is not enough space for the size of the packet
|
||||||
|
if ((sizeof(Frame::content) - frameOffset) < 2) {
|
||||||
|
// Fill the rest of the frame with noise and send it
|
||||||
|
for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next packet
|
||||||
|
pkt = popPacket();
|
||||||
|
|
||||||
|
// If there was an available packet
|
||||||
|
if (pkt) {
|
||||||
|
// Serialize the packet
|
||||||
|
pktToWrite = pkt.serializedSize();
|
||||||
|
pkt.serialize(pktBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none was available
|
||||||
|
if (!pkt) {
|
||||||
|
// Fill the rest of the frame with noise and send it
|
||||||
|
for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the beginning of the packet
|
||||||
|
if (!pktWritten) {
|
||||||
|
//flog::debug("Starting to write a {} byte packet at offset {}", pktToWrite-2, frameOffset);
|
||||||
|
|
||||||
|
// If this is the first packet of the frame, update its offset
|
||||||
|
if (frame.firstPacket == PKT_OFFS_NONE) { frame.firstPacket = frameOffset; }
|
||||||
|
|
||||||
|
// Update the last packet pointer
|
||||||
|
frame.lastPacket = frameOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the amount of data writeable to the frame
|
||||||
|
int writeable = std::min<int>(pktToWrite - pktWritten, sizeof(Frame::content) - frameOffset);
|
||||||
|
|
||||||
|
// Copy the data to the frame
|
||||||
|
memcpy(&frame.content[frameOffset], &pktBuffer[pktWritten], writeable);
|
||||||
|
pktWritten += writeable;
|
||||||
|
frameOffset += writeable;
|
||||||
|
|
||||||
|
// If the packet is done being sent
|
||||||
|
if (pktWritten >= pktToWrite) {
|
||||||
|
// Prepare for a new packet
|
||||||
|
pktToWrite = 0;
|
||||||
|
pktWritten = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the frame
|
||||||
|
if (!txFrame(frame)) { break; }
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] pktBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
69
decoder_modules/ryfi_decoder/src/ryfi/transmitter.h
Normal file
69
decoder_modules/ryfi_decoder/src/ryfi/transmitter.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "dsp/multirate/rational_resampler.h"
|
||||||
|
#include "dsp/taps/root_raised_cosine.h"
|
||||||
|
#include "dsp/filter/fir.h"
|
||||||
|
#include "packet.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "rs_codec.h"
|
||||||
|
#include "conv_codec.h"
|
||||||
|
#include "framing.h"
|
||||||
|
#include <queue>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace ryfi {
|
||||||
|
class Transmitter {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a transmitter.
|
||||||
|
* @param baudrate Baudrate to use over the air.
|
||||||
|
* @param samplerate Samplerate of the baseband.
|
||||||
|
*/
|
||||||
|
Transmitter(double baudrate, double samplerate);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~Transmitter();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the transmitter's DSP.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the transmitter's DSP.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a packet.
|
||||||
|
* @param pkg Packet to send.
|
||||||
|
* @return True if the packet was send, false if it was dropped.
|
||||||
|
*/
|
||||||
|
bool send(const Packet& pkt);
|
||||||
|
|
||||||
|
// Baseband output
|
||||||
|
dsp::stream<dsp::complex_t>* out;
|
||||||
|
|
||||||
|
static inline const int MAX_QUEUE_SIZE = 32;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool txFrame(const Frame& frame);
|
||||||
|
Packet popPacket();
|
||||||
|
void worker();
|
||||||
|
|
||||||
|
// Packet queue
|
||||||
|
std::mutex packetsMtx;
|
||||||
|
std::queue<Packet> packets;
|
||||||
|
|
||||||
|
// DSP
|
||||||
|
dsp::stream<uint8_t> in;
|
||||||
|
RSEncoder rs;
|
||||||
|
ConvEncoder conv;
|
||||||
|
Framer framer;
|
||||||
|
dsp::multirate::RationalResampler<dsp::complex_t> resamp;
|
||||||
|
dsp::tap<float> rrcTaps;
|
||||||
|
dsp::filter::FIR<dsp::complex_t, float> rrc;
|
||||||
|
|
||||||
|
bool running = false;
|
||||||
|
std::thread workerThread;
|
||||||
|
};
|
||||||
|
}
|
||||||
8
decoder_modules/vor_receiver/CMakeLists.txt
Normal file
8
decoder_modules/vor_receiver/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(vor_receiver)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC "src/*.cpp")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
target_include_directories(vor_receiver PRIVATE "src/")
|
||||||
128
decoder_modules/vor_receiver/src/main.cpp
Normal file
128
decoder_modules/vor_receiver/src/main.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#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/buffer/reshaper.h>
|
||||||
|
#include <dsp/sink/handler_sink.h>
|
||||||
|
#include <gui/widgets/constellation_diagram.h>
|
||||||
|
#include "vor_decoder.h"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "vor_receiver",
|
||||||
|
/* Description: */ "VOR Receiver for SDR++",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigManager config;
|
||||||
|
|
||||||
|
#define INPUT_SAMPLE_RATE VOR_IN_SR
|
||||||
|
|
||||||
|
class VORReceiverModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
VORReceiverModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
config.acquire();
|
||||||
|
// TODO: Load config
|
||||||
|
config.release();
|
||||||
|
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, true);
|
||||||
|
decoder = new vor::Decoder(vfo->output, 1);
|
||||||
|
decoder->onBearing.bind(&VORReceiverModule::onBearing, this);
|
||||||
|
|
||||||
|
decoder->start();
|
||||||
|
|
||||||
|
gui::menu.registerEntry(name, menuHandler, this, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~VORReceiverModule() {
|
||||||
|
decoder->stop();
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
gui::menu.removeEntry(name);
|
||||||
|
delete decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
double bw = gui::waterfall.getBandwidth();
|
||||||
|
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, INPUT_SAMPLE_RATE, true);
|
||||||
|
|
||||||
|
decoder->setInput(vfo->output);
|
||||||
|
|
||||||
|
decoder->start();
|
||||||
|
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
decoder->stop();
|
||||||
|
|
||||||
|
sigpath::vfoManager.deleteVFO(vfo);
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
VORReceiverModule* _this = (VORReceiverModule*)ctx;
|
||||||
|
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::beginDisabled(); }
|
||||||
|
|
||||||
|
ImGui::Text("Bearing: %f°", _this->bearing);
|
||||||
|
ImGui::Text("Quality: %0.1f%%", _this->quality);
|
||||||
|
|
||||||
|
if (!_this->enabled) { style::endDisabled(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void onBearing(float nbearing, float nquality) {
|
||||||
|
bearing = (180.0f * nbearing / FL_M_PI);
|
||||||
|
quality = nquality * 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
|
||||||
|
// DSP Chain
|
||||||
|
VFOManager::VFO* vfo;
|
||||||
|
vor::Decoder* decoder;
|
||||||
|
|
||||||
|
float bearing = 0.0f, quality = 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
// Create default recording directory
|
||||||
|
std::string root = (std::string)core::args["root"];
|
||||||
|
json def = json({});
|
||||||
|
config.setPath(root + "/vor_receiver_config.json");
|
||||||
|
config.load(def);
|
||||||
|
config.enableAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new VORReceiverModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (VORReceiverModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
config.disableAutoSave();
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
50
decoder_modules/vor_receiver/src/vor_decoder.cpp
Normal file
50
decoder_modules/vor_receiver/src/vor_decoder.cpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#include "vor_decoder.h"
|
||||||
|
|
||||||
|
#define STDDEV_NORM_FACTOR 1.813799364234218f // 2.0f * FL_M_PI / sqrt(12)
|
||||||
|
|
||||||
|
namespace vor {
|
||||||
|
Decoder::Decoder(dsp::stream<dsp::complex_t>* in, double integrationTime) {
|
||||||
|
rx.init(in);
|
||||||
|
reshape.init(&rx.out, round(1000.0 * integrationTime), 0);
|
||||||
|
symSink.init(&reshape.out, dataHandler, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoder::~Decoder() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decoder::setInput(dsp::stream<dsp::complex_t>* in) {
|
||||||
|
rx.setInput(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decoder::start() {
|
||||||
|
rx.start();
|
||||||
|
reshape.start();
|
||||||
|
symSink.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decoder::stop() {
|
||||||
|
rx.stop();
|
||||||
|
reshape.stop();
|
||||||
|
symSink.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decoder::dataHandler(float* data, int count, void* ctx) {
|
||||||
|
// Get the instance from context
|
||||||
|
Decoder* _this = (Decoder*)ctx;
|
||||||
|
|
||||||
|
// Compute the mean and standard deviation of the
|
||||||
|
float mean, stddev;
|
||||||
|
volk_32f_stddev_and_mean_32f_x2(&stddev, &mean, data, count);
|
||||||
|
|
||||||
|
// Compute the signal quality
|
||||||
|
float quality = std::max<float>(1.0f - (stddev / STDDEV_NORM_FACTOR), 0.0f);
|
||||||
|
|
||||||
|
// Convert the phase difference to a compass heading
|
||||||
|
mean = -mean;
|
||||||
|
if (mean < 0) { mean = 2.0f*FL_M_PI + mean; }
|
||||||
|
|
||||||
|
// Call the handler
|
||||||
|
_this->onBearing(mean, quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
49
decoder_modules/vor_receiver/src/vor_decoder.h
Normal file
49
decoder_modules/vor_receiver/src/vor_decoder.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#include "vor_receiver.h"
|
||||||
|
#include <dsp/buffer/reshaper.h>
|
||||||
|
#include <dsp/sink/handler_sink.h>
|
||||||
|
#include <utils/new_event.h>
|
||||||
|
|
||||||
|
namespace vor {
|
||||||
|
// Note: hard coded to 22KHz samplerate
|
||||||
|
class Decoder {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create an instance of a VOR decoder.
|
||||||
|
* @param in Input IQ stream at 22 KHz sampling rate.
|
||||||
|
* @param integrationTime Integration time of the bearing data in seconds.
|
||||||
|
*/
|
||||||
|
Decoder(dsp::stream<dsp::complex_t>* in, double integrationTime);
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~Decoder();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the input stream.
|
||||||
|
* @param in Input IQ stream at 22 KHz sampling rate.
|
||||||
|
*/
|
||||||
|
void setInput(dsp::stream<dsp::complex_t>* in);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the decoder.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the decoder.
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handler(bearing, signalQuality);
|
||||||
|
*/
|
||||||
|
NewEvent<float, float> onBearing;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void dataHandler(float* data, int count, void* ctx);
|
||||||
|
|
||||||
|
// DSP
|
||||||
|
Receiver rx;
|
||||||
|
dsp::buffer::Reshaper<float> reshape;
|
||||||
|
dsp::sink::Handler<float> symSink;
|
||||||
|
};
|
||||||
|
}
|
||||||
2019
decoder_modules/vor_receiver/src/vor_fm_filter.h
Normal file
2019
decoder_modules/vor_receiver/src/vor_fm_filter.h
Normal file
File diff suppressed because it is too large
Load Diff
106
decoder_modules/vor_receiver/src/vor_receiver.h
Normal file
106
decoder_modules/vor_receiver/src/vor_receiver.h
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include <dsp/sink.h>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
#include <dsp/demod/am.h>
|
||||||
|
#include <dsp/demod/quadrature.h>
|
||||||
|
#include <dsp/convert/real_to_complex.h>
|
||||||
|
#include <dsp/channel/frequency_xlator.h>
|
||||||
|
#include <dsp/filter/fir.h>
|
||||||
|
#include <dsp/math/delay.h>
|
||||||
|
#include <dsp/math/conjugate.h>
|
||||||
|
#include <dsp/channel/rx_vfo.h>
|
||||||
|
#include "vor_fm_filter.h"
|
||||||
|
#include <utils/wav.h>
|
||||||
|
|
||||||
|
#define VOR_IN_SR 25e3
|
||||||
|
|
||||||
|
namespace vor {
|
||||||
|
class Receiver : public dsp::Processor<dsp::complex_t, float> {
|
||||||
|
using base_type = dsp::Processor<dsp::complex_t, float>;
|
||||||
|
public:
|
||||||
|
Receiver() {}
|
||||||
|
|
||||||
|
Receiver(dsp::stream<dsp::complex_t>* in) { init(in); }
|
||||||
|
|
||||||
|
~Receiver() {
|
||||||
|
if (!base_type::_block_init) { return; }
|
||||||
|
base_type::stop();
|
||||||
|
dsp::taps::free(fmfTaps);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(dsp::stream<dsp::complex_t>* in) {
|
||||||
|
amd.init(NULL, dsp::demod::AM<float>::CARRIER, VOR_IN_SR, 50.0f / VOR_IN_SR, 5.0f / VOR_IN_SR, 100.0f / VOR_IN_SR, VOR_IN_SR);
|
||||||
|
amr2c.init(NULL);
|
||||||
|
fmr2c.init(NULL);
|
||||||
|
fmx.init(NULL, -9960, VOR_IN_SR);
|
||||||
|
fmfTaps = dsp::taps::fromArray(FM_TAPS_COUNT, fm_taps);
|
||||||
|
fmf.init(NULL, fmfTaps);
|
||||||
|
fmd.init(NULL, 600, VOR_IN_SR);
|
||||||
|
amde.init(NULL, FM_TAPS_COUNT / 2);
|
||||||
|
amv.init(NULL, VOR_IN_SR, 1000, 30, 30);
|
||||||
|
fmv.init(NULL, VOR_IN_SR, 1000, 30, 30);
|
||||||
|
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
int process(dsp::complex_t* in, float* out, int count) {
|
||||||
|
// Demodulate the AM outer modulation
|
||||||
|
volk_32fc_magnitude_32f(amd.out.writeBuf, (lv_32fc_t*)in, count);
|
||||||
|
amr2c.process(count, amd.out.writeBuf, amr2c.out.writeBuf);
|
||||||
|
|
||||||
|
// Isolate the FM subcarrier
|
||||||
|
fmx.process(count, amr2c.out.writeBuf, fmx.out.writeBuf);
|
||||||
|
fmf.process(count, fmx.out.writeBuf, fmx.out.writeBuf);
|
||||||
|
|
||||||
|
// Demodulate the FM subcarrier
|
||||||
|
fmd.process(count, fmx.out.writeBuf, fmd.out.writeBuf);
|
||||||
|
fmr2c.process(count, fmd.out.writeBuf, fmr2c.out.writeBuf);
|
||||||
|
|
||||||
|
// Delay the AM signal by the same amount as the FM one
|
||||||
|
amde.process(count, amr2c.out.writeBuf, amr2c.out.writeBuf);
|
||||||
|
|
||||||
|
// Isolate the 30Hz component on both the AM and FM channels
|
||||||
|
int rcount = amv.process(count, amr2c.out.writeBuf, amv.out.writeBuf);
|
||||||
|
fmv.process(count, fmr2c.out.writeBuf, fmv.out.writeBuf);
|
||||||
|
|
||||||
|
// If no data was returned, we're done for this round
|
||||||
|
if (!rcount) { return 0; }
|
||||||
|
|
||||||
|
// Conjugate FM reference
|
||||||
|
volk_32fc_conjugate_32fc((lv_32fc_t*)fmv.out.writeBuf, (lv_32fc_t*)fmv.out.writeBuf, rcount);
|
||||||
|
|
||||||
|
// Multiply both together
|
||||||
|
volk_32fc_x2_multiply_32fc((lv_32fc_t*)amv.out.writeBuf, (lv_32fc_t*)amv.out.writeBuf, (lv_32fc_t*)fmv.out.writeBuf, rcount);
|
||||||
|
|
||||||
|
// Compute angle
|
||||||
|
volk_32fc_s32f_atan2_32f(out, (lv_32fc_t*)amv.out.writeBuf, 1.0f, rcount);
|
||||||
|
|
||||||
|
return rcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
int outCount = process(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||||
|
|
||||||
|
// Swap if some data was generated
|
||||||
|
base_type::_in->flush();
|
||||||
|
if (outCount) {
|
||||||
|
if (!base_type::out.swap(outCount)) { return -1; }
|
||||||
|
}
|
||||||
|
return outCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
dsp::demod::AM<float> amd;
|
||||||
|
dsp::convert::RealToComplex amr2c;
|
||||||
|
dsp::convert::RealToComplex fmr2c;
|
||||||
|
dsp::channel::FrequencyXlator fmx;
|
||||||
|
dsp::tap<float> fmfTaps;
|
||||||
|
dsp::filter::FIR<dsp::complex_t, float> fmf;
|
||||||
|
dsp::demod::Quadrature fmd;
|
||||||
|
dsp::math::Delay<dsp::complex_t> amde;
|
||||||
|
dsp::channel::RxVFO amv;
|
||||||
|
dsp::channel::RxVFO fmv;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -6,13 +6,14 @@ cd /root
|
|||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
libcodec2-dev autoconf libtool xxd
|
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
# Install SDRPlay libraries
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
cp inc/* /usr/include/
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
# Install libperseus
|
# Install libperseus
|
||||||
@@ -25,10 +26,40 @@ make install
|
|||||||
ldconfig
|
ldconfig
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
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
|
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 -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ cd /root
|
|||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
libcodec2-dev autoconf libtool xxd
|
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
# Install SDRPlay libraries
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
cp inc/* /usr/include/
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
# Install libperseus
|
# Install libperseus
|
||||||
@@ -25,10 +26,40 @@ make install
|
|||||||
ldconfig
|
ldconfig
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
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
|
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 -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
cd /root
|
|
||||||
|
|
||||||
# Install dependencies and tools
|
|
||||||
apt update
|
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
|
||||||
libcodec2-dev autoconf libtool xxd
|
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
|
||||||
cp inc/* /usr/include/
|
|
||||||
|
|
||||||
# Install libperseus
|
|
||||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
|
||||||
cd libperseus-sdr
|
|
||||||
autoreconf -i
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
make install
|
|
||||||
ldconfig
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
cd SDRPlusPlus
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
|
||||||
make VERBOSE=1 -j2
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk1-dev, librtaudio-dev, libzstd-dev'
|
|
||||||
@@ -6,13 +6,14 @@ cd /root
|
|||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
libcodec2-dev autoconf libtool xxd
|
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
# Install SDRPlay libraries
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
cp inc/* /usr/include/
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
# Install libperseus
|
# Install libperseus
|
||||||
@@ -25,10 +26,40 @@ make install
|
|||||||
ldconfig
|
ldconfig
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
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
|
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 -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM ubuntu:mantic
|
FROM debian:trixie
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
COPY do_build.sh /root
|
COPY do_build.sh /root
|
||||||
RUN chmod +x /root/do_build.sh
|
RUN chmod +x /root/do_build.sh
|
||||||
66
docker_builds/debian_trixie/do_build.sh
Normal file
66
docker_builds/debian_trixie/do_build.sh
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
cd /root
|
||||||
|
|
||||||
|
# Install dependencies and tools
|
||||||
|
apt update
|
||||||
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||||
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
|
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
|
# Install SDRPlay libraries
|
||||||
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
|
# Install libperseus
|
||||||
|
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||||
|
cd libperseus-sdr
|
||||||
|
autoreconf -i
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
ldconfig
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
cd SDRPlusPlus
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'
|
||||||
@@ -12,13 +12,14 @@ apt update
|
|||||||
# Install dependencies and tools
|
# Install dependencies and tools
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspy-dev \
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspy-dev \
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
libcodec2-dev libudev-dev autoconf libtool xxd
|
libcodec2-dev libudev-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
# Install SDRPlay libraries
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
cp inc/* /usr/include/
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
# Install a more recent libusb version
|
# Install a more recent libusb version
|
||||||
@@ -51,6 +52,26 @@ make install
|
|||||||
ldconfig
|
ldconfig
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
# Fix missing .pc file for codec2
|
# 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
|
||||||
@@ -62,11 +83,21 @@ echo 'Version: 0.7' >> /usr/share/pkgconfig/codec2.pc
|
|||||||
echo 'Libs: -L/usr/include/x86_64-linux-gnu/ -lcodec2' >> /usr/share/pkgconfig/codec2.pc
|
echo 'Libs: -L/usr/include/x86_64-linux-gnu/ -lcodec2' >> /usr/share/pkgconfig/codec2.pc
|
||||||
echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
|
echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
# Build SDR++ Itself
|
# Build SDR++ Itself
|
||||||
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
|
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 -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
# Generate package
|
# Generate package
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ cd /root
|
|||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
libcodec2-dev autoconf libtool xxd
|
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
# Install SDRPlay libraries
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
cp inc/* /usr/include/
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
# Install libperseus
|
# Install libperseus
|
||||||
@@ -25,10 +26,40 @@ make install
|
|||||||
ldconfig
|
ldconfig
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
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
|
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 -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ cd /root
|
|||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
libcodec2-dev autoconf libtool xxd
|
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
# Install SDRPlay libraries
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
cp inc/* /usr/include/
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
# Install libperseus
|
# Install libperseus
|
||||||
@@ -25,10 +26,40 @@ make install
|
|||||||
ldconfig
|
ldconfig
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
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
|
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 -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
cd /root
|
|
||||||
|
|
||||||
# Install dependencies and tools
|
|
||||||
apt update
|
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
|
||||||
libcodec2-dev autoconf libtool xxd
|
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
|
||||||
cp inc/* /usr/include/
|
|
||||||
|
|
||||||
# Install libperseus
|
|
||||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
|
||||||
cd libperseus-sdr
|
|
||||||
autoreconf -i
|
|
||||||
./configure
|
|
||||||
make
|
|
||||||
make install
|
|
||||||
ldconfig
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
cd SDRPlusPlus
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
|
||||||
make VERBOSE=1 -j2
|
|
||||||
|
|
||||||
cd ..
|
|
||||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'
|
|
||||||
@@ -6,13 +6,14 @@ cd /root
|
|||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
libcodec2-dev autoconf libtool xxd
|
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
# Install SDRPlay libraries
|
# Install SDRPlay libraries
|
||||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
cp inc/* /usr/include/
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
# Install libperseus
|
# Install libperseus
|
||||||
@@ -25,10 +26,40 @@ make install
|
|||||||
ldconfig
|
ldconfig
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
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
|
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 -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM debian:buster
|
FROM ubuntu:oracular
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
COPY do_build.sh /root
|
COPY do_build.sh /root
|
||||||
RUN chmod +x /root/do_build.sh
|
RUN chmod +x /root/do_build.sh
|
||||||
66
docker_builds/ubuntu_oracular/do_build.sh
Normal file
66
docker_builds/ubuntu_oracular/do_build.sh
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
cd /root
|
||||||
|
|
||||||
|
# Install dependencies and tools
|
||||||
|
apt update
|
||||||
|
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||||
|
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||||
|
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||||
|
|
||||||
|
# Install SDRPlay libraries
|
||||||
|
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||||
|
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
|
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||||
|
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||||
|
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||||
|
cp inc/* /usr/include/
|
||||||
|
|
||||||
|
# Install libperseus
|
||||||
|
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||||
|
cd libperseus-sdr
|
||||||
|
autoreconf -i
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
make install
|
||||||
|
ldconfig
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
# Install librfnm
|
||||||
|
git clone https://github.com/AlexandreRouma/librfnm
|
||||||
|
cd librfnm
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libfobos
|
||||||
|
git clone https://github.com/AlexandreRouma/libfobos
|
||||||
|
cd libfobos
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
cd SDRPlusPlus
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON
|
||||||
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
|
cd ..
|
||||||
|
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'
|
||||||
@@ -28,6 +28,8 @@ bundle_is_not_to_be_installed() {
|
|||||||
if [ "$1" = "Security" ]; then echo 1; fi
|
if [ "$1" = "Security" ]; then echo 1; fi
|
||||||
if [ "$1" = "AppleFSCompression" ]; then echo 1; fi
|
if [ "$1" = "AppleFSCompression" ]; then echo 1; fi
|
||||||
if [ "$1" = "libsdrplay_api.so.3.14" ]; then echo 1; fi
|
if [ "$1" = "libsdrplay_api.so.3.14" ]; then echo 1; fi
|
||||||
|
if [ "$1" = "libsdrplay_api.so.3.15" ]; then echo 1; fi
|
||||||
|
if [ "$1" = "libxml2.2.dylib" ]; then echo 1; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ========================= FOR INTERNAL USE ONLY =========================
|
# ========================= FOR INTERNAL USE ONLY =========================
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ mkdir sdrpp_debian_amd64/DEBIAN
|
|||||||
# Create package info
|
# Create package info
|
||||||
echo Create package info
|
echo Create package info
|
||||||
echo Package: sdrpp >> sdrpp_debian_amd64/DEBIAN/control
|
echo Package: sdrpp >> sdrpp_debian_amd64/DEBIAN/control
|
||||||
echo Version: 1.2.0$BUILD_NO >> sdrpp_debian_amd64/DEBIAN/control
|
echo Version: 1.2.1$BUILD_NO >> sdrpp_debian_amd64/DEBIAN/control
|
||||||
echo Maintainer: Ryzerth >> sdrpp_debian_amd64/DEBIAN/control
|
echo Maintainer: Ryzerth >> sdrpp_debian_amd64/DEBIAN/control
|
||||||
echo Architecture: all >> sdrpp_debian_amd64/DEBIAN/control
|
echo Architecture: all >> sdrpp_debian_amd64/DEBIAN/control
|
||||||
echo Description: Bloat-free SDR receiver software >> sdrpp_debian_amd64/DEBIAN/control
|
echo Description: Bloat-free SDR receiver software >> sdrpp_debian_amd64/DEBIAN/control
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ cp -R root/res/* $BUNDLE/Contents/Resources/
|
|||||||
bundle_create_icns root/res/icons/sdrpp.macos.png $BUNDLE/Contents/Resources/sdrpp
|
bundle_create_icns root/res/icons/sdrpp.macos.png $BUNDLE/Contents/Resources/sdrpp
|
||||||
|
|
||||||
# Create the property list
|
# Create the property list
|
||||||
bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.2.0 sdrp sdrpp sdrpp $BUNDLE/Contents/Info.plist
|
bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.2.1 sdrp sdrpp sdrpp $BUNDLE/Contents/Info.plist
|
||||||
|
|
||||||
# ========================= Install binaries =========================
|
# ========================= Install binaries =========================
|
||||||
|
|
||||||
@@ -35,11 +35,15 @@ 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/hydrasdr_source/hydrasdr_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/rfspace_source/rfspace_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfspace_source/rfspace_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_sdr_source/rtl_sdr_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_sdr_source/rtl_sdr_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_tcp_source/rtl_tcp_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_tcp_source/rtl_tcp_source.dylib
|
||||||
@@ -54,6 +58,7 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/sink_modules/n
|
|||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/sink_modules/new_portaudio_sink/new_portaudio_sink.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/sink_modules/new_portaudio_sink/new_portaudio_sink.dylib
|
||||||
|
|
||||||
# Decoder modules
|
# Decoder modules
|
||||||
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/atv_decoder/atv_decoder.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/m17_decoder/m17_decoder.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/m17_decoder/m17_decoder.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/meteor_demodulator/meteor_demodulator.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/meteor_demodulator/meteor_demodulator.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/radio/radio.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/radio/radio.dylib
|
||||||
|
|||||||
@@ -24,14 +24,22 @@ 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/
|
||||||
|
|
||||||
cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windows_x64/modules/
|
cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windows_x64/modules/
|
||||||
|
|
||||||
|
cp $build_dir/source_modules/hydrasdr_source/Release/hydrasdr_source.dll sdrpp_windows_x64/modules/
|
||||||
|
cp 'C:/Program Files (x86)/hydrasdr_all/bin/hydrasdr.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
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/
|
||||||
|
|
||||||
@@ -39,6 +47,11 @@ cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_w
|
|||||||
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
|
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
|
||||||
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/
|
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
|
cp $build_dir/source_modules/rfnm_source/Release/rfnm_source.dll sdrpp_windows_x64/modules/
|
||||||
|
cp 'C:/Program Files/RFNM/bin/rfnm.dll' sdrpp_windows_x64/
|
||||||
|
cp 'C:/Program Files/RFNM/bin/spdlog.dll' sdrpp_windows_x64/
|
||||||
|
cp 'C:/Program Files/RFNM/bin/fmt.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
cp $build_dir/source_modules/rfspace_source/Release/rfspace_source.dll sdrpp_windows_x64/modules/
|
cp $build_dir/source_modules/rfspace_source/Release/rfspace_source.dll sdrpp_windows_x64/modules/
|
||||||
|
|
||||||
cp $build_dir/source_modules/rtl_sdr_source/Release/rtl_sdr_source.dll sdrpp_windows_x64/modules/
|
cp $build_dir/source_modules/rtl_sdr_source/Release/rtl_sdr_source.dll sdrpp_windows_x64/modules/
|
||||||
@@ -64,6 +77,8 @@ cp $build_dir/sink_modules/network_sink/Release/network_sink.dll sdrpp_windows_x
|
|||||||
|
|
||||||
|
|
||||||
# Copy decoder modules
|
# Copy decoder modules
|
||||||
|
cp $build_dir/decoder_modules/atv_decoder/Release/atv_decoder.dll sdrpp_windows_x64/modules/
|
||||||
|
|
||||||
cp $build_dir/decoder_modules/m17_decoder/Release/m17_decoder.dll sdrpp_windows_x64/modules/
|
cp $build_dir/decoder_modules/m17_decoder/Release/m17_decoder.dll sdrpp_windows_x64/modules/
|
||||||
cp "C:/Program Files/codec2/lib/libcodec2.dll" sdrpp_windows_x64/
|
cp "C:/Program Files/codec2/lib/libcodec2.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;
|
||||||
@@ -249,7 +248,6 @@ private:
|
|||||||
}
|
}
|
||||||
ImGui::Columns(1, CONCAT("EndRecorderModeColumns##_", _this->name), false);
|
ImGui::Columns(1, CONCAT("EndRecorderModeColumns##_", _this->name), false);
|
||||||
ImGui::EndGroup();
|
ImGui::EndGroup();
|
||||||
if (_this->recording) { style::endDisabled(); }
|
|
||||||
|
|
||||||
// Recording path
|
// Recording path
|
||||||
if (_this->folderSelect.render("##_recorder_fold_" + _this->name)) {
|
if (_this->folderSelect.render("##_recorder_fold_" + _this->name)) {
|
||||||
@@ -284,8 +282,11 @@ private:
|
|||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_this->recording) { style::endDisabled(); }
|
||||||
|
|
||||||
// Show additional audio options
|
// Show additional audio options
|
||||||
if (_this->recMode == RECORDER_MODE_AUDIO) {
|
if (_this->recMode == RECORDER_MODE_AUDIO) {
|
||||||
|
if (_this->recording) { style::beginDisabled(); }
|
||||||
ImGui::LeftLabel("Stream");
|
ImGui::LeftLabel("Stream");
|
||||||
ImGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
if (ImGui::Combo(CONCAT("##_recorder_stream_", _this->name), &_this->streamId, _this->audioStreams.txt)) {
|
if (ImGui::Combo(CONCAT("##_recorder_stream_", _this->name), &_this->streamId, _this->audioStreams.txt)) {
|
||||||
@@ -294,6 +295,7 @@ private:
|
|||||||
config.conf[_this->name]["audioStream"] = _this->audioStreams.key(_this->streamId);
|
config.conf[_this->name]["audioStream"] = _this->audioStreams.key(_this->streamId);
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
if (_this->recording) { style::endDisabled(); }
|
||||||
|
|
||||||
_this->updateAudioMeter(_this->audioLvl);
|
_this->updateAudioMeter(_this->audioLvl);
|
||||||
ImGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
@@ -449,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);
|
||||||
@@ -459,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];
|
||||||
@@ -467,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);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
SDRPP_MOD_INFO{
|
SDRPP_MOD_INFO{
|
||||||
/* Name: */ "scanner",
|
/* Name: */ "scanner",
|
||||||
|
|||||||
33
readme.md
33
readme.md
@@ -41,14 +41,13 @@ To create a desktop shortcut, rightclick the exe and select `Send to -> Desktop
|
|||||||
|
|
||||||
Download the latest release from [the Releases page](https://github.com/AlexandreRouma/SDRPlusPlus/releases) and extract to the directory of your choice.
|
Download the latest release from [the Releases page](https://github.com/AlexandreRouma/SDRPlusPlus/releases) and extract to the directory of your choice.
|
||||||
|
|
||||||
Then, run:
|
Then, use apt to install it:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev
|
sudo apt install path/to/the/sdrpp_debian_amd64.deb
|
||||||
sudo dpkg -i sdrpp_debian_amd64.deb
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If `libvolk2-dev` is not available, use `libvolk1-dev`.
|
**IMPORTANT: You must install the drivers for your SDR. Follow instructions from your manufacturer as to how to do this on your particular distro.**
|
||||||
|
|
||||||
### Arch-based
|
### Arch-based
|
||||||
|
|
||||||
@@ -325,13 +324,16 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
|
| 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 | 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 | ⛔ | ✅ | ✅ |
|
||||||
| rfspace_source | Working | - | OPT_BUILD_RFSPACE_SOURCE | ✅ | ✅ | ✅ |
|
| rfspace_source | Working | - | OPT_BUILD_RFSPACE_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| rtl_sdr_source | Working | librtlsdr | OPT_BUILD_RTL_SDR_SOURCE | ✅ | ✅ | ✅ |
|
| rtl_sdr_source | Working | librtlsdr | OPT_BUILD_RTL_SDR_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| rtl_tcp_source | Working | - | OPT_BUILD_RTL_TCP_SOURCE | ✅ | ✅ | ✅ |
|
| rtl_tcp_source | Working | - | OPT_BUILD_RTL_TCP_SOURCE | ✅ | ✅ | ✅ |
|
||||||
@@ -339,9 +341,9 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| sdrpp_server_source | Working | - | OPT_BUILD_SDRPP_SERVER_SOURCE | ✅ | ✅ | ✅ |
|
| 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,20 +352,22 @@ 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 | ⛔ | ✅ | ⛔ |
|
||||||
| meteor_demodulator | Working | - | OPT_BUILD_METEOR_DEMODULATOR | ✅ | ✅ | ⛔ |
|
| meteor_demodulator | Working | - | OPT_BUILD_METEOR_DEMODULATOR | ✅ | ✅ | ⛔ |
|
||||||
| pager_decoder | Unfinished | - | OPT_BUILD_PAGER_DECODER | ⛔ | ⛔ | ⛔ |
|
| pager_decoder | Unfinished | - | OPT_BUILD_PAGER_DECODER | ⛔ | ⛔ | ⛔ |
|
||||||
| radio | Working | - | OPT_BUILD_RADIO | ✅ | ✅ | ✅ |
|
| radio | Working | - | OPT_BUILD_RADIO | ✅ | ✅ | ✅ |
|
||||||
|
| radio | Unfinished | - | OPT_BUILD_VOR_RECEIVER | ⛔ | ⛔ | ⛔ |
|
||||||
| weather_sat_decoder | Unfinished | - | OPT_BUILD_WEATHER_SAT_DECODER | ⛔ | ⛔ | ⛔ |
|
| weather_sat_decoder | Unfinished | - | OPT_BUILD_WEATHER_SAT_DECODER | ⛔ | ⛔ | ⛔ |
|
||||||
|
|
||||||
## Misc
|
## Misc
|
||||||
@@ -416,8 +420,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
|
||||||
|
|
||||||
@@ -437,15 +441,18 @@ I will soon publish a contributing.md listing the code style to use.
|
|||||||
* Flinger Films
|
* Flinger Films
|
||||||
* [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ)
|
* [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ)
|
||||||
* gringogrigio
|
* gringogrigio
|
||||||
|
* Jandro
|
||||||
* Jeff Moe
|
* Jeff Moe
|
||||||
* Joe Cupano
|
* Joe Cupano
|
||||||
* KD1SQ
|
* KD1SQ
|
||||||
* Kezza
|
* Kezza
|
||||||
* Krys Kamieniecki
|
* Krys Kamieniecki
|
||||||
* Lee Donaghy
|
* Lee Donaghy
|
||||||
* Lee KD1SQ
|
* Lee (KD1SQ)
|
||||||
* .lozenge. (Hank Hill)
|
* .lozenge. (Hank Hill)
|
||||||
* Martin Herren (HB9FXX)
|
* Martin Herren (HB9FXX)
|
||||||
|
* NeoVilsonWong
|
||||||
|
* Nitin (VU2JEK)
|
||||||
* ON4MU
|
* ON4MU
|
||||||
* [Passion-Radio.com](https://passion-radio.com/)
|
* [Passion-Radio.com](https://passion-radio.com/)
|
||||||
* Paul Maine
|
* Paul Maine
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
"name": "France",
|
"name": "France",
|
||||||
"country_name": "France",
|
"country_name": "France",
|
||||||
"country_code": "FR",
|
"country_code": "FR",
|
||||||
"author_name": "Fred F4EED",
|
"author_name": "Fred F4EED, Armand31",
|
||||||
"author_url": "http://f4eed.wordpress.com",
|
"author_url": "http://f4eed.wordpress.com, https://github.com/Armand31",
|
||||||
"bands": [
|
"bands": [
|
||||||
{
|
{
|
||||||
"name": "137KHz - Radioamateur",
|
"name": "137KHz - Radioamateur",
|
||||||
@@ -355,7 +355,7 @@
|
|||||||
"end": 54000000
|
"end": 54000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Bande FM - Radiodif.",
|
"name": "Radiodiffusion - Bande FM",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 80000000,
|
"start": 80000000,
|
||||||
"end": 108000000
|
"end": 108000000
|
||||||
@@ -396,6 +396,12 @@
|
|||||||
"start": 162362500,
|
"start": 162362500,
|
||||||
"end": 162587500
|
"end": 162587500
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiodiffusion - Bande DAB",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 174000000,
|
||||||
|
"end": 223000000
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Military Aviation",
|
"name": "Military Aviation",
|
||||||
"type": "military",
|
"type": "military",
|
||||||
@@ -408,6 +414,12 @@
|
|||||||
"start": 240000000,
|
"start": 240000000,
|
||||||
"end": 270000000
|
"end": 270000000
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Police (TETRAPOL)",
|
||||||
|
"type": "military",
|
||||||
|
"start": 380000000,
|
||||||
|
"end": 400000000
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "70cm - Radioamateur",
|
"name": "70cm - Radioamateur",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
@@ -420,12 +432,24 @@
|
|||||||
"start": 446000000,
|
"start": 446000000,
|
||||||
"end": 446200000
|
"end": 446200000
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "TNT (DVB-T)",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 470000000,
|
||||||
|
"end": 694000000
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "23cm - Radioamateur",
|
"name": "23cm - Radioamateur",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 1240000000,
|
"start": 1240000000,
|
||||||
"end": 1300000000
|
"end": 1300000000
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiodiffusion - Bande DAB",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 1452000000,
|
||||||
|
"end": 1492000000
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "13cm - Radioamateur",
|
"name": "13cm - Radioamateur",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
|
|||||||
231
root/res/bandplans/ireland.json
Normal file
231
root/res/bandplans/ireland.json
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
{
|
||||||
|
"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": "CB",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 26965000,
|
||||||
|
"end": 27405000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "10m ham band",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 28000000,
|
||||||
|
"end": 29700000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FM Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 87500000,
|
||||||
|
"end": 108000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Airband VOR/ILS",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 108000000,
|
||||||
|
"end": 117900000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Airband Voice",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 118000000,
|
||||||
|
"end": 137000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Polar orbiting satellites",
|
||||||
|
"type": "satellite",
|
||||||
|
"start": 137000000,
|
||||||
|
"end": 138000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ADS-B",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 1089000000,
|
||||||
|
"end": 1091000000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"name": "Radionavigazione",
|
"name": "Radionavigazione",
|
||||||
"type": "marine",
|
"type": "marine",
|
||||||
"start": 11300,
|
"start": 11300,
|
||||||
"end": 135500
|
"end": 148500
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 137kHz",
|
"name": "Radioamatori 137kHz",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"end": 520000
|
"end": 520000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 472 kHz",
|
"name": "Radioamatori 472kHz",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 472000,
|
"start": 472000,
|
||||||
"end": 479000
|
"end": 479000
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
"end": 1830000
|
"end": 1830000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 160 m",
|
"name": "Radioamatori 160m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 1830000,
|
"start": 1830000,
|
||||||
"end": 1850000
|
"end": 1850000
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
"end": 2300000
|
"end": 2300000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 120 m",
|
"name": "Radiodiffusione OC 120m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 2300000,
|
"start": 2300000,
|
||||||
"end": 2500000
|
"end": 2500000
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
"end": 3200000
|
"end": 3200000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 90 m",
|
"name": "Radiodiffusione OC 90m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 3200000,
|
"start": 3200000,
|
||||||
"end": 3400000
|
"end": 3400000
|
||||||
@@ -125,13 +125,13 @@
|
|||||||
"end": 3600000
|
"end": 3600000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 80 m",
|
"name": "Radioamatori 80m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 3600000,
|
"start": 3500000,
|
||||||
"end": 3800000
|
"end": 3800000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 75 m",
|
"name": "Radiodiffusione OC 75m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 3900000,
|
"start": 3900000,
|
||||||
"end": 4000000
|
"end": 4000000
|
||||||
@@ -150,19 +150,19 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 60 m",
|
"name": "Radiodiffusione OC 60m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 4750000,
|
"start": 4750000,
|
||||||
"end": 4995000
|
"end": 4995000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 60 m",
|
"name": "Radiodiffusione OC 60m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 5005000,
|
"start": 5005000,
|
||||||
"end": 5060000
|
"end": 5060000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 60 m",
|
"name": "Radioamatori 60m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 5351500,
|
"start": 5351500,
|
||||||
"end": 5366500
|
"end": 5366500
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
"end": 5730000
|
"end": 5730000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 49 m",
|
"name": "Radiodiffusione OC 49m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 5900000,
|
"start": 5900000,
|
||||||
"end": 6200000
|
"end": 6200000
|
||||||
@@ -192,13 +192,13 @@
|
|||||||
"end": 6765000
|
"end": 6765000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 40 m",
|
"name": "Radioamatori 40m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 7000000,
|
"start": 7000000,
|
||||||
"end": 7200000
|
"end": 7200000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 41 m",
|
"name": "Radiodiffusione OC 41m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 7200000,
|
"start": 7200000,
|
||||||
"end": 7450000
|
"end": 7450000
|
||||||
@@ -216,7 +216,7 @@
|
|||||||
"end": 9040000
|
"end": 9040000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 31 m",
|
"name": "Radiodiffusione OC 31m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 9400000,
|
"start": 9400000,
|
||||||
"end": 9900000
|
"end": 9900000
|
||||||
@@ -228,7 +228,7 @@
|
|||||||
"end": 10100000
|
"end": 10100000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "30m - Radioamateur",
|
"name": "Radioamatori 30m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 10100000,
|
"start": 10100000,
|
||||||
"end": 10150000
|
"end": 10150000
|
||||||
@@ -282,7 +282,7 @@
|
|||||||
"end": 15100000
|
"end": 15100000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 19 m",
|
"name": "Radiodiffusione OC 19m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 15100000,
|
"start": 15100000,
|
||||||
"end": 15800000
|
"end": 15800000
|
||||||
@@ -294,7 +294,7 @@
|
|||||||
"end": 17410000
|
"end": 17410000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 16 m",
|
"name": "Radiodiffusione OC 16m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 17480000,
|
"start": 17480000,
|
||||||
"end": 17900000
|
"end": 17900000
|
||||||
@@ -306,9 +306,9 @@
|
|||||||
"end": 18030000
|
"end": 18030000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 17 m",
|
"name": "Radioamatori 17m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 18068000,
|
"start": 18069000,
|
||||||
"end": 18168000
|
"end": 18168000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -318,7 +318,7 @@
|
|||||||
"end": 18900000
|
"end": 18900000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 15 m",
|
"name": "Radiodiffusione OC 15m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 18900000,
|
"start": 18900000,
|
||||||
"end": 19020000
|
"end": 19020000
|
||||||
@@ -336,13 +336,13 @@
|
|||||||
"end": 20010000
|
"end": 20010000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 15 m",
|
"name": "Radioamatori 15m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 21000000,
|
"start": 21000000,
|
||||||
"end": 21450000
|
"end": 21450000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 13 m",
|
"name": "Radiodiffusione OC 13m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 21450000,
|
"start": 21450000,
|
||||||
"end": 21850000
|
"end": 21850000
|
||||||
@@ -366,7 +366,7 @@
|
|||||||
"end": 23350000
|
"end": 23350000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 12 m",
|
"name": "Radioamatori 12m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 24890000,
|
"start": 24890000,
|
||||||
"end": 24990000
|
"end": 24990000
|
||||||
@@ -390,7 +390,7 @@
|
|||||||
"end": 25670000
|
"end": 25670000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiodiffusione OC 11 m",
|
"name": "Radiodiffusione OC 11m",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 25670000,
|
"start": 25670000,
|
||||||
"end": 26100000
|
"end": 26100000
|
||||||
@@ -408,7 +408,7 @@
|
|||||||
"end": 27230000
|
"end": 27230000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 10 m",
|
"name": "Radioamatori 10m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 28000000,
|
"start": 28000000,
|
||||||
"end": 29700000
|
"end": 29700000
|
||||||
@@ -420,10 +420,10 @@
|
|||||||
"end": 47000000
|
"end": 47000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 9 m",
|
"name": "Radioamatori 6m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 47000000,
|
"start": 50000000,
|
||||||
"end": 52500000
|
"end": 51000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Wind profiler",
|
"name": "Wind profiler",
|
||||||
@@ -438,7 +438,7 @@
|
|||||||
"end": 74800000
|
"end": 74800000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radiofari 75 MHz",
|
"name": "Radiofari 75MHz",
|
||||||
"type": "aviation",
|
"type": "aviation",
|
||||||
"start": 74800000,
|
"start": 74800000,
|
||||||
"end": 75200000
|
"end": 75200000
|
||||||
@@ -446,19 +446,19 @@
|
|||||||
{
|
{
|
||||||
"name": "Radiodiffusione FM",
|
"name": "Radiodiffusione FM",
|
||||||
"type": "broadcast",
|
"type": "broadcast",
|
||||||
"start": 80000000,
|
"start": 87500000,
|
||||||
"end": 108000000
|
"end": 108000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "VOR/ILS",
|
"name": "VOR/ILS",
|
||||||
"type": "aviation",
|
"type": "aviation",
|
||||||
"start": 108000000,
|
"start": 108000000,
|
||||||
"end": 118000000
|
"end": 117975000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Mobile aeronautico",
|
"name": "Mobile aeronautico",
|
||||||
"type": "aviation",
|
"type": "aviation",
|
||||||
"start": 118000000,
|
"start": 117975000,
|
||||||
"end": 137000000
|
"end": 137000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -468,10 +468,10 @@
|
|||||||
"end": 138000000
|
"end": 138000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 2 m",
|
"name": "Radioamatori 2m",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 144000000,
|
"start": 144000000,
|
||||||
"end": 148000000
|
"end": 146000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Telefonia satellitare",
|
"name": "Telefonia satellitare",
|
||||||
@@ -552,7 +552,7 @@
|
|||||||
"end": 430000000
|
"end": 430000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 70 cm",
|
"name": "Radioamatori 70cm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 430000000,
|
"start": 430000000,
|
||||||
"end": 434000000
|
"end": 434000000
|
||||||
@@ -564,10 +564,10 @@
|
|||||||
"end": 435000000
|
"end": 435000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 70 cm",
|
"name": "Radioamatori 70cm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 435000000,
|
"start": 435000000,
|
||||||
"end": 436000000
|
"end": 438000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Mobile o fisso privato",
|
"name": "Mobile o fisso privato",
|
||||||
@@ -642,10 +642,16 @@
|
|||||||
"end": 124000000
|
"end": 124000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 23 cm",
|
"name": "Radioamatori 23cm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 1240000000,
|
"start": 1240000000,
|
||||||
"end": 1270000000
|
"end": 1245000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radioamatori 23cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 1267000000,
|
||||||
|
"end": 1298000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Wind profiler",
|
"name": "Wind profiler",
|
||||||
@@ -792,10 +798,10 @@
|
|||||||
"end": 2300000000
|
"end": 2300000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 13 cm",
|
"name": "Radioamatori 13cm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 2300000000,
|
"start": 2300000000,
|
||||||
"end": 2400000000
|
"end": 2450000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ISM, SAP/SAB, 802.11",
|
"name": "ISM, SAP/SAB, 802.11",
|
||||||
@@ -827,12 +833,6 @@
|
|||||||
"start": 2900000000,
|
"start": 2900000000,
|
||||||
"end": 3400000000
|
"end": 3400000000
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Radioamatori 9 cm",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 3400000000,
|
|
||||||
"end": 3475000000
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Reti numeriche",
|
"name": "Reti numeriche",
|
||||||
"type": "comms",
|
"type": "comms",
|
||||||
@@ -858,9 +858,21 @@
|
|||||||
"end": 5650000000
|
"end": 5650000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 6 cm",
|
"name": "Radioamatori 5cm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 5650000000,
|
"start": 5650000000,
|
||||||
|
"end": 5670000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radioamatori 5cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 5760000000,
|
||||||
|
"end": 5770000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radioamatori 5cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 5830000000,
|
||||||
"end": 5850000000
|
"end": 5850000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -912,9 +924,9 @@
|
|||||||
"end": 10000000000
|
"end": 10000000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 3 cm",
|
"name": "Radioamatori 3cm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 10000000000,
|
"start": 10300000000,
|
||||||
"end": 10500000000
|
"end": 10500000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1001,10 +1013,16 @@
|
|||||||
"start": 23150000000,
|
"start": 23150000000,
|
||||||
"end": 23338000000
|
"end": 23338000000
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Radioamatori 1,5cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 24000000000,
|
||||||
|
"end": 24050000000
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "ISM, SRD e LPR",
|
"name": "ISM, SRD e LPR",
|
||||||
"type": "ism",
|
"type": "ism",
|
||||||
"start": 24000000000,
|
"start": 24050000000,
|
||||||
"end": 24450000000
|
"end": 24450000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1086,7 +1104,7 @@
|
|||||||
"end": 43500000000
|
"end": 43500000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 6 mm",
|
"name": "Radioamatori 7mm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 47000000000,
|
"start": 47000000000,
|
||||||
"end": 47200000000
|
"end": 47200000000
|
||||||
@@ -1128,9 +1146,9 @@
|
|||||||
"end": 76500000000
|
"end": 76500000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 4 mm",
|
"name": "Radioamatori 4mm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 76500000000,
|
"start": 75500000000,
|
||||||
"end": 81500000000
|
"end": 81500000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1146,19 +1164,25 @@
|
|||||||
"end": 122250000000
|
"end": 122250000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 2,5 mm",
|
"name": "Radioamatori 2,4mm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 122250000000,
|
"start": 122500000000,
|
||||||
"end": 123000000000
|
"end": 123000000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 2 mm",
|
"name": "Radioamatori 2,23mm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 134000000000,
|
"start": 134000000000,
|
||||||
"end": 141000000000
|
"end": 141000000000
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Radioamatori 1 mm",
|
"name": "Radioamatori 2,1mm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 142000000000,
|
||||||
|
"end": 144000000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radioamatori 1mm",
|
||||||
"type": "amateur",
|
"type": "amateur",
|
||||||
"start": 241000000000,
|
"start": 241000000000,
|
||||||
"end": 250000000000
|
"end": 250000000000
|
||||||
|
|||||||
549
root/res/bandplans/republic-of-korea.json
Normal file
549
root/res/bandplans/republic-of-korea.json
Normal file
@@ -0,0 +1,549 @@
|
|||||||
|
{
|
||||||
|
"name": "Republic of Korea",
|
||||||
|
"country_name": "Republic of Korea",
|
||||||
|
"country_code": "KR",
|
||||||
|
"author_name": "SeoyeonBae",
|
||||||
|
"author_url": "https://github.com/bsy0317",
|
||||||
|
"bands": [
|
||||||
|
{
|
||||||
|
"name": "Radio Navigation",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 8300,
|
||||||
|
"end": 14000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Coastal Telegraph",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 14000,
|
||||||
|
"end": 19950
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 19950,
|
||||||
|
"end": 20250
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Coastal Telegraph",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 20250,
|
||||||
|
"end": 70000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radio Navigation",
|
||||||
|
"type": "navigation",
|
||||||
|
"start": 70000,
|
||||||
|
"end": 160000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Radio Navigation",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 160000,
|
||||||
|
"end": 285000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Maritime Radiobeacon",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 285000,
|
||||||
|
"end": 325000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Radio Navigation",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 325000,
|
||||||
|
"end": 472000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 472000,
|
||||||
|
"end": 479000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "International Distress Safety Call",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 479000,
|
||||||
|
"end": 505000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Maritime Telegraph",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 505000,
|
||||||
|
"end": 526500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 526500,
|
||||||
|
"end": 1606500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiobuoy",
|
||||||
|
"type": "navigation",
|
||||||
|
"start": 1606500,
|
||||||
|
"end": 1800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 1800000,
|
||||||
|
"end": 1825000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiobuoy Control LORAN",
|
||||||
|
"type": "radiolocation",
|
||||||
|
"start": 1825000,
|
||||||
|
"end": 2000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiobuoy",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 2000000,
|
||||||
|
"end": 2065000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Distress Call",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 2065000,
|
||||||
|
"end": 2107000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "International Distress Search and Rescue",
|
||||||
|
"type": "mobile",
|
||||||
|
"start": 2173500,
|
||||||
|
"end": 2190500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Road Management",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 2194000,
|
||||||
|
"end": 2495000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 2495000,
|
||||||
|
"end": 2505000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Telephone",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 2505000,
|
||||||
|
"end": 2850000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile R",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 2850000,
|
||||||
|
"end": 3025000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile OR",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 3025000,
|
||||||
|
"end": 3155000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile R",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 3400000,
|
||||||
|
"end": 3500000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 3500000,
|
||||||
|
"end": 3550000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Experimental Station",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 3550000,
|
||||||
|
"end": 3790000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 3790000,
|
||||||
|
"end": 3800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 3900000,
|
||||||
|
"end": 3950000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 3995000,
|
||||||
|
"end": 4005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Telephone",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4005000,
|
||||||
|
"end": 4063000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Oceanographic Data",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4063000,
|
||||||
|
"end": 4065000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Duplex Telephone",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4065000,
|
||||||
|
"end": 4146000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Simplex Telephone",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4146000,
|
||||||
|
"end": 4152000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Wideband Telegraph Fax",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4152000,
|
||||||
|
"end": 4172000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station Narrowband",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4172000,
|
||||||
|
"end": 4181750
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ship Station A1A Morse Code Communication",
|
||||||
|
"type": "marine",
|
||||||
|
"start": 4186750,
|
||||||
|
"end": 4202250
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radiolocation",
|
||||||
|
"type": "radiolocation",
|
||||||
|
"start": 4438000,
|
||||||
|
"end": 4488000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Calling Response",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 4488000,
|
||||||
|
"end": 4650000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile R",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 4650000,
|
||||||
|
"end": 4850000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 4995000,
|
||||||
|
"end": 5005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Search Rescue",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 5480000,
|
||||||
|
"end": 5730000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 5900000,
|
||||||
|
"end": 5950000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 5950000,
|
||||||
|
"end": 6200000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 7000000,
|
||||||
|
"end": 7100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 7100000,
|
||||||
|
"end": 7200000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 7200000,
|
||||||
|
"end": 7450000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 7995000,
|
||||||
|
"end": 8005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 9400000,
|
||||||
|
"end": 9500000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 9500000,
|
||||||
|
"end": 9900000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 9995000,
|
||||||
|
"end": 10005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 10100000,
|
||||||
|
"end": 10150000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 10150000,
|
||||||
|
"end": 11600000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 11600000,
|
||||||
|
"end": 11650000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 11650000,
|
||||||
|
"end": 12050000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 12050000,
|
||||||
|
"end": 12100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 13260000,
|
||||||
|
"end": 13360000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Radio Astronomy",
|
||||||
|
"type": "astronomy",
|
||||||
|
"start": 13360000,
|
||||||
|
"end": 13410000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 13570000,
|
||||||
|
"end": 13600000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 13600000,
|
||||||
|
"end": 13800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 13800000,
|
||||||
|
"end": 13870000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 14000000,
|
||||||
|
"end": 14350000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Aviation Mobile",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 15010000,
|
||||||
|
"end": 15100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 15100000,
|
||||||
|
"end": 15600000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 15600000,
|
||||||
|
"end": 15800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 15800000,
|
||||||
|
"end": 15995000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Standard Frequency Time Signal",
|
||||||
|
"type": "utility",
|
||||||
|
"start": 15995000,
|
||||||
|
"end": 16005000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 18900000,
|
||||||
|
"end": 19020000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 21000000,
|
||||||
|
"end": 21450000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 21450000,
|
||||||
|
"end": 21850000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 24890000,
|
||||||
|
"end": 24990000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Shortwave Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 25670000,
|
||||||
|
"end": 26100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 28000000,
|
||||||
|
"end": 29700000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 50000000,
|
||||||
|
"end": 54000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TV Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 54000000,
|
||||||
|
"end": 72000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Flood Warning",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 72000000,
|
||||||
|
"end": 74800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TV Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 76000000,
|
||||||
|
"end": 88000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FM Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 88000000,
|
||||||
|
"end": 100000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FM Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 100000000,
|
||||||
|
"end": 108000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ILS Localizer VOR",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 108000000,
|
||||||
|
"end": 117975000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Amateur Station",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 144000000,
|
||||||
|
"end": 146000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "General Communication",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 146000000,
|
||||||
|
"end": 148000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Low Power Device",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 162037500,
|
||||||
|
"end": 174000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "TV Broadcast",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 174000000,
|
||||||
|
"end": 216000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Low Power Device",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 216000000,
|
||||||
|
"end": 230000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Low Power Device",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 273000000,
|
||||||
|
"end": 322000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Personal Radio",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 420000000,
|
||||||
|
"end": 470000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Public Network",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 698000000,
|
||||||
|
"end": 806000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Low Power Device",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 942000000,
|
||||||
|
"end": 960000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Satellite Mobile Communication",
|
||||||
|
"type": "fixed",
|
||||||
|
"start": 15250000000,
|
||||||
|
"end": 16605000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Mobile Communication",
|
||||||
|
"type": "mobile",
|
||||||
|
"start": 25000000000,
|
||||||
|
"end": 37000000000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
285
root/res/bandplans/turkey.json
Normal file
285
root/res/bandplans/turkey.json
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
{
|
||||||
|
"name": "Turkey",
|
||||||
|
"country_name": "Turkey",
|
||||||
|
"country_code": "TR",
|
||||||
|
"author_name": "Yunus TA2PEA",
|
||||||
|
"author_url": "https://github.com/ycanerol",
|
||||||
|
"bands": [
|
||||||
|
{
|
||||||
|
"name": "LW",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 135700,
|
||||||
|
"end": 137800
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "630m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 472000,
|
||||||
|
"end": 479000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "160m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 1810000,
|
||||||
|
"end": 1850000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "80m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 3500000,
|
||||||
|
"end": 3800000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "60m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 5351500,
|
||||||
|
"end": 5366500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "40m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 7000000,
|
||||||
|
"end": 7200000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "30m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 10100000,
|
||||||
|
"end": 10150000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "20m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 14000000,
|
||||||
|
"end": 14350000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "17m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 18068000,
|
||||||
|
"end": 18168000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "15m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 21000000,
|
||||||
|
"end": 21450000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "12m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 24890000,
|
||||||
|
"end": 24990000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CB",
|
||||||
|
"type": "other",
|
||||||
|
"start": 26565000,
|
||||||
|
"end": 27405000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pagers",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 27750000,
|
||||||
|
"end": 28000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "10m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 28000000,
|
||||||
|
"end": 29700000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "6m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 50030000,
|
||||||
|
"end": 51000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FM",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 87500000,
|
||||||
|
"end": 108000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Airband VOR/ILS",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 108000000,
|
||||||
|
"end": 117975000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Airband Voice",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 117975000,
|
||||||
|
"end": 137000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "2m",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 144000000,
|
||||||
|
"end": 146000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Sayac Okuma",
|
||||||
|
"type": "other",
|
||||||
|
"start": 169400000,
|
||||||
|
"end": 169475000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pagers",
|
||||||
|
"type": "other",
|
||||||
|
"start": 167000000,
|
||||||
|
"end": 167100000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Public announcement systems",
|
||||||
|
"type": "other",
|
||||||
|
"start": 173882500,
|
||||||
|
"end": 174000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DVB-T",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 174000000,
|
||||||
|
"end": 216000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "T-DAB",
|
||||||
|
"type": "broadcast",
|
||||||
|
"start": 216000000,
|
||||||
|
"end": 233000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ILS-Glide Path",
|
||||||
|
"type": "aviation",
|
||||||
|
"start": 328600000,
|
||||||
|
"end": 335400000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Public Safety/Emergency",
|
||||||
|
"type": "other",
|
||||||
|
"start": 380000000,
|
||||||
|
"end": 385000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Public Safety/Emergency",
|
||||||
|
"type": "other",
|
||||||
|
"start": 390000000,
|
||||||
|
"end": 395000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 430200000,
|
||||||
|
"end": 430700000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70cm-RepeaterRX",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 431550000,
|
||||||
|
"end": 431825000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 432000000,
|
||||||
|
"end": 432975000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 433400000,
|
||||||
|
"end": 434000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 435000000,
|
||||||
|
"end": 438000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "70cm-RepeaterTX",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 439150000,
|
||||||
|
"end": 439425000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Public announcement systems",
|
||||||
|
"type": "other",
|
||||||
|
"start": 445250000,
|
||||||
|
"end": 445462500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PMR446",
|
||||||
|
"type": "other",
|
||||||
|
"start": 446006250,
|
||||||
|
"end": 446196875
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RFID",
|
||||||
|
"type": "other",
|
||||||
|
"start": 865000000,
|
||||||
|
"end": 868000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "RFID",
|
||||||
|
"type": "other",
|
||||||
|
"start": 916100000,
|
||||||
|
"end": 918900000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "23cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 1240000000,
|
||||||
|
"end": 1300000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "DECT",
|
||||||
|
"type": "other",
|
||||||
|
"start": 1880000000,
|
||||||
|
"end": 1900000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "5GHz",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 5650000000,
|
||||||
|
"end": 5670000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "5GHz",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 5820000000,
|
||||||
|
"end": 5850000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "3cm",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 104500000000,
|
||||||
|
"end": 104520000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "24GHz",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 24000000000,
|
||||||
|
"end": 24050000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "47GHz",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 47000000000,
|
||||||
|
"end": 47200000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "75GHz",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 75500000000,
|
||||||
|
"end": 7600000000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "134GHz",
|
||||||
|
"type": "amateur",
|
||||||
|
"start": 134000000000,
|
||||||
|
"end": 142000000000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -217,14 +217,19 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void startServer() {
|
void startServer() {
|
||||||
if (modeId == SINK_MODE_TCP) {
|
try {
|
||||||
listener = net::listen(hostname, port);
|
if (modeId == SINK_MODE_TCP) {
|
||||||
if (listener) {
|
listener = net::listen(hostname, port);
|
||||||
listener->acceptAsync(clientHandler, this);
|
if (listener) {
|
||||||
|
listener->acceptAsync(clientHandler, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
catch (const std::exception& e) {
|
||||||
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
|
flog::error("Failed to open socket: {}", e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -184,6 +184,9 @@ private:
|
|||||||
AudioSourceModule* _this = (AudioSourceModule*)ctx;
|
AudioSourceModule* _this = (AudioSourceModule*)ctx;
|
||||||
if (_this->running) { return; }
|
if (_this->running) { return; }
|
||||||
|
|
||||||
|
// If no device is selected, give up
|
||||||
|
if (_this->selectedDevice.empty()) { return; }
|
||||||
|
|
||||||
// Stream options
|
// Stream options
|
||||||
RtAudio::StreamParameters parameters;
|
RtAudio::StreamParameters parameters;
|
||||||
parameters.deviceId = _this->devices[_this->devId].id;
|
parameters.deviceId = _this->devices[_this->devId].id;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
17
source_modules/harogic_source/CMakeLists.txt
Normal file
17
source_modules/harogic_source/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(harogic_source)
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Lib path
|
||||||
|
target_link_directories(harogic_source PRIVATE "C:/radio/HTRA_API/x64/htra_api/")
|
||||||
|
target_include_directories(harogic_source PRIVATE "C:/radio/HTRA_API/x64/htra_api/")
|
||||||
|
target_link_libraries(harogic_source PRIVATE htra_api)
|
||||||
|
else (MSVC)
|
||||||
|
target_link_directories(harogic_source PRIVATE "/opt/htraapi/lib/${CMAKE_SYSTEM_PROCESSOR}/")
|
||||||
|
target_include_directories(harogic_source PRIVATE "/opt/htraapi/inc/")
|
||||||
|
target_link_libraries(harogic_source PRIVATE htraapi)
|
||||||
|
endif ()
|
||||||
510
source_modules/harogic_source/src/main.cpp
Normal file
510
source_modules/harogic_source/src/main.cpp
Normal file
@@ -0,0 +1,510 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <gui/smgui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <utils/optionlist.h>
|
||||||
|
#include <htra_api.h>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "harogic_source",
|
||||||
|
/* Description: */ "harogic Source Module",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
class HarogicSourceModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
HarogicSourceModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
sampleRate = 61440000.0;
|
||||||
|
|
||||||
|
handler.ctx = this;
|
||||||
|
handler.selectHandler = menuSelected;
|
||||||
|
handler.deselectHandler = menuDeselected;
|
||||||
|
handler.menuHandler = menuHandler;
|
||||||
|
handler.startHandler = start;
|
||||||
|
handler.stopHandler = stop;
|
||||||
|
handler.tuneHandler = tune;
|
||||||
|
handler.stream = &stream;
|
||||||
|
|
||||||
|
// Refresh devices
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
// Select first (TODO: Select from config)
|
||||||
|
select("");
|
||||||
|
|
||||||
|
sigpath::sourceManager.registerSource("Harogic", &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
~HarogicSourceModule() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refresh() {
|
||||||
|
devices.clear();
|
||||||
|
|
||||||
|
// Set up the device parameters
|
||||||
|
BootProfile_TypeDef profile = {};
|
||||||
|
profile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
|
||||||
|
profile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortOnly;
|
||||||
|
|
||||||
|
// Working variables
|
||||||
|
void* dev;
|
||||||
|
BootInfo_TypeDef binfo;
|
||||||
|
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
// Attempt to open the device with the given ID
|
||||||
|
int ret = Device_Open(&dev, i, &profile, &binfo);
|
||||||
|
if (ret < 0) { break; }
|
||||||
|
|
||||||
|
// Create serial string
|
||||||
|
char serial[64];
|
||||||
|
sprintf(serial, "%" PRIX64, binfo.DeviceInfo.DeviceUID);
|
||||||
|
|
||||||
|
// Add the device to the list
|
||||||
|
devices.define(serial, serial, i);
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
Device_Close(&dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void select(const std::string& serial) {
|
||||||
|
// If there are no devices, give up
|
||||||
|
if (devices.empty()) {
|
||||||
|
selectedSerial.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the serial was not found, select the first available serial
|
||||||
|
if (!devices.keyExists(serial)) {
|
||||||
|
select(devices.key(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the menu ID
|
||||||
|
devId = devices.keyId(serial);
|
||||||
|
selectedDevIndex = devices.value(devId);
|
||||||
|
|
||||||
|
// Set up the device parameters
|
||||||
|
BootProfile_TypeDef bprofile = {};
|
||||||
|
bprofile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
|
||||||
|
bprofile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortOnly;
|
||||||
|
|
||||||
|
// Working variables
|
||||||
|
BootInfo_TypeDef binfo;
|
||||||
|
|
||||||
|
// Attempt to open the device by ID
|
||||||
|
void* dev;
|
||||||
|
int ret = Device_Open(&dev, selectedDevIndex, &bprofile, &binfo);
|
||||||
|
if (ret < 0) {
|
||||||
|
flog::error("Could not open device: {}", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the default streaming parameters to query some info
|
||||||
|
IQS_Profile_TypeDef profile;
|
||||||
|
IQS_ProfileDeInit(&dev, &profile);
|
||||||
|
|
||||||
|
// Compute all available samplerates
|
||||||
|
samplerates.clear();
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
double sr = profile.NativeIQSampleRate_SPS / (double)(1 << i);
|
||||||
|
char buf[128];
|
||||||
|
sprintf(buf, "%.02fMHz", sr / 1e6);
|
||||||
|
samplerates.define(1 << i, buf, sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define RX ports
|
||||||
|
rxPorts.clear();
|
||||||
|
rxPorts.define("external", "External", ExternalPort);
|
||||||
|
rxPorts.define("internal", "Internal", InternalPort);
|
||||||
|
rxPorts.define("ant", "ANT", ANT_Port);
|
||||||
|
rxPorts.define("tr", "T/R", TR_Port);
|
||||||
|
rxPorts.define("swr", "SWR", SWR_Port);
|
||||||
|
rxPorts.define("int", "INT", INT_Port);
|
||||||
|
|
||||||
|
// Define gain strategies
|
||||||
|
gainStategies.clear();
|
||||||
|
gainStategies.define("lowNoise", "Low Noise", LowNoisePreferred);
|
||||||
|
gainStategies.define("highLinearity", "High Linearity", HighLinearityPreferred);
|
||||||
|
|
||||||
|
// Define preamplifier modes
|
||||||
|
preampModes.clear();
|
||||||
|
preampModes.define("auto", "Auto", AutoOn);
|
||||||
|
preampModes.define("off", "Off", ForcedOff);
|
||||||
|
|
||||||
|
// Define LO modes
|
||||||
|
loModes.clear();
|
||||||
|
loModes.define("auto", "Auto", LOOpt_Auto);
|
||||||
|
loModes.define("speed", "Speed", LOOpt_Speed);
|
||||||
|
loModes.define("spurs", "Spurs", LOOpt_Spur);
|
||||||
|
loModes.define("phaseNoise", "Phase Noise", LOOpt_PhaseNoise);
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
Device_Close(&dev);
|
||||||
|
|
||||||
|
// TODO: Load configuration
|
||||||
|
sampleRate = samplerates.value(0);
|
||||||
|
refLvl = 0;
|
||||||
|
portId = rxPorts.valueId(ExternalPort);
|
||||||
|
gainStratId = gainStategies.valueId(LowNoisePreferred);
|
||||||
|
preampModeId = preampModes.valueId(AutoOn);
|
||||||
|
ifAgc = false;
|
||||||
|
loModeId = loModes.valueId(LOOpt_Auto);
|
||||||
|
|
||||||
|
// Update the samplerate
|
||||||
|
core::setInputSampleRate(sampleRate);
|
||||||
|
|
||||||
|
// Save serial number
|
||||||
|
selectedSerial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuSelected(void* ctx) {
|
||||||
|
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
flog::info("HarogicSourceModule '{0}': Menu Select!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuDeselected(void* ctx) {
|
||||||
|
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||||
|
flog::info("HarogicSourceModule '{0}': Menu Deselect!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(void* ctx) {
|
||||||
|
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||||
|
if (_this->running) { return; }
|
||||||
|
|
||||||
|
// Set up the device parameters
|
||||||
|
BootProfile_TypeDef bprofile = {};
|
||||||
|
bprofile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
|
||||||
|
bprofile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortAndPowerPort;
|
||||||
|
|
||||||
|
// Working variables
|
||||||
|
BootInfo_TypeDef binfo;
|
||||||
|
|
||||||
|
// Attempt to open the device by ID
|
||||||
|
int ret = Device_Open(&_this->openDev, _this->selectedDevIndex, &bprofile, &binfo);
|
||||||
|
if (ret < 0) {
|
||||||
|
flog::error("Could not open device: {}", ret);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the decimation amount
|
||||||
|
int dec = _this->samplerates.key(_this->samplerates.valueId(_this->sampleRate));
|
||||||
|
flog::debug("Using decimation factor: {}", dec);
|
||||||
|
|
||||||
|
// Decide to use either 8 or 16bit samples
|
||||||
|
_this->sampsInt8 = (_this->sampleRate > 64e6);
|
||||||
|
|
||||||
|
// Get the default configuration
|
||||||
|
IQS_ProfileDeInit(&_this->openDev, &_this->profile);
|
||||||
|
|
||||||
|
// Automatic configuration
|
||||||
|
_this->profile.Atten = -1;
|
||||||
|
_this->profile.BusTimeout_ms = 100;
|
||||||
|
_this->profile.TriggerSource = Bus;
|
||||||
|
_this->profile.TriggerMode = Adaptive;
|
||||||
|
_this->profile.DataFormat = _this->sampsInt8 ? Complex8bit : Complex16bit;
|
||||||
|
|
||||||
|
// User selectable config
|
||||||
|
_this->profile.CenterFreq_Hz = _this->freq;
|
||||||
|
_this->profile.RefLevel_dBm = _this->refLvl;
|
||||||
|
_this->profile.DecimateFactor = dec;
|
||||||
|
_this->profile.RxPort = _this->rxPorts.value(_this->portId);
|
||||||
|
_this->profile.GainStrategy = _this->gainStategies.value(_this->gainStratId);
|
||||||
|
_this->profile.Preamplifier = _this->preampModes.value(_this->preampModeId);
|
||||||
|
_this->profile.EnableIFAGC = _this->ifAgc;
|
||||||
|
_this->profile.LOOptimization = _this->loModes.value(_this->loModeId);
|
||||||
|
|
||||||
|
// Apply the configuration
|
||||||
|
IQS_StreamInfo_TypeDef info;
|
||||||
|
ret = IQS_Configuration(&_this->openDev, &_this->profile, &_this->profile, &info);
|
||||||
|
if (ret < 0) {
|
||||||
|
flog::error("Could not configure device: {}", ret);
|
||||||
|
Device_Close(&_this->openDev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the stream configuration
|
||||||
|
_this->bufferSize = info.PacketSamples;
|
||||||
|
flog::debug("Got buffer size: {}", _this->bufferSize);
|
||||||
|
|
||||||
|
// Start the stream
|
||||||
|
ret = IQS_BusTriggerStart(&_this->openDev);
|
||||||
|
if (ret < 0) {
|
||||||
|
flog::error("Could not start stream: {}", ret);
|
||||||
|
Device_Close(&_this->openDev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start worker
|
||||||
|
_this->run = true;
|
||||||
|
_this->workerThread = std::thread(&HarogicSourceModule::worker, _this);
|
||||||
|
|
||||||
|
_this->running = true;
|
||||||
|
flog::info("HarogicSourceModule '{0}': Start!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop(void* ctx) {
|
||||||
|
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||||
|
if (!_this->running) { return; }
|
||||||
|
_this->running = false;
|
||||||
|
|
||||||
|
// Stop worker
|
||||||
|
_this->run = false;
|
||||||
|
_this->stream.stopWriter();
|
||||||
|
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||||
|
_this->stream.clearWriteStop();
|
||||||
|
|
||||||
|
// Stop the stream
|
||||||
|
IQS_BusTriggerStop(&_this->openDev);
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
Device_Close(&_this->openDev);
|
||||||
|
|
||||||
|
flog::info("HarogicSourceModule '{0}': Stop!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune(double freq, void* ctx) {
|
||||||
|
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||||
|
if (_this->running) {
|
||||||
|
// Update the frequency in the configuration
|
||||||
|
_this->profile.CenterFreq_Hz = freq;
|
||||||
|
_this->applyProfile();
|
||||||
|
}
|
||||||
|
_this->freq = freq;
|
||||||
|
flog::info("HarogicSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::BeginDisabled(); }
|
||||||
|
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Combo(CONCAT("##_harogic_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
||||||
|
_this->select(_this->devices.key(_this->devId));
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SmGui::Combo(CONCAT("##_harogic_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
|
||||||
|
_this->sampleRate = _this->samplerates.value(_this->srId);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::SameLine();
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Button(CONCAT("Refresh##_harogic_refr_", _this->name))) {
|
||||||
|
_this->refresh();
|
||||||
|
_this->select(_this->selectedSerial);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
SmGui::LeftLabel("RX Port");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_harogic_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
_this->profile.RxPort = _this->rxPorts.value(_this->portId);
|
||||||
|
_this->applyProfile();
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("LO Mode");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_lo_mode_", _this->name), &_this->loModeId, _this->loModes.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
_this->profile.LOOptimization = _this->loModes.value(_this->loModeId);
|
||||||
|
_this->applyProfile();
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Gain Mode");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_harogic_gain_mode_", _this->name), &_this->gainStratId, _this->gainStategies.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
_this->profile.GainStrategy = _this->gainStategies.value(_this->gainStratId);
|
||||||
|
_this->applyProfile();
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Preamp Mode");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_harogic_preamp_mode_", _this->name), &_this->preampModeId, _this->preampModes.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
_this->profile.Preamplifier = _this->preampModes.value(_this->preampModeId);
|
||||||
|
_this->applyProfile();
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Reference");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_harogic_ref_", _this->name), &_this->refLvl, _this->minRef, _this->maxRef)) {
|
||||||
|
if (_this->running) {
|
||||||
|
_this->profile.RefLevel_dBm = _this->refLvl;
|
||||||
|
_this->applyProfile();
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SmGui::Checkbox(CONCAT("IF AGC##_harogic_if_agc_", _this->name), &_this->ifAgc)) {
|
||||||
|
if (_this->running) {
|
||||||
|
_this->profile.EnableIFAGC = _this->ifAgc;
|
||||||
|
_this->applyProfile();
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void applyProfile() {
|
||||||
|
// Acquire device
|
||||||
|
std::lock_guard<std::mutex> lck(devMtx);
|
||||||
|
|
||||||
|
// Configure the device
|
||||||
|
IQS_StreamInfo_TypeDef info;
|
||||||
|
int ret = IQS_Configuration(&openDev, &profile, &profile, &info);
|
||||||
|
if (ret < 0) {
|
||||||
|
flog::error("Failed to apply tuning config: {}", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-trigger the stream
|
||||||
|
ret = IQS_BusTriggerStart(&openDev);
|
||||||
|
if (ret < 0) {
|
||||||
|
flog::error("Could not start stream: {}", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker() {
|
||||||
|
// Allocate sample buffer
|
||||||
|
int realSamps = bufferSize*2;
|
||||||
|
IQStream_TypeDef iqs;
|
||||||
|
|
||||||
|
// Define number of buffers per swap to maintain 200 fps
|
||||||
|
int maxBufCount = STREAM_BUFFER_SIZE / bufferSize;
|
||||||
|
int bufCount = (sampleRate / bufferSize) / 200;
|
||||||
|
if (bufCount <= 0) { bufCount = 1; }
|
||||||
|
if (bufCount > maxBufCount) { bufCount = maxBufCount; }
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
flog::debug("Swapping will be done {} buffers at a time", bufCount);
|
||||||
|
|
||||||
|
// Worker loop
|
||||||
|
while (run) {
|
||||||
|
// Read samples
|
||||||
|
devMtx.lock();
|
||||||
|
int ret = IQS_GetIQStream_PM1(&openDev, &iqs);
|
||||||
|
devMtx.unlock();
|
||||||
|
if (ret < 0) {
|
||||||
|
if (ret == APIRETVAL_WARNING_BusTimeOut) {
|
||||||
|
flog::warn("Stream timed out");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (ret <= APIRETVAL_WARNING_IFOverflow && ret >= APIRETVAL_WARNING_ADCConfigError) {
|
||||||
|
// Just warnings, do nothing
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
flog::error("Streaming error: {}", ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert them to floating point
|
||||||
|
if (sampsInt8) {
|
||||||
|
volk_8i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*bufferSize], (int8_t*)iqs.AlternIQStream, 128.0f, realSamps);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*bufferSize], (int16_t*)iqs.AlternIQStream, 32768.0f, realSamps);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send them off if we have enough
|
||||||
|
if (count >= bufCount) {
|
||||||
|
count = 0;
|
||||||
|
if (!stream.swap(bufferSize*bufCount)) { break; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
dsp::stream<dsp::complex_t> stream;
|
||||||
|
double sampleRate;
|
||||||
|
SourceManager::SourceHandler handler;
|
||||||
|
bool running = false;
|
||||||
|
double freq;
|
||||||
|
|
||||||
|
OptionList<std::string, int> devices;
|
||||||
|
OptionList<int, double> samplerates;
|
||||||
|
OptionList<std::string, RxPort_TypeDef> rxPorts;
|
||||||
|
OptionList<std::string, GainStrategy_TypeDef> gainStategies;
|
||||||
|
OptionList<std::string, PreamplifierState_TypeDef> preampModes;
|
||||||
|
OptionList<std::string, LOOptimization_TypeDef> loModes;
|
||||||
|
int devId = 0;
|
||||||
|
int srId = 0;
|
||||||
|
int refLvl = -30;
|
||||||
|
int minRef = -100;
|
||||||
|
int maxRef = 7;
|
||||||
|
int portId = 0;
|
||||||
|
int gainStratId = 0;
|
||||||
|
int preampModeId = 0;
|
||||||
|
int loModeId = 0;
|
||||||
|
bool ifAgc = false;
|
||||||
|
std::string selectedSerial;
|
||||||
|
int selectedDevIndex;
|
||||||
|
|
||||||
|
void* openDev;
|
||||||
|
IQS_Profile_TypeDef profile;
|
||||||
|
|
||||||
|
int bufferSize;
|
||||||
|
std::thread workerThread;
|
||||||
|
std::atomic<bool> run = false;
|
||||||
|
std::mutex devMtx;
|
||||||
|
bool sampsInt8;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new HarogicSourceModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (HarogicSourceModule*)instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _END_() {
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
36
source_modules/hydrasdr_source/CMakeLists.txt
Normal file
36
source_modules/hydrasdr_source/CMakeLists.txt
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(hydrasdr_source)
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
target_link_directories(hydrasdr_source PRIVATE "C:/Program Files (x86)/hydrasdr_all/bin/")
|
||||||
|
|
||||||
|
target_include_directories(hydrasdr_source PUBLIC "C:/Program Files (x86)/hydrasdr_all/include/libhydrasdr")
|
||||||
|
|
||||||
|
target_link_libraries(hydrasdr_source PRIVATE hydrasdr)
|
||||||
|
elseif (ANDROID)
|
||||||
|
target_include_directories(hydrasdr_source PUBLIC
|
||||||
|
/sdr-kit/${ANDROID_ABI}/include/libhydrasdr
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(hydrasdr_source PUBLIC
|
||||||
|
/sdr-kit/${ANDROID_ABI}/lib/libusb1.0.so
|
||||||
|
/sdr-kit/${ANDROID_ABI}/lib/libhydrasdr.so
|
||||||
|
)
|
||||||
|
else (MSVC)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBHYDRASDR REQUIRED libhydrasdr)
|
||||||
|
|
||||||
|
target_include_directories(hydrasdr_source PRIVATE ${LIBHYDRASDR_INCLUDE_DIRS})
|
||||||
|
target_link_directories(hydrasdr_source PRIVATE ${LIBHYDRASDR_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(hydrasdr_source PRIVATE ${LIBHYDRASDR_LIBRARIES})
|
||||||
|
|
||||||
|
# Include it because for some reason pkgconfig doesn't look here?
|
||||||
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||||
|
target_include_directories(hydrasdr_source PRIVATE "/usr/local/include")
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
639
source_modules/hydrasdr_source/src/main.cpp
Normal file
639
source_modules/hydrasdr_source/src/main.cpp
Normal file
@@ -0,0 +1,639 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include <utils/flog.h>
|
||||||
|
#include <module.h>
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <core.h>
|
||||||
|
#include <gui/style.h>
|
||||||
|
#include <config.h>
|
||||||
|
#include <gui/smgui.h>
|
||||||
|
#include <hydrasdr.h>
|
||||||
|
#include <utils/optionlist.h>
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <android_backend.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "hydrasdr_source",
|
||||||
|
/* Description: */ "HydraSDR source module for SDR++",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ 1
|
||||||
|
};
|
||||||
|
|
||||||
|
ConfigManager config;
|
||||||
|
|
||||||
|
class HydraSDRSourceModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
HydraSDRSourceModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
// Define the ports
|
||||||
|
ports.define("rx0", "RX0", RF_PORT_RX0);
|
||||||
|
ports.define("rx1", "RX1", RF_PORT_RX1);
|
||||||
|
ports.define("rx2", "RX2", RF_PORT_RX2);
|
||||||
|
|
||||||
|
regStr[0] = 0;
|
||||||
|
valStr[0] = 0;
|
||||||
|
|
||||||
|
sampleRate = 10000000.0;
|
||||||
|
|
||||||
|
handler.ctx = this;
|
||||||
|
handler.selectHandler = menuSelected;
|
||||||
|
handler.deselectHandler = menuDeselected;
|
||||||
|
handler.menuHandler = menuHandler;
|
||||||
|
handler.startHandler = start;
|
||||||
|
handler.stopHandler = stop;
|
||||||
|
handler.tuneHandler = tune;
|
||||||
|
handler.stream = &stream;
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
|
||||||
|
// Select device from config
|
||||||
|
config.acquire();
|
||||||
|
std::string devSerial = config.conf["device"];
|
||||||
|
config.release();
|
||||||
|
selectByString(devSerial);
|
||||||
|
|
||||||
|
sigpath::sourceManager.registerSource("HydraSDR", &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
~HydraSDRSourceModule() {
|
||||||
|
stop(this);
|
||||||
|
sigpath::sourceManager.unregisterSource("HydraSDR");;
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void refresh() {
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
devices.clear();
|
||||||
|
|
||||||
|
uint64_t serials[256];
|
||||||
|
int n = hydrasdr_list_devices(serials, 256);
|
||||||
|
|
||||||
|
char buf[1024];
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
sprintf(buf, "%016" PRIX64, serials[i]);
|
||||||
|
devices.define(buf, buf, serials[i]);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Check for device presence
|
||||||
|
int vid, pid;
|
||||||
|
devFd = backend::getDeviceFD(vid, pid, backend::HYDRASDR_VIDPIDS);
|
||||||
|
if (devFd < 0) { return; }
|
||||||
|
|
||||||
|
// Get device info
|
||||||
|
std::string fakeName = "HydraSDR USB";
|
||||||
|
devices.define(fakeName, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectFirst() {
|
||||||
|
if (!devices.empty()) {
|
||||||
|
selectBySerial(devices.value(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectByString(std::string serial) {
|
||||||
|
if (devices.keyExists(serial)) {
|
||||||
|
selectBySerial(devices.value(devices.keyId(serial)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectBySerial(uint64_t serial) {
|
||||||
|
hydrasdr_device* dev;
|
||||||
|
try {
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
int err = hydrasdr_open_sn(&dev, serial);
|
||||||
|
#else
|
||||||
|
int err = hydrasdr_open_fd(&dev, devFd);
|
||||||
|
#endif
|
||||||
|
if (err != 0) {
|
||||||
|
char buf[1024];
|
||||||
|
sprintf(buf, "%016" PRIX64, serial);
|
||||||
|
flog::error("Could not open HydraSDR {0}", buf);
|
||||||
|
selectedSerial = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e) {
|
||||||
|
char buf[1024];
|
||||||
|
sprintf(buf, "%016" PRIX64, serial);
|
||||||
|
flog::error("Could not open HydraSDR {}", buf);
|
||||||
|
}
|
||||||
|
devId = devices.valueId(serial);
|
||||||
|
selectedSerial = serial;
|
||||||
|
selectedSerStr = devices.key(devId);
|
||||||
|
|
||||||
|
uint32_t sampleRates[256];
|
||||||
|
hydrasdr_get_samplerates(dev, sampleRates, 0);
|
||||||
|
int n = sampleRates[0];
|
||||||
|
hydrasdr_get_samplerates(dev, sampleRates, n);
|
||||||
|
samplerates.clear();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
samplerates.define(sampleRates[i], getBandwdithScaled(sampleRates[i]), sampleRates[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config here
|
||||||
|
config.acquire();
|
||||||
|
bool created = false;
|
||||||
|
if (!config.conf["devices"].contains(selectedSerStr)) {
|
||||||
|
created = true;
|
||||||
|
config.conf["devices"][selectedSerStr]["sampleRate"] = 10000000;
|
||||||
|
config.conf["devices"][selectedSerStr]["gainMode"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["sensitiveGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["linearGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["lnaGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["mixerGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["vgaGain"] = 0;
|
||||||
|
config.conf["devices"][selectedSerStr]["lnaAgc"] = false;
|
||||||
|
config.conf["devices"][selectedSerStr]["mixerAgc"] = false;
|
||||||
|
config.conf["devices"][selectedSerStr]["biasT"] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load sample rate
|
||||||
|
srId = 0;
|
||||||
|
sampleRate = samplerates.value(0);
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("sampleRate")) {
|
||||||
|
int selectedSr = config.conf["devices"][selectedSerStr]["sampleRate"];
|
||||||
|
if (samplerates.keyExists(selectedSr)) {
|
||||||
|
srId = samplerates.keyId(selectedSr);
|
||||||
|
sampleRate = samplerates[srId];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load port
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("port")) {
|
||||||
|
std::string portStr = config.conf["devices"][selectedSerStr]["port"];
|
||||||
|
if (ports.keyExists(portStr)) {
|
||||||
|
portId = ports.keyId(portStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load gains
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("gainMode")) {
|
||||||
|
gainMode = config.conf["devices"][selectedSerStr]["gainMode"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("sensitiveGain")) {
|
||||||
|
sensitiveGain = config.conf["devices"][selectedSerStr]["sensitiveGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("linearGain")) {
|
||||||
|
linearGain = config.conf["devices"][selectedSerStr]["linearGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("lnaGain")) {
|
||||||
|
lnaGain = config.conf["devices"][selectedSerStr]["lnaGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("mixerGain")) {
|
||||||
|
mixerGain = config.conf["devices"][selectedSerStr]["mixerGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("vgaGain")) {
|
||||||
|
vgaGain = config.conf["devices"][selectedSerStr]["vgaGain"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("lnaAgc")) {
|
||||||
|
lnaAgc = config.conf["devices"][selectedSerStr]["lnaAgc"];
|
||||||
|
}
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("mixerAgc")) {
|
||||||
|
mixerAgc = config.conf["devices"][selectedSerStr]["mixerAgc"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Bias-T
|
||||||
|
if (config.conf["devices"][selectedSerStr].contains("biasT")) {
|
||||||
|
biasT = config.conf["devices"][selectedSerStr]["biasT"];
|
||||||
|
}
|
||||||
|
|
||||||
|
config.release(created);
|
||||||
|
|
||||||
|
hydrasdr_close(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuSelected(void* ctx) {
|
||||||
|
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
flog::info("HydraSDRSourceModule '{0}': Menu Select!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuDeselected(void* ctx) {
|
||||||
|
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
|
||||||
|
flog::info("HydraSDRSourceModule '{0}': Menu Deselect!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(void* ctx) {
|
||||||
|
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
|
||||||
|
if (_this->running) { return; }
|
||||||
|
if (_this->selectedSerial == 0) {
|
||||||
|
flog::error("Tried to start HydraSDR source with null serial");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
int err = hydrasdr_open_sn(&_this->openDev, _this->selectedSerial);
|
||||||
|
#else
|
||||||
|
int err = hydrasdr_open_fd(&_this->openDev, _this->devFd);
|
||||||
|
#endif
|
||||||
|
if (err != 0) {
|
||||||
|
char buf[1024];
|
||||||
|
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
|
||||||
|
flog::error("Could not open HydraSDR {0}", buf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hydrasdr_set_samplerate(_this->openDev, _this->samplerates[_this->srId]);
|
||||||
|
hydrasdr_set_freq(_this->openDev, _this->freq);
|
||||||
|
|
||||||
|
hydrasdr_set_rf_port(_this->openDev, _this->ports[_this->portId]);
|
||||||
|
|
||||||
|
if (_this->gainMode == 0) {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||||
|
}
|
||||||
|
else if (_this->gainMode == 1) {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||||
|
}
|
||||||
|
else if (_this->gainMode == 2) {
|
||||||
|
if (_this->lnaAgc) {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
if (_this->mixerAgc) {
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||||
|
}
|
||||||
|
hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
hydrasdr_set_rf_bias(_this->openDev, _this->biasT);
|
||||||
|
|
||||||
|
hydrasdr_start_rx(_this->openDev, callback, _this);
|
||||||
|
|
||||||
|
_this->running = true;
|
||||||
|
flog::info("HydraSDRSourceModule '{0}': Start!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop(void* ctx) {
|
||||||
|
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
|
||||||
|
if (!_this->running) { return; }
|
||||||
|
_this->running = false;
|
||||||
|
_this->stream.stopWriter();
|
||||||
|
hydrasdr_close(_this->openDev);
|
||||||
|
_this->stream.clearWriteStop();
|
||||||
|
flog::info("HydraSDRSourceModule '{0}': Stop!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune(double freq, void* ctx) {
|
||||||
|
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_freq(_this->openDev, freq);
|
||||||
|
}
|
||||||
|
_this->freq = freq;
|
||||||
|
flog::info("HydraSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::BeginDisabled(); }
|
||||||
|
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Combo(CONCAT("##_hydrasdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
||||||
|
_this->selectBySerial(_this->devices[_this->devId]);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["device"] = _this->selectedSerStr;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SmGui::Combo(CONCAT("##_hydrasdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
|
||||||
|
_this->sampleRate = _this->samplerates[_this->srId];
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->samplerates.key(_this->srId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::SameLine();
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Button(CONCAT("Refresh##_hydrasdr_refr_", _this->name))) {
|
||||||
|
_this->refresh();
|
||||||
|
config.acquire();
|
||||||
|
std::string devSerial = config.conf["device"];
|
||||||
|
config.release();
|
||||||
|
_this->selectByString(devSerial);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Antenna Port");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_hydrasdr_port_", _this->name), &_this->portId, _this->ports.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_rf_port(_this->openDev, _this->ports[_this->portId]);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["port"] = _this->ports.key(_this->portId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::BeginGroup();
|
||||||
|
SmGui::Columns(3, CONCAT("HydraSDRGainModeColumns##_", _this->name), false);
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::RadioButton(CONCAT("Sensitive##_hydrasdr_gm_", _this->name), _this->gainMode == 0)) {
|
||||||
|
_this->gainMode = 0;
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::NextColumn();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::RadioButton(CONCAT("Linear##_hydrasdr_gm_", _this->name), _this->gainMode == 1)) {
|
||||||
|
_this->gainMode = 1;
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::NextColumn();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::RadioButton(CONCAT("Free##_hydrasdr_gm_", _this->name), _this->gainMode == 2)) {
|
||||||
|
_this->gainMode = 2;
|
||||||
|
if (_this->running) {
|
||||||
|
if (_this->lnaAgc) {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
if (_this->mixerAgc) {
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||||
|
}
|
||||||
|
hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::Columns(1, CONCAT("EndHydraSDRGainModeColumns##_", _this->name), false);
|
||||||
|
SmGui::EndGroup();
|
||||||
|
|
||||||
|
// Gain menus
|
||||||
|
|
||||||
|
if (_this->gainMode == 0) {
|
||||||
|
SmGui::LeftLabel("Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_hydrasdr_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) {
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_this->gainMode == 1) {
|
||||||
|
SmGui::LeftLabel("Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_hydrasdr_lin_gain_", _this->name), &_this->linearGain, 0, 21)) {
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_this->gainMode == 2) {
|
||||||
|
// TODO: Switch to a table for alignment
|
||||||
|
if (_this->lnaAgc) { SmGui::BeginDisabled(); }
|
||||||
|
SmGui::LeftLabel("LNA Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_hydrasdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->lnaAgc) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
if (_this->mixerAgc) { SmGui::BeginDisabled(); }
|
||||||
|
SmGui::LeftLabel("Mixer Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_hydrasdr_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->mixerAgc) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
SmGui::LeftLabel("VGA Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_hydrasdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AGC Control
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Checkbox(CONCAT("LNA AGC##_hydrasdr_", _this->name), &_this->lnaAgc)) {
|
||||||
|
if (_this->running) {
|
||||||
|
if (_this->lnaAgc) {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hydrasdr_set_lna_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Checkbox(CONCAT("Mixer AGC##_hydrasdr_", _this->name), &_this->mixerAgc)) {
|
||||||
|
if (_this->running) {
|
||||||
|
if (_this->mixerAgc) {
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hydrasdr_set_mixer_agc(_this->openDev, 0);
|
||||||
|
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bias T
|
||||||
|
if (SmGui::Checkbox(CONCAT("Bias T##_hydrasdr_", _this->name), &_this->biasT)) {
|
||||||
|
if (_this->running) {
|
||||||
|
hydrasdr_set_rf_bias(_this->openDev, _this->biasT);
|
||||||
|
}
|
||||||
|
if (_this->selectedSerStr != "") {
|
||||||
|
config.acquire();
|
||||||
|
config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char valStr[256];
|
||||||
|
char regStr[256];
|
||||||
|
|
||||||
|
static int callback(hydrasdr_transfer_t* transfer) {
|
||||||
|
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)transfer->ctx;
|
||||||
|
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
||||||
|
if (!_this->stream.swap(transfer->sample_count)) { return -1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
hydrasdr_device* openDev;
|
||||||
|
bool enabled = true;
|
||||||
|
dsp::stream<dsp::complex_t> stream;
|
||||||
|
double sampleRate;
|
||||||
|
SourceManager::SourceHandler handler;
|
||||||
|
bool running = false;
|
||||||
|
double freq;
|
||||||
|
uint64_t selectedSerial = 0;
|
||||||
|
std::string selectedSerStr = "";
|
||||||
|
int devId = 0;
|
||||||
|
int srId = 0;
|
||||||
|
int portId = 0;
|
||||||
|
|
||||||
|
bool biasT = false;
|
||||||
|
|
||||||
|
int lnaGain = 0;
|
||||||
|
int vgaGain = 0;
|
||||||
|
int mixerGain = 0;
|
||||||
|
int linearGain = 0;
|
||||||
|
int sensitiveGain = 0;
|
||||||
|
|
||||||
|
int gainMode = 0;
|
||||||
|
|
||||||
|
bool lnaAgc = false;
|
||||||
|
bool mixerAgc = false;
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
int devFd = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
OptionList<std::string, uint64_t> devices;
|
||||||
|
OptionList<uint32_t, uint32_t> samplerates;
|
||||||
|
OptionList<std::string, hydrasdr_rf_port_t> ports;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
json def = json({});
|
||||||
|
def["devices"] = json({});
|
||||||
|
def["device"] = "";
|
||||||
|
config.setPath(core::args["root"].s() + "/hydrasdr_config.json");
|
||||||
|
config.load(def);
|
||||||
|
config.enableAutoSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new HydraSDRSourceModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||||
|
delete (HydraSDRSourceModule*)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;
|
||||||
}
|
}
|
||||||
@@ -163,6 +176,9 @@ private:
|
|||||||
// Construct the device name
|
// Construct the device name
|
||||||
std::string devName = '(' + backend + ") " + model + " [" + serial + ']';
|
std::string devName = '(' + backend + ") " + model + " [" + serial + ']';
|
||||||
|
|
||||||
|
// Skip duplicate devices
|
||||||
|
if (devices.keyExists(desc) || devices.nameExists(devName) || devices.valueExists(duri)) { continue; }
|
||||||
|
|
||||||
// Save device
|
// Save device
|
||||||
devices.define(desc, devName, duri);
|
devices.define(desc, devName, duri);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,17 @@ file(GLOB SRC "src/*.cpp")
|
|||||||
|
|
||||||
include(${SDRPP_MODULE_CMAKE})
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
target_include_directories(rfnm_source PRIVATE "C:/Users/ryzerth/Documents/Code/librfnm/include/librfnm")
|
if (MSVC)
|
||||||
target_link_directories(rfnm_source PRIVATE "C:/Users/ryzerth/Documents/Code/librfnm/build/Release")
|
# Lib path
|
||||||
target_link_libraries(rfnm_source PRIVATE librfnm)
|
target_link_directories(rfnm_source PRIVATE "C:/Program Files/RFNM/lib/")
|
||||||
|
target_include_directories(rfnm_source PUBLIC "C:/Program Files/RFNM/include/")
|
||||||
|
target_link_libraries(rfnm_source PRIVATE rfnm)
|
||||||
|
else (MSVC)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBRFNM REQUIRED librfnm)
|
||||||
|
|
||||||
|
target_include_directories(rfnm_source PRIVATE ${LIBRFNM_INCLUDE_DIRS})
|
||||||
|
target_link_directories(rfnm_source PRIVATE ${LIBRFNM_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(rfnm_source PRIVATE ${LIBRFNM_LIBRARIES})
|
||||||
|
endif ()
|
||||||
@@ -3,9 +3,10 @@
|
|||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <gui/smgui.h>
|
#include <gui/smgui.h>
|
||||||
#include <signal_path/signal_path.h>
|
#include <signal_path/signal_path.h>
|
||||||
#include <librfnm.h>
|
#include <librfnm/librfnm.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
#include <utils/optionlist.h>
|
#include <utils/optionlist.h>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
SDRPP_MOD_INFO{
|
SDRPP_MOD_INFO{
|
||||||
/* Name: */ "rfnm_source",
|
/* Name: */ "rfnm_source",
|
||||||
@@ -90,8 +91,131 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Open the device
|
// Open the device
|
||||||
// librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
|
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
|
||||||
|
|
||||||
|
// Define samplerates
|
||||||
|
samplerates.clear();
|
||||||
|
samplerates.define(61440000, "61.44 MHz", 2);
|
||||||
|
samplerates.define(122880000, "122.88 MHz", 1);
|
||||||
|
|
||||||
|
// Define daughterboards
|
||||||
|
daughterboards.clear();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
// If not present, skip
|
||||||
|
if (!dev->s->hwinfo.daughterboard[i].board_id) { continue; }
|
||||||
|
|
||||||
|
// Format the daughterboard name
|
||||||
|
std::string name = (i ? "[SEC] " : "[PRI] ") + std::string(dev->s->hwinfo.daughterboard[i].user_readable_name);
|
||||||
|
|
||||||
|
// Add the daughterboard to the list
|
||||||
|
daughterboards.define(name, name, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load options (TODO)
|
||||||
|
srId = samplerates.keyId(61440000);
|
||||||
|
dgbId = 0;
|
||||||
|
|
||||||
|
// Select the daughterboard
|
||||||
|
selectDaughterboard(dev, 0);
|
||||||
|
|
||||||
|
// Update samplerate
|
||||||
|
sampleRate = samplerates.key(srId);
|
||||||
|
|
||||||
|
// Close device
|
||||||
|
delete dev;
|
||||||
|
|
||||||
|
// Save serial number
|
||||||
|
selectedSerial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PathConfig {
|
||||||
|
rfnm_rf_path path;
|
||||||
|
int chId;
|
||||||
|
uint16_t appliesCh;
|
||||||
|
|
||||||
|
bool operator==(const PathConfig& b) const {
|
||||||
|
return b.path == path;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void selectDaughterboard(librfnm* dev, int id) {
|
||||||
|
// If no daugherboard is populated, give up
|
||||||
|
if (!dev->s->hwinfo.daughterboard[0].board_id && !dev->s->hwinfo.daughterboard[1].board_id) {
|
||||||
|
flog::error("The selected device has no daughterboards");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the ID is not populated, select the other one
|
||||||
|
if (id >= 2 || !dev->s->hwinfo.daughterboard[id].board_id) {
|
||||||
|
selectDaughterboard(dev, 1 - id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the channel offset
|
||||||
|
int offset = 0;
|
||||||
|
for (int i = 0; i < id; i++) {
|
||||||
|
offset += dev->s->hwinfo.daughterboard[i].rx_ch_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define antenna paths by going through all channels
|
||||||
|
paths.clear();
|
||||||
|
int count = dev->s->hwinfo.daughterboard[id].rx_ch_cnt;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Go through each possible path
|
||||||
|
for (int j = 0; j < 10; j++) {
|
||||||
|
// If it's the null path, stop searching
|
||||||
|
rfnm_rf_path path = dev->s->rx.ch[offset + i].path_possible[j];
|
||||||
|
if (path == RFNM_PATH_NULL) { continue; }
|
||||||
|
|
||||||
|
// Get the path
|
||||||
|
PathConfig pc = { path, offset + i, (uint16_t)(1 << (offset + i + 8))};
|
||||||
|
|
||||||
|
// If it's not in the list, add it
|
||||||
|
if (!paths.valueExists(pc)) {
|
||||||
|
std::string name = librfnm::rf_path_to_string(pc.path);
|
||||||
|
std::string capName = name;
|
||||||
|
if (std::islower(capName[0])) { capName[0] = std::toupper(capName[0]); }
|
||||||
|
paths.define(name, capName, pc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the preferred path
|
||||||
|
PathConfig preferred_pc = { dev->s->rx.ch[offset + i].path_preferred, 0, 0 };
|
||||||
|
|
||||||
|
// Make sure the path is accessible or give up
|
||||||
|
if (!paths.valueExists(preferred_pc)) { continue; }
|
||||||
|
|
||||||
|
// Set this channel as the channel of its prefered path (cursed af but lazy)
|
||||||
|
const PathConfig& pc = paths.value(paths.valueId(preferred_pc));
|
||||||
|
((PathConfig*)&pc)->chId = offset + i;
|
||||||
|
((PathConfig*)&pc)->appliesCh = (uint16_t)(1 << (offset + i + 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump antenna paths
|
||||||
|
for (int i = 0; i < paths.size(); i++) {
|
||||||
|
flog::debug("PATH[{}]: Name={}, Ch={}, Path={}", i, paths.name(i), paths.value(i).chId, (int)paths.value(i).path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load configuration (TODO)
|
||||||
|
selectedPath = paths.key(0);
|
||||||
|
|
||||||
|
// Select antenna path
|
||||||
|
selectPath(dev, id, selectedPath);
|
||||||
|
|
||||||
|
// Save selected daughterboard
|
||||||
|
dgbId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectPath(librfnm* dev, int dgbId, const std::string& path) {
|
||||||
|
// If the path doesn't exist, select the first path
|
||||||
|
if (!paths.keyExists(path)) {
|
||||||
|
selectPath(dev, dgbId, paths.key(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save selected path
|
||||||
|
selectedPath = path;
|
||||||
|
pathId = paths.keyId(path);
|
||||||
|
currentPath = paths.value(pathId);
|
||||||
|
|
||||||
// Define bandwidths
|
// Define bandwidths
|
||||||
bandwidths.clear();
|
bandwidths.clear();
|
||||||
@@ -103,27 +227,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get gain range
|
// Get gain range
|
||||||
gainMin = -30;//dev->librfnm_s->rx.ch[0].gain_range.min;
|
gainMin = dev->s->rx.ch[currentPath.chId].gain_range.min;
|
||||||
gainMax = 60;//dev->librfnm_s->rx.ch[0].gain_range.max;
|
gainMax = dev->s->rx.ch[currentPath.chId].gain_range.max;
|
||||||
|
|
||||||
// // Close device
|
|
||||||
// delete dev;
|
|
||||||
|
|
||||||
// Define samplerates
|
|
||||||
samplerates.clear();
|
|
||||||
samplerates.define(61440000, "61.44 MHz", 2);
|
|
||||||
samplerates.define(122880000, "122.88 MHz", 1);
|
|
||||||
|
|
||||||
// TODO: Load options
|
|
||||||
srId = samplerates.keyId(61440000);
|
|
||||||
bwId = bandwidths.nameId("Auto");
|
|
||||||
gain = 0;
|
|
||||||
|
|
||||||
// Update samplerate
|
|
||||||
sampleRate = samplerates.key(srId);
|
|
||||||
|
|
||||||
// Save serial number
|
|
||||||
selectedSerial = serial;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void menuSelected(void* ctx) {
|
static void menuSelected(void* ctx) {
|
||||||
@@ -144,19 +249,6 @@ private:
|
|||||||
// Open the device
|
// Open the device
|
||||||
_this->openDev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
|
_this->openDev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
|
||||||
|
|
||||||
// Configure the device
|
|
||||||
_this->openDev->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
|
|
||||||
_this->openDev->librfnm_s->rx.ch[0].samp_freq_div_n = _this->samplerates[_this->srId];
|
|
||||||
_this->openDev->librfnm_s->rx.ch[0].freq = _this->freq;
|
|
||||||
_this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
|
|
||||||
_this->openDev->librfnm_s->rx.ch[0].rfic_lpf_bw = 100;
|
|
||||||
_this->openDev->librfnm_s->rx.ch[0].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
|
||||||
_this->openDev->librfnm_s->rx.ch[0].path = _this->openDev->librfnm_s->rx.ch[0].path_preferred;
|
|
||||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
|
||||||
if (fail != rfnm_api_failcode::RFNM_API_OK) {
|
|
||||||
flog::error("Failed to configure device: {}", (int)fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure the stream
|
// Configure the stream
|
||||||
_this->bufferSize = -1;
|
_this->bufferSize = -1;
|
||||||
_this->openDev->rx_stream(librfnm_stream_format::LIBRFNM_STREAM_FORMAT_CS16, &_this->bufferSize);
|
_this->openDev->rx_stream(librfnm_stream_format::LIBRFNM_STREAM_FORMAT_CS16, &_this->bufferSize);
|
||||||
@@ -171,7 +263,24 @@ private:
|
|||||||
_this->openDev->rx_qbuf(&_this->rxBuf[i]);
|
_this->openDev->rx_qbuf(&_this->rxBuf[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush buffers
|
||||||
|
_this->openDev->rx_flush();
|
||||||
|
|
||||||
|
// Configure the device
|
||||||
|
_this->openDev->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_ON;
|
||||||
|
_this->openDev->s->rx.ch[_this->currentPath.chId].samp_freq_div_n = _this->samplerates[_this->srId];
|
||||||
|
_this->openDev->s->rx.ch[_this->currentPath.chId].freq = _this->freq;
|
||||||
|
_this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain;
|
||||||
|
_this->openDev->s->rx.ch[_this->currentPath.chId].rfic_lpf_bw = 100;
|
||||||
|
_this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
||||||
|
_this->openDev->s->rx.ch[_this->currentPath.chId].path = _this->currentPath.path;
|
||||||
|
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||||
|
if (fail != rfnm_api_failcode::RFNM_API_OK) {
|
||||||
|
flog::error("Failed to configure device: {}", (int)fail);
|
||||||
|
}
|
||||||
|
|
||||||
// Start worker
|
// Start worker
|
||||||
|
_this->run = true;
|
||||||
_this->workerThread = std::thread(&RFNMSourceModule::worker, _this);
|
_this->workerThread = std::thread(&RFNMSourceModule::worker, _this);
|
||||||
|
|
||||||
_this->running = true;
|
_this->running = true;
|
||||||
@@ -184,13 +293,20 @@ private:
|
|||||||
_this->running = false;
|
_this->running = false;
|
||||||
|
|
||||||
// Stop worker
|
// Stop worker
|
||||||
|
_this->run = false;
|
||||||
_this->stream.stopWriter();
|
_this->stream.stopWriter();
|
||||||
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||||
_this->stream.clearWriteStop();
|
_this->stream.clearWriteStop();
|
||||||
|
|
||||||
|
// Stop the RX streaming
|
||||||
|
_this->openDev->rx_stream_stop();
|
||||||
|
|
||||||
// Disable channel
|
// Disable channel
|
||||||
_this->openDev->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
|
_this->openDev->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_OFF;
|
||||||
_this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
_this->openDev->set(_this->currentPath.appliesCh);
|
||||||
|
|
||||||
|
// Flush buffers
|
||||||
|
_this->openDev->rx_flush();
|
||||||
|
|
||||||
// Close device
|
// Close device
|
||||||
delete _this->openDev;
|
delete _this->openDev;
|
||||||
@@ -206,8 +322,8 @@ private:
|
|||||||
static void tune(double freq, void* ctx) {
|
static void tune(double freq, void* ctx) {
|
||||||
RFNMSourceModule* _this = (RFNMSourceModule*)ctx;
|
RFNMSourceModule* _this = (RFNMSourceModule*)ctx;
|
||||||
if (_this->running) {
|
if (_this->running) {
|
||||||
_this->openDev->librfnm_s->rx.ch[0].freq = freq;
|
_this->openDev->s->rx.ch[_this->currentPath.chId].freq = freq;
|
||||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||||
if (fail != rfnm_api_failcode::RFNM_API_OK) {
|
if (fail != rfnm_api_failcode::RFNM_API_OK) {
|
||||||
flog::error("Failed to tune: {}", (int)fail);
|
flog::error("Failed to tune: {}", (int)fail);
|
||||||
}
|
}
|
||||||
@@ -244,11 +360,48 @@ private:
|
|||||||
core::setInputSampleRate(_this->sampleRate);
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_this->daughterboards.size() > 1) {
|
||||||
|
SmGui::LeftLabel("Daughterboard");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_rfnm_dgb_sel_", _this->name), &_this->dgbId, _this->daughterboards.txt)) {
|
||||||
|
// Open the device
|
||||||
|
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
|
||||||
|
|
||||||
|
// Select the daughterboard
|
||||||
|
_this->selectDaughterboard(dev, _this->dgbId);
|
||||||
|
|
||||||
|
// Close device
|
||||||
|
delete dev;
|
||||||
|
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->paths.size() > 1) {
|
||||||
|
SmGui::LeftLabel("Antenna Path");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_rfnm_path_sel_", _this->name), &_this->pathId, _this->paths.txt)) {
|
||||||
|
// Open the device
|
||||||
|
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
|
||||||
|
|
||||||
|
// Select the atennna path
|
||||||
|
_this->selectPath(dev, _this->dgbId, _this->paths.key(_this->pathId));
|
||||||
|
|
||||||
|
// Close device
|
||||||
|
delete dev;
|
||||||
|
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (_this->running) { SmGui::EndDisabled(); }
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
SmGui::LeftLabel("Bandwidth");
|
SmGui::LeftLabel("Bandwidth");
|
||||||
SmGui::FillWidth();
|
SmGui::FillWidth();
|
||||||
if (SmGui::Combo(CONCAT("##_rfnm_bw_sel_", _this->name), &_this->bwId, _this->bandwidths.txt)) {
|
if (SmGui::Combo(CONCAT("##_rfnm_bw_sel_", _this->name), &_this->bwId, _this->bandwidths.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
// TODO: Set
|
||||||
|
}
|
||||||
// TODO: Save
|
// TODO: Save
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,16 +409,16 @@ private:
|
|||||||
SmGui::FillWidth();
|
SmGui::FillWidth();
|
||||||
if (SmGui::SliderInt(CONCAT("##_rfnm_gain_", _this->name), &_this->gain, _this->gainMin, _this->gainMax)) {
|
if (SmGui::SliderInt(CONCAT("##_rfnm_gain_", _this->name), &_this->gain, _this->gainMin, _this->gainMax)) {
|
||||||
if (_this->running) {
|
if (_this->running) {
|
||||||
_this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
|
_this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain;
|
||||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||||
}
|
}
|
||||||
// TODO: Save
|
// TODO: Save
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SmGui::Checkbox(CONCAT("FM Notch##_rfnm_", _this->name), &_this->fmNotch)) {
|
if (SmGui::Checkbox(CONCAT("FM Notch##_rfnm_", _this->name), &_this->fmNotch)) {
|
||||||
if (_this->running) {
|
if (_this->running) {
|
||||||
_this->openDev->librfnm_s->rx.ch[0].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
_this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
||||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||||
}
|
}
|
||||||
// TODO: Save
|
// TODO: Save
|
||||||
}
|
}
|
||||||
@@ -274,12 +427,18 @@ private:
|
|||||||
void worker() {
|
void worker() {
|
||||||
librfnm_rx_buf* lrxbuf;
|
librfnm_rx_buf* lrxbuf;
|
||||||
int sampCount = bufferSize/4;
|
int sampCount = bufferSize/4;
|
||||||
|
uint8_t ch = (1 << currentPath.chId);
|
||||||
|
|
||||||
// TODO: Define number of buffers per swap to maintain 200 fps
|
// Define number of buffers per swap to maintain 200 fps
|
||||||
|
int maxBufCount = STREAM_BUFFER_SIZE / sampCount;
|
||||||
|
int bufCount = (sampleRate / sampCount) / 200;
|
||||||
|
if (bufCount <= 0) { bufCount = 1; }
|
||||||
|
if (bufCount > maxBufCount) { bufCount = maxBufCount; }
|
||||||
|
|
||||||
while (true) {
|
int count = 0;
|
||||||
|
while (run) {
|
||||||
// Receive a buffer
|
// Receive a buffer
|
||||||
auto fail = openDev->rx_dqbuf(&lrxbuf, LIBRFNM_CH0, 1000);
|
auto fail = openDev->rx_dqbuf(&lrxbuf, ch, 1000);
|
||||||
if (fail == rfnm_api_failcode::RFNM_API_DQBUF_NO_DATA) {
|
if (fail == rfnm_api_failcode::RFNM_API_DQBUF_NO_DATA) {
|
||||||
flog::error("Dequeue buffer didn't have any data");
|
flog::error("Dequeue buffer didn't have any data");
|
||||||
continue;
|
continue;
|
||||||
@@ -287,13 +446,17 @@ private:
|
|||||||
else if (fail) { break; }
|
else if (fail) { break; }
|
||||||
|
|
||||||
// Convert buffer to CF32
|
// Convert buffer to CF32
|
||||||
volk_16i_s32f_convert_32f((float*)stream.writeBuf, (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
|
volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*sampCount], (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
|
||||||
|
|
||||||
// Reque buffer
|
// Reque buffer
|
||||||
openDev->rx_qbuf(lrxbuf);
|
openDev->rx_qbuf(lrxbuf);
|
||||||
|
|
||||||
// Swap data
|
// Swap data
|
||||||
if (!stream.swap(sampCount)) { break; }
|
if (count >= bufCount) {
|
||||||
|
if (!stream.swap(count*sampCount)) { break; }
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flog::debug("Worker exiting");
|
flog::debug("Worker exiting");
|
||||||
@@ -308,12 +471,15 @@ private:
|
|||||||
double freq;
|
double freq;
|
||||||
|
|
||||||
OptionList<std::string, std::string> devices;
|
OptionList<std::string, std::string> devices;
|
||||||
|
OptionList<std::string, int> daughterboards;
|
||||||
|
OptionList<std::string, PathConfig> paths;
|
||||||
OptionList<int, int> bandwidths;
|
OptionList<int, int> bandwidths;
|
||||||
OptionList<int, int> samplerates;
|
OptionList<int, int> samplerates;
|
||||||
int gainMin = 0;
|
int gainMin = 0;
|
||||||
int gainMax = 0;
|
int gainMax = 0;
|
||||||
|
|
||||||
int devId = 0;
|
int devId = 0;
|
||||||
|
int dgbId = 0;
|
||||||
|
int pathId = 0;
|
||||||
int srId = 0;
|
int srId = 0;
|
||||||
int bwId = 0;
|
int bwId = 0;
|
||||||
int gain = 0;
|
int gain = 0;
|
||||||
@@ -321,8 +487,11 @@ private:
|
|||||||
std::string selectedSerial;
|
std::string selectedSerial;
|
||||||
librfnm* openDev;
|
librfnm* openDev;
|
||||||
int bufferSize = -1;
|
int bufferSize = -1;
|
||||||
|
std::string selectedPath;
|
||||||
|
PathConfig currentPath;
|
||||||
librfnm_rx_buf rxBuf[LIBRFNM_MIN_RX_BUFCNT];
|
librfnm_rx_buf rxBuf[LIBRFNM_MIN_RX_BUFCNT];
|
||||||
|
|
||||||
|
std::atomic<bool> run = false;
|
||||||
std::thread workerThread;
|
std::thread workerThread;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
#define RFSPACE_MAX_SIZE 8192
|
#define RFSPACE_MAX_SIZE 8192
|
||||||
#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000
|
#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000
|
||||||
|
|||||||
@@ -121,14 +121,23 @@ public:
|
|||||||
#ifndef __ANDROID__
|
#ifndef __ANDROID__
|
||||||
devCount = rtlsdr_get_device_count();
|
devCount = rtlsdr_get_device_count();
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
char snBuf[1024];
|
char venBuf[256];
|
||||||
|
char prodBuf[256];
|
||||||
|
char snBuf[256];
|
||||||
for (int i = 0; i < devCount; i++) {
|
for (int i = 0; i < devCount; i++) {
|
||||||
// Gather device info
|
// Gather device info
|
||||||
const char* devName = rtlsdr_get_device_name(i);
|
const char* devName = rtlsdr_get_device_name(i);
|
||||||
int snErr = rtlsdr_get_device_usb_strings(i, NULL, NULL, snBuf);
|
int snErr = rtlsdr_get_device_usb_strings(i, venBuf, prodBuf, snBuf);
|
||||||
|
|
||||||
// Build name
|
// Build name
|
||||||
sprintf(buf, "[%s] %s##%d", (!snErr && snBuf[0]) ? snBuf : "No Serial", devName, i);
|
if (venBuf[0] && prodBuf[0]) {
|
||||||
|
sprintf(buf, "%s %s [%s]##%d", venBuf, prodBuf, (!snErr && snBuf[0]) ? snBuf : "No Serial", i);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sprintf(buf, "%s [%s]##%d", devName, (!snErr && snBuf[0]) ? snBuf : "No Serial", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add device to list
|
||||||
devNames.push_back(buf);
|
devNames.push_back(buf);
|
||||||
devListTxt += buf;
|
devListTxt += buf;
|
||||||
devListTxt += '\0';
|
devListTxt += '\0';
|
||||||
@@ -199,8 +208,6 @@ public:
|
|||||||
config.conf["devices"][selectedDevName]["tunerAgc"] = tunerAgc;
|
config.conf["devices"][selectedDevName]["tunerAgc"] = tunerAgc;
|
||||||
config.conf["devices"][selectedDevName]["gain"] = gainId;
|
config.conf["devices"][selectedDevName]["gain"] = gainId;
|
||||||
}
|
}
|
||||||
if (gainId >= gainList.size()) { gainId = gainList.size() - 1; }
|
|
||||||
updateGainTxt();
|
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
if (config.conf["devices"][selectedDevName].contains("sampleRate")) {
|
if (config.conf["devices"][selectedDevName].contains("sampleRate")) {
|
||||||
@@ -240,9 +247,11 @@ public:
|
|||||||
|
|
||||||
if (config.conf["devices"][selectedDevName].contains("gain")) {
|
if (config.conf["devices"][selectedDevName].contains("gain")) {
|
||||||
gainId = config.conf["devices"][selectedDevName]["gain"];
|
gainId = config.conf["devices"][selectedDevName]["gain"];
|
||||||
updateGainTxt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gainId >= gainList.size()) { gainId = gainList.size() - 1; }
|
||||||
|
updateGainTxt();
|
||||||
|
|
||||||
config.release(created);
|
config.release(created);
|
||||||
|
|
||||||
rtlsdr_close(openDev);
|
rtlsdr_close(openDev);
|
||||||
|
|||||||
9
source_modules/sddc_source/CMakeLists.txt
Normal file
9
source_modules/sddc_source/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(sddc_source)
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
add_subdirectory("./libsddc")
|
||||||
|
target_link_libraries(sddc_source PRIVATE sddc)
|
||||||
2
source_modules/sddc_source/libsddc/.gitignore
vendored
Normal file
2
source_modules/sddc_source/libsddc/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
build/
|
||||||
|
.vscode/
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user