mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2026-04-20 15:22:42 +00:00
Compare commits
117 Commits
fobos_test
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6df4d58e5 | ||
|
|
f817f7365b | ||
|
|
8eebbacb96 | ||
|
|
7be83ffe65 | ||
|
|
6fa945333c | ||
|
|
a3168f0365 | ||
|
|
acb96dd70a | ||
|
|
e8c73f4dab | ||
|
|
8c247c5679 | ||
|
|
ce34af01ee | ||
|
|
1f1315f015 | ||
|
|
11ffdcfd53 | ||
|
|
2330af01e5 | ||
|
|
621c031364 | ||
|
|
2bf3faebae | ||
|
|
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 | ||
|
|
e835c8dd9a |
370
.github/workflows/build_all.yml
vendored
370
.github/workflows/build_all.yml
vendored
@@ -50,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"
|
||||||
@@ -79,9 +79,12 @@ jobs:
|
|||||||
- name: Install libfobos
|
- name: Install libfobos
|
||||||
run: git clone https://github.com/AlexandreRouma/libfobos ; cd libfobos ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
|
run: git clone https://github.com/AlexandreRouma/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 -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
run: cmake -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
|
||||||
@@ -98,64 +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:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Create Build Environment
|
|
||||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako
|
|
||||||
|
|
||||||
- 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 ../../
|
|
||||||
|
|
||||||
- name: Install SDRplay API
|
|
||||||
run: wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.15.0.pkg && sudo installer -pkg SDRplayAPI-macos-installer-universal-3.15.0.pkg -target /
|
|
||||||
|
|
||||||
- name: Install libiio
|
|
||||||
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
|
|
||||||
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 ../../
|
|
||||||
|
|
||||||
- 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 ../../
|
|
||||||
|
|
||||||
- name: Install libperseus
|
|
||||||
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && sudo make install && cd ..
|
|
||||||
|
|
||||||
- name: Install librfnm
|
|
||||||
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
|
||||||
|
|
||||||
- name: Install libfobos
|
|
||||||
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
|
||||||
|
|
||||||
- name: Install more recent librtlsdr
|
|
||||||
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
|
|
||||||
|
|
||||||
- name: Prepare CMake
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: make VERBOSE=1 -j3
|
|
||||||
|
|
||||||
- name: Create Archive
|
|
||||||
working-directory: ${{runner.workspace}}
|
|
||||||
run: cd $GITHUB_WORKSPACE && sh make_macos_bundle.sh ${{runner.workspace}}/build ./SDR++.app && zip -r ${{runner.workspace}}/sdrpp_macos_intel.zip SDR++.app
|
|
||||||
|
|
||||||
- name: Save Archive
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: sdrpp_macos_intel
|
|
||||||
path: ${{runner.workspace}}/sdrpp_macos_intel.zip
|
|
||||||
|
|
||||||
build_macos_arm:
|
|
||||||
runs-on: macos-14
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@@ -176,7 +122,70 @@ 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
|
||||||
|
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||||
|
|
||||||
|
- name: Install libperseus
|
||||||
|
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && sudo make install && cd ..
|
||||||
|
|
||||||
|
- name: Install librfnm
|
||||||
|
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||||
|
|
||||||
|
- name: Install libfobos
|
||||||
|
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||||
|
|
||||||
|
- name: Install more recent librtlsdr
|
||||||
|
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
|
||||||
|
|
||||||
|
- name: 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: Install libdlcr
|
||||||
|
run: wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip && mkdir dlcr_host && cd dlcr_host && 7z x ../dlcr_host_v0.3.0.zip && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 .. -DCMAKE_BUILD_TYPE=Release && make -j2 && sudo make install && cd ../../
|
||||||
|
|
||||||
|
- name: Prepare CMake
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
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 -DOPT_BUILD_DRAGONLABS_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
run: make VERBOSE=1 -j3
|
||||||
|
|
||||||
|
- name: Create Archive
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: cd $GITHUB_WORKSPACE && sh make_macos_bundle.sh ${{runner.workspace}}/build ./SDR++.app && zip -r ${{runner.workspace}}/sdrpp_macos_intel.zip SDR++.app
|
||||||
|
|
||||||
|
- name: Save Archive
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: sdrpp_macos_intel
|
||||||
|
path: ${{runner.workspace}}/sdrpp_macos_intel.zip
|
||||||
|
|
||||||
|
build_macos_arm:
|
||||||
|
runs-on: macos-15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Create Build Environment
|
||||||
|
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako --break-system-packages
|
||||||
|
|
||||||
|
- 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 ../../
|
||||||
|
|
||||||
|
- name: Install SDRplay API
|
||||||
|
run: wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.15.0.pkg && sudo installer -pkg SDRplayAPI-macos-installer-universal-3.15.0.pkg -target /
|
||||||
|
|
||||||
|
- name: Install libiio
|
||||||
|
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
|
||||||
|
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 ../../
|
||||||
@@ -193,9 +202,15 @@ jobs:
|
|||||||
- 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: Install libdlcr
|
||||||
|
run: wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip && mkdir dlcr_host && cd dlcr_host && 7z x ../dlcr_host_v0.3.0.zip && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 .. -DCMAKE_BUILD_TYPE=Release && make -j2 && sudo make install && cd ../../
|
||||||
|
|
||||||
- name: Prepare CMake
|
- name: Prepare CMake
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -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 -DOPT_BUILD_DRAGONLABS_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
@@ -211,29 +226,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:
|
||||||
@@ -255,7 +248,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:
|
||||||
@@ -277,7 +292,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:
|
||||||
@@ -299,7 +380,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:
|
||||||
@@ -321,7 +424,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:
|
||||||
@@ -343,14 +468,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
|
||||||
@@ -362,10 +487,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:
|
||||||
@@ -387,32 +512,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
|
||||||
@@ -442,7 +562,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:
|
||||||
@@ -455,15 +594,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/badgesdr_source
|
||||||
|
decoder_modules/mystery_decoder
|
||||||
|
decoder_modules/tetra_decoder
|
||||||
|
misc_modules/modulation_monitor
|
||||||
@@ -14,11 +14,14 @@ option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Dependencies:
|
|||||||
option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)" ON)
|
option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)" ON)
|
||||||
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_DRAGONLABS_SOURCE "Build Dragon Labs Source Module (Dependencies: libdlcr)" 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_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF)
|
||||||
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
||||||
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
|
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
|
||||||
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
||||||
|
option(OPT_BUILD_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)
|
||||||
@@ -43,7 +46,7 @@ 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_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)
|
||||||
@@ -52,6 +55,7 @@ option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dep
|
|||||||
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_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
|
||||||
@@ -140,6 +144,10 @@ if (OPT_BUILD_BLADERF_SOURCE)
|
|||||||
add_subdirectory("source_modules/bladerf_source")
|
add_subdirectory("source_modules/bladerf_source")
|
||||||
endif (OPT_BUILD_BLADERF_SOURCE)
|
endif (OPT_BUILD_BLADERF_SOURCE)
|
||||||
|
|
||||||
|
if (OPT_BUILD_DRAGONLABS_SOURCE)
|
||||||
|
add_subdirectory("source_modules/dragonlabs_source")
|
||||||
|
endif (OPT_BUILD_DRAGONLABS_SOURCE)
|
||||||
|
|
||||||
if (OPT_BUILD_FILE_SOURCE)
|
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)
|
||||||
@@ -160,6 +168,14 @@ 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)
|
||||||
@@ -192,6 +208,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)
|
||||||
@@ -280,6 +300,10 @@ if (OPT_BUILD_RYFI_DECODER)
|
|||||||
add_subdirectory("decoder_modules/ryfi_decoder")
|
add_subdirectory("decoder_modules/ryfi_decoder")
|
||||||
endif (OPT_BUILD_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)
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project(Correct C)
|
project(Correct C)
|
||||||
include(CheckLibraryExists)
|
include(CheckLibraryExists)
|
||||||
include(CheckIncludeFiles)
|
include(CheckIncludeFiles)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -171,6 +171,8 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
defConfig["moduleInstances"]["Audio Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["Audio Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["BladeRF Source"]["module"] = "bladerf_source";
|
defConfig["moduleInstances"]["BladeRF Source"]["module"] = "bladerf_source";
|
||||||
defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
|
||||||
|
defConfig["moduleInstances"]["Dragon Labs Source"]["module"] = "dragonlabs_source";
|
||||||
|
defConfig["moduleInstances"]["Dragon Labs 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"]["module"] = "fobossdr_source";
|
||||||
@@ -181,8 +183,12 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true;
|
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"]["Network Source"]["module"] = "network_source";
|
||||||
|
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"]["module"] = "plutosdr_source";
|
||||||
@@ -230,12 +236,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;
|
||||||
|
|
||||||
@@ -286,6 +299,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";
|
||||||
@@ -316,12 +330,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()) {
|
||||||
|
|||||||
@@ -70,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",
|
||||||
|
|||||||
@@ -163,10 +163,10 @@ namespace dsp {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Processor<T, T>* blockBefore(Processor<T, T>* block) {
|
Processor<T, T>* blockBefore(Processor<T, T>* block) {
|
||||||
// TODO: This is wrong and must be fixed when I get more time
|
Processor<T, T>* prev = NULL;
|
||||||
for (auto& ln : links) {
|
for (auto& ln : links) {
|
||||||
if (ln == block) { return NULL; }
|
if (ln == block) { return prev; }
|
||||||
if (states[ln]) { return ln; }
|
if (states[ln]) { prev = ln; }
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,17 @@ namespace dsp::demod {
|
|||||||
dsp::taps::free(filterTaps);
|
dsp::taps::free(filterTaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass, bool highPass) {
|
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass) {
|
||||||
_samplerate = samplerate;
|
_samplerate = samplerate;
|
||||||
_bandwidth = bandwidth;
|
_bandwidth = bandwidth;
|
||||||
_lowPass = lowPass;
|
_lowPass = lowPass;
|
||||||
_highPass = highPass;
|
|
||||||
|
|
||||||
demod.init(NULL, bandwidth / 2.0, _samplerate);
|
demod.init(NULL, bandwidth / 2.0, _samplerate);
|
||||||
loadDummyTaps();
|
loadDummyTaps();
|
||||||
fir.init(NULL, filterTaps);
|
fir.init(NULL, filterTaps);
|
||||||
|
|
||||||
// Initialize taps
|
// Initialize taps
|
||||||
updateFilter(lowPass, highPass);
|
updateFilter(lowPass);
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, float>) {
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
demod.out.free();
|
demod.out.free();
|
||||||
@@ -49,7 +48,7 @@ namespace dsp::demod {
|
|||||||
base_type::tempStop();
|
base_type::tempStop();
|
||||||
_samplerate = samplerate;
|
_samplerate = samplerate;
|
||||||
demod.setDeviation(_bandwidth / 2.0, _samplerate);
|
demod.setDeviation(_bandwidth / 2.0, _samplerate);
|
||||||
updateFilter(_lowPass, _highPass);
|
updateFilter(_lowPass);
|
||||||
base_type::tempStart();
|
base_type::tempStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,19 +58,13 @@ namespace dsp::demod {
|
|||||||
if (bandwidth == _bandwidth) { return; }
|
if (bandwidth == _bandwidth) { return; }
|
||||||
_bandwidth = bandwidth;
|
_bandwidth = bandwidth;
|
||||||
demod.setDeviation(_bandwidth / 2.0, _samplerate);
|
demod.setDeviation(_bandwidth / 2.0, _samplerate);
|
||||||
updateFilter(_lowPass, _highPass);
|
updateFilter(_lowPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLowPass(bool lowPass) {
|
void setLowPass(bool lowPass) {
|
||||||
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);
|
||||||
updateFilter(lowPass, _highPass);
|
updateFilter(lowPass);
|
||||||
}
|
|
||||||
|
|
||||||
void setHighPass(bool highPass) {
|
|
||||||
assert(base_type::_block_init);
|
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
|
||||||
updateFilter(_lowPass, highPass);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() {
|
void reset() {
|
||||||
@@ -86,14 +79,14 @@ namespace dsp::demod {
|
|||||||
inline int process(int count, dsp::complex_t* in, T* out) {
|
inline int process(int count, dsp::complex_t* in, T* out) {
|
||||||
if constexpr (std::is_same_v<T, float>) {
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
demod.process(count, in, out);
|
demod.process(count, in, out);
|
||||||
if (filtering) {
|
if (_lowPass) {
|
||||||
std::lock_guard<std::mutex> lck(filterMtx);
|
std::lock_guard<std::mutex> lck(filterMtx);
|
||||||
fir.process(count, out, out);
|
fir.process(count, out, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if constexpr (std::is_same_v<T, stereo_t>) {
|
if constexpr (std::is_same_v<T, stereo_t>) {
|
||||||
demod.process(count, in, demod.out.writeBuf);
|
demod.process(count, in, demod.out.writeBuf);
|
||||||
if (filtering) {
|
if (_lowPass) {
|
||||||
std::lock_guard<std::mutex> lck(filterMtx);
|
std::lock_guard<std::mutex> lck(filterMtx);
|
||||||
fir.process(count, demod.out.writeBuf, demod.out.writeBuf);
|
fir.process(count, demod.out.writeBuf, demod.out.writeBuf);
|
||||||
}
|
}
|
||||||
@@ -114,25 +107,17 @@ namespace dsp::demod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateFilter(bool lowPass, bool highPass) {
|
void updateFilter(bool lowPass) {
|
||||||
std::lock_guard<std::mutex> lck(filterMtx);
|
std::lock_guard<std::mutex> lck(filterMtx);
|
||||||
|
|
||||||
// Update values
|
// Update values
|
||||||
_lowPass = lowPass;
|
_lowPass = lowPass;
|
||||||
_highPass = highPass;
|
|
||||||
filtering = (lowPass || highPass);
|
|
||||||
|
|
||||||
// Free filter taps
|
// Free filter taps
|
||||||
dsp::taps::free(filterTaps);
|
dsp::taps::free(filterTaps);
|
||||||
|
|
||||||
// Generate filter depending on low and high pass settings
|
// Generate filter depending on the low pass settings
|
||||||
if (_lowPass && _highPass) {
|
if (_lowPass) {
|
||||||
filterTaps = dsp::taps::bandPass<float>(300.0, _bandwidth / 2.0, 100.0, _samplerate);
|
|
||||||
}
|
|
||||||
else if (_highPass) {
|
|
||||||
filterTaps = dsp::taps::highPass(300.0, 100.0, _samplerate);
|
|
||||||
}
|
|
||||||
else if (_lowPass) {
|
|
||||||
filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
|
filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -152,7 +137,6 @@ namespace dsp::demod {
|
|||||||
double _samplerate;
|
double _samplerate;
|
||||||
double _bandwidth;
|
double _bandwidth;
|
||||||
bool _lowPass;
|
bool _lowPass;
|
||||||
bool _highPass;
|
|
||||||
bool filtering;
|
bool filtering;
|
||||||
|
|
||||||
Quadrature demod;
|
Quadrature demod;
|
||||||
|
|||||||
303
core/src/dsp/noise_reduction/ctcss_squelch.h
Normal file
303
core/src/dsp/noise_reduction/ctcss_squelch.h
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../channel/rx_vfo.h"
|
||||||
|
#include "../demod/quadrature.h"
|
||||||
|
#include "../filter/fir.h"
|
||||||
|
#include "../taps/high_pass.h"
|
||||||
|
#include <fftw3.h>
|
||||||
|
#include <map>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#define CTCSS_DECODE_SAMPLERATE 500//250.0
|
||||||
|
#define CTCSS_DECODE_BANDWIDTH 200.0
|
||||||
|
#define CTCSS_DECODE_OFFSET 160.55
|
||||||
|
|
||||||
|
namespace dsp::noise_reduction {
|
||||||
|
enum CTCSSTone {
|
||||||
|
/**
|
||||||
|
* Indicates that any valid tone will let audio through.
|
||||||
|
*/
|
||||||
|
CTCSS_TONE_ANY = -2,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that no tone is being received, or to act as a decoder only, letting audio through continuously.
|
||||||
|
*/
|
||||||
|
CTCSS_TONE_NONE = -1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CTCSS Tone Frequency.
|
||||||
|
*/
|
||||||
|
CTCSS_TONE_67Hz,
|
||||||
|
CTCSS_TONE_69_3Hz,
|
||||||
|
CTCSS_TONE_71_9Hz,
|
||||||
|
CTCSS_TONE_74_4Hz,
|
||||||
|
CTCSS_TONE_77Hz,
|
||||||
|
CTCSS_TONE_79_7Hz,
|
||||||
|
CTCSS_TONE_82_5Hz,
|
||||||
|
CTCSS_TONE_85_4Hz,
|
||||||
|
CTCSS_TONE_88_5Hz,
|
||||||
|
CTCSS_TONE_91_5Hz,
|
||||||
|
CTCSS_TONE_94_8Hz,
|
||||||
|
CTCSS_TONE_97_4Hz,
|
||||||
|
CTCSS_TONE_100Hz,
|
||||||
|
CTCSS_TONE_103_5Hz,
|
||||||
|
CTCSS_TONE_107_2Hz,
|
||||||
|
CTCSS_TONE_110_9Hz,
|
||||||
|
CTCSS_TONE_114_8Hz,
|
||||||
|
CTCSS_TONE_118_8Hz,
|
||||||
|
CTCSS_TONE_123Hz,
|
||||||
|
CTCSS_TONE_127_3Hz,
|
||||||
|
CTCSS_TONE_131_8Hz,
|
||||||
|
CTCSS_TONE_136_5Hz,
|
||||||
|
CTCSS_TONE_141_3Hz,
|
||||||
|
CTCSS_TONE_146_2Hz,
|
||||||
|
CTCSS_TONE_150Hz,
|
||||||
|
CTCSS_TONE_151_4Hz,
|
||||||
|
CTCSS_TONE_156_7Hz,
|
||||||
|
CTCSS_TONE_159_8Hz,
|
||||||
|
CTCSS_TONE_162_2Hz,
|
||||||
|
CTCSS_TONE_165_5Hz,
|
||||||
|
CTCSS_TONE_167_9Hz,
|
||||||
|
CTCSS_TONE_171_3Hz,
|
||||||
|
CTCSS_TONE_173_8Hz,
|
||||||
|
CTCSS_TONE_177_3Hz,
|
||||||
|
CTCSS_TONE_179_9Hz,
|
||||||
|
CTCSS_TONE_183_5Hz,
|
||||||
|
CTCSS_TONE_186_2Hz,
|
||||||
|
CTCSS_TONE_189_9Hz,
|
||||||
|
CTCSS_TONE_192_8Hz,
|
||||||
|
CTCSS_TONE_196_6Hz,
|
||||||
|
CTCSS_TONE_199_5Hz,
|
||||||
|
CTCSS_TONE_203_5Hz,
|
||||||
|
CTCSS_TONE_206_5Hz,
|
||||||
|
CTCSS_TONE_210_7Hz,
|
||||||
|
CTCSS_TONE_218_1Hz,
|
||||||
|
CTCSS_TONE_225_7Hz,
|
||||||
|
CTCSS_TONE_229_1Hz,
|
||||||
|
CTCSS_TONE_233_6Hz,
|
||||||
|
CTCSS_TONE_241_8Hz,
|
||||||
|
CTCSS_TONE_250_3Hz,
|
||||||
|
CTCSS_TONE_254_1Hz,
|
||||||
|
_CTCSS_TONE_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
const float CTCSS_TONES[_CTCSS_TONE_COUNT] = {
|
||||||
|
67.0f,
|
||||||
|
69.3f,
|
||||||
|
71.9f,
|
||||||
|
74.4f,
|
||||||
|
77.0f,
|
||||||
|
79.7f,
|
||||||
|
82.5f,
|
||||||
|
85.4f,
|
||||||
|
88.5f,
|
||||||
|
91.5f,
|
||||||
|
94.8f,
|
||||||
|
97.4f,
|
||||||
|
100.0f,
|
||||||
|
103.5f,
|
||||||
|
107.2f,
|
||||||
|
110.9f,
|
||||||
|
114.8f,
|
||||||
|
118.8f,
|
||||||
|
123.0f,
|
||||||
|
127.3f,
|
||||||
|
131.8f,
|
||||||
|
136.5f,
|
||||||
|
141.3f,
|
||||||
|
146.2f,
|
||||||
|
150.0f,
|
||||||
|
151.4f,
|
||||||
|
156.7f,
|
||||||
|
159.8f,
|
||||||
|
162.2f,
|
||||||
|
165.5f,
|
||||||
|
167.9f,
|
||||||
|
171.3f,
|
||||||
|
173.8f,
|
||||||
|
177.3f,
|
||||||
|
179.9f,
|
||||||
|
183.5f,
|
||||||
|
186.2f,
|
||||||
|
189.9f,
|
||||||
|
192.8f,
|
||||||
|
196.6f,
|
||||||
|
199.5f,
|
||||||
|
203.5f,
|
||||||
|
206.5f,
|
||||||
|
210.7f,
|
||||||
|
218.1f,
|
||||||
|
225.7f,
|
||||||
|
229.1f,
|
||||||
|
233.6f,
|
||||||
|
241.8f,
|
||||||
|
250.3f,
|
||||||
|
254.1f
|
||||||
|
};
|
||||||
|
|
||||||
|
class CTCSSSquelch : public Processor<stereo_t, stereo_t> {
|
||||||
|
using base_type = Processor<stereo_t, stereo_t>;
|
||||||
|
public:
|
||||||
|
CTCSSSquelch() {}
|
||||||
|
|
||||||
|
CTCSSSquelch(stream<stereo_t>* in, double samplerate) { init(in, samplerate); }
|
||||||
|
|
||||||
|
~CTCSSSquelch() {
|
||||||
|
// If not initialized, do nothing
|
||||||
|
if (!base_type::_block_init) { return; }
|
||||||
|
|
||||||
|
// Stop the DSP thread
|
||||||
|
base_type::stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<stereo_t>* in, double samplerate) {
|
||||||
|
// Save settings
|
||||||
|
_samplerate = samplerate;
|
||||||
|
|
||||||
|
// Create dummy taps just for initialization
|
||||||
|
float dummy[1] = { 1.0f };
|
||||||
|
auto dummyTaps = dsp::taps::fromArray(1, dummy);
|
||||||
|
|
||||||
|
// Initialize the DDC and FM demod
|
||||||
|
ddc.init(NULL, samplerate, CTCSS_DECODE_SAMPLERATE, CTCSS_DECODE_BANDWIDTH, CTCSS_DECODE_OFFSET);
|
||||||
|
fm.init(NULL, 1.0, CTCSS_DECODE_SAMPLERATE);
|
||||||
|
|
||||||
|
// Initilize the base block class
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSamplerate(double samplerate) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
base_type::tempStop();
|
||||||
|
_samplerate = samplerate;
|
||||||
|
ddc.setInSamplerate(samplerate);
|
||||||
|
base_type::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRequiredTone(CTCSSTone tone) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
requiredTone = tone;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTCSSTone getCurrentTone() {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
return currentTone;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int process(int count, const stereo_t* in, stereo_t* out) {
|
||||||
|
// Shift and resample to the correct samplerate
|
||||||
|
int ddcOutCount = ddc.process(count, (complex_t*)in, ddc.out.writeBuf);
|
||||||
|
|
||||||
|
// FM Demod the CTCSS tone
|
||||||
|
fm.process(ddcOutCount, ddc.out.writeBuf, fm.out.writeBuf);
|
||||||
|
|
||||||
|
// Get the required tone
|
||||||
|
const CTCSSTone rtone = requiredTone;
|
||||||
|
|
||||||
|
// Detect the tone frequency
|
||||||
|
for (int i = 0; i < ddcOutCount; i++) {
|
||||||
|
// Compute the running mean
|
||||||
|
const float val = fm.out.writeBuf[i];
|
||||||
|
mean = 0.95f*mean + 0.05f*val;
|
||||||
|
|
||||||
|
// Compute the running variance
|
||||||
|
const float err = val - mean;
|
||||||
|
var = 0.95f*var + 0.05f*err*err;
|
||||||
|
|
||||||
|
// Run a schmitt trigger on the variance
|
||||||
|
bool nvarOk = varOk ? (var < 1100.0f) : (var < 1000.0f);
|
||||||
|
|
||||||
|
// Check if the tone has to be rematched
|
||||||
|
if (nvarOk && (!varOk || mean < minFreq || mean > maxFreq)) {
|
||||||
|
// Compute the absolute frequency
|
||||||
|
float freq = mean + CTCSS_DECODE_OFFSET;
|
||||||
|
|
||||||
|
// Check it against the known tones
|
||||||
|
if (freq < CTCSS_TONES[0] - 2.5) {
|
||||||
|
currentTone = CTCSS_TONE_NONE;
|
||||||
|
}
|
||||||
|
else if (freq > CTCSS_TONES[_CTCSS_TONE_COUNT-1] + 2.5) {
|
||||||
|
currentTone = CTCSS_TONE_NONE;
|
||||||
|
}
|
||||||
|
else if (freq < CTCSS_TONES[0]) {
|
||||||
|
currentTone = (CTCSSTone)0;
|
||||||
|
}
|
||||||
|
else if (freq > CTCSS_TONES[_CTCSS_TONE_COUNT-1]) {
|
||||||
|
currentTone = (CTCSSTone)(_CTCSS_TONE_COUNT-1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
int a = 0;
|
||||||
|
int b = _CTCSS_TONE_COUNT-1;
|
||||||
|
while (b - a > 1) {
|
||||||
|
int c = (a + b) >> 1;
|
||||||
|
((CTCSS_TONES[c] < freq) ? a : b) = c;
|
||||||
|
}
|
||||||
|
currentTone = (CTCSSTone)((freq - CTCSS_TONES[a] < CTCSS_TONES[b] - freq) ? a : b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the mute status
|
||||||
|
mute = !(currentTone == rtone || (currentTone != CTCSS_TONE_NONE && rtone == CTCSS_TONE_ANY));
|
||||||
|
|
||||||
|
// Unmuted the audio if needed
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Recompute min and max freq if a valid tone is detected
|
||||||
|
if (currentTone != CTCSS_TONE_NONE) {
|
||||||
|
float c = CTCSS_TONES[currentTone];
|
||||||
|
float l = (currentTone > CTCSS_TONE_67Hz) ? CTCSS_TONES[currentTone - 1] : c - 2.5f;
|
||||||
|
float r = (currentTone < CTCSS_TONE_254_1Hz) ? CTCSS_TONES[currentTone + 1] : c + 2.5f;
|
||||||
|
minFreq = (l+c) / 2.0f;
|
||||||
|
maxFreq = (r+c) / 2.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a rising edge on the variance
|
||||||
|
if (!nvarOk && varOk) {
|
||||||
|
// Mute the audio
|
||||||
|
// TODO
|
||||||
|
mute = true;
|
||||||
|
currentTone = CTCSS_TONE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the new variance state
|
||||||
|
varOk = nvarOk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEBUG ONLY
|
||||||
|
if ((rtone != CTCSS_TONE_NONE) && mute) {
|
||||||
|
memset(out, 0, count * sizeof(stereo_t));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(out, in, count * sizeof(stereo_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
double _samplerate;
|
||||||
|
std::atomic<CTCSSTone> requiredTone = CTCSS_TONE_ANY;
|
||||||
|
|
||||||
|
float mean = 0.0f;
|
||||||
|
float var = 0.0f;
|
||||||
|
bool varOk = false;
|
||||||
|
float minFreq = 0.0f;
|
||||||
|
float maxFreq = 0.0f;
|
||||||
|
bool mute = true;
|
||||||
|
std::atomic<CTCSSTone> currentTone = CTCSS_TONE_NONE;
|
||||||
|
|
||||||
|
channel::RxVFO ddc;
|
||||||
|
demod::Quadrature fm;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
// TODO: Rewrite better!!!!!
|
// TODO: Rewrite better!!!!!
|
||||||
namespace dsp::noise_reduction {
|
namespace dsp::noise_reduction {
|
||||||
class Squelch : public Processor<complex_t, complex_t> {
|
class PowerSquelch : public Processor<complex_t, complex_t> {
|
||||||
using base_type = Processor<complex_t, complex_t>;
|
using base_type = Processor<complex_t, complex_t>;
|
||||||
public:
|
public:
|
||||||
Squelch() {}
|
PowerSquelch() {}
|
||||||
|
|
||||||
Squelch(stream<complex_t>* in, double level) {}
|
PowerSquelch(stream<complex_t>* in, double level) {}
|
||||||
|
|
||||||
~Squelch() {
|
~PowerSquelch() {
|
||||||
if (!base_type::_block_init) { return; }
|
if (!base_type::_block_init) { return; }
|
||||||
base_type::stop();
|
base_type::stop();
|
||||||
buffer::free(normBuffer);
|
buffer::free(normBuffer);
|
||||||
@@ -31,8 +31,11 @@ namespace dsp::noise_reduction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline int process(int count, const complex_t* in, complex_t* out) {
|
inline int process(int count, const complex_t* in, complex_t* out) {
|
||||||
float sum;
|
// Compute the amplitude of each sample
|
||||||
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)in, count);
|
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)in, count);
|
||||||
|
|
||||||
|
// Compute the mean amplitude
|
||||||
|
float sum = 0.0f;
|
||||||
volk_32f_accumulator_s32f(&sum, normBuffer, count);
|
volk_32f_accumulator_s32f(&sum, normBuffer, count);
|
||||||
sum /= (float)count;
|
sum /= (float)count;
|
||||||
|
|
||||||
@@ -46,8 +49,6 @@ namespace dsp::noise_reduction {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
//DEFAULT_PROC_RUN();
|
|
||||||
|
|
||||||
int run() {
|
int run() {
|
||||||
int count = base_type::_in->read();
|
int count = base_type::_in->read();
|
||||||
if (count < 0) { return -1; }
|
if (count < 0) { return -1; }
|
||||||
@@ -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() {}
|
||||||
|
|||||||
@@ -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,8 +85,8 @@ 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 + tuneOffset) : ifFreq)), selectedHandler->ctx);
|
||||||
onRetune.emit(freq);
|
onRetune.emit(freq + tuneOffset);
|
||||||
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.3.0"
|
||||||
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
|
// Compute the error
|
||||||
if ((left < syncLevel && right < syncLevel) && !forceLock) {
|
float error = (left - right) * (1.0f/((float)SYNC_HALF_LEN));
|
||||||
error = (left + syncBias - right);
|
|
||||||
locked = true;
|
// 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 {
|
else {
|
||||||
locked = false;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (++counter >= 100) {
|
// Check the the line is in lock
|
||||||
counter = 0;
|
bool lineLocked = (lowestId < SYNC_R_END || lowestId >= SYNC_L_START);
|
||||||
//flog::warn("Left: {}, Right: {}, Error: {}, Freq: {}, Phase: {}", left, right, error, pcl.freq, pcl.phase);
|
|
||||||
|
// 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
|
// Get the offset ready for the next buffer
|
||||||
pcl.advance(error);
|
|
||||||
float delta = floorf(pcl.phase);
|
|
||||||
offset += delta;
|
|
||||||
pcl.phase -= delta;
|
|
||||||
}
|
|
||||||
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 {
|
||||||
|
_this->ypos += 2;
|
||||||
|
_this->line++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Measure vsync levels
|
// NEW SYNC:
|
||||||
float sync0 = 0.0f, sync1 = 0.0f;
|
float offset = 0.0f;
|
||||||
for (int i = 0; i < 306; i++) {
|
float gain = 1.0f;
|
||||||
sync0 += data[i];
|
uint16_t syncHistory = 0;
|
||||||
}
|
int line = 0;
|
||||||
for (int i = (720/2); i < ((720/2)+306); i++) {
|
int ypos = 0;
|
||||||
sync1 += data[i];
|
int vlock = 0;
|
||||||
}
|
float subcarrierFreq = 0.0f;
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ enum IFNRPreset {
|
|||||||
IFNR_PRESET_BROADCAST
|
IFNR_PRESET_BROADCAST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum SquelchMode {
|
||||||
|
SQUELCH_MODE_OFF,
|
||||||
|
SQUELCH_MODE_POWER,
|
||||||
|
SQUELCH_MODE_SNR,
|
||||||
|
SQUELCH_MODE_CTCSS_MUTE,
|
||||||
|
SQUELCH_MODE_CTCSS_DECODE,
|
||||||
|
SQUELCH_MODE_DCS_MUTE,
|
||||||
|
SQUELCH_MODE_DCS_DECODE,
|
||||||
|
};
|
||||||
|
|
||||||
namespace demod {
|
namespace demod {
|
||||||
class Demodulator {
|
class Demodulator {
|
||||||
public:
|
public:
|
||||||
@@ -45,6 +55,8 @@ namespace demod {
|
|||||||
virtual int getDefaultDeemphasisMode() = 0;
|
virtual int getDefaultDeemphasisMode() = 0;
|
||||||
virtual bool getFMIFNRAllowed() = 0;
|
virtual bool getFMIFNRAllowed() = 0;
|
||||||
virtual bool getNBAllowed() = 0;
|
virtual bool getNBAllowed() = 0;
|
||||||
|
virtual bool getHighPassAllowed() = 0;
|
||||||
|
virtual bool getSquelchAllowed() = 0;
|
||||||
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ namespace demod {
|
|||||||
|
|
||||||
void showMenu() {
|
void showMenu() {
|
||||||
float menuWidth = ImGui::GetContentRegionAvail().x;
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
if (ImGui::Checkbox(("Carrier AGC##_radio_am_carrier_agc_" + name).c_str(), &carrierAgc)) {
|
||||||
|
demod.setAGCMode(carrierAgc ? dsp::demod::AM<dsp::stereo_t>::AGCMode::CARRIER : dsp::demod::AM<dsp::stereo_t>::AGCMode::AUDIO);
|
||||||
|
_config->acquire();
|
||||||
|
_config->conf[name][getName()]["carrierAgc"] = carrierAgc;
|
||||||
|
_config->release(true);
|
||||||
|
}
|
||||||
ImGui::LeftLabel("AGC Attack");
|
ImGui::LeftLabel("AGC Attack");
|
||||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
if (ImGui::SliderFloat(("##_radio_am_agc_attack_" + name).c_str(), &agcAttack, 1.0f, 200.0f)) {
|
if (ImGui::SliderFloat(("##_radio_am_agc_attack_" + name).c_str(), &agcAttack, 1.0f, 200.0f)) {
|
||||||
@@ -56,12 +62,6 @@ namespace demod {
|
|||||||
_config->conf[name][getName()]["agcDecay"] = agcDecay;
|
_config->conf[name][getName()]["agcDecay"] = agcDecay;
|
||||||
_config->release(true);
|
_config->release(true);
|
||||||
}
|
}
|
||||||
if (ImGui::Checkbox(("Carrier AGC##_radio_am_carrier_agc_" + name).c_str(), &carrierAgc)) {
|
|
||||||
demod.setAGCMode(carrierAgc ? dsp::demod::AM<dsp::stereo_t>::AGCMode::CARRIER : dsp::demod::AM<dsp::stereo_t>::AGCMode::AUDIO);
|
|
||||||
_config->acquire();
|
|
||||||
_config->conf[name][getName()]["carrierAgc"] = carrierAgc;
|
|
||||||
_config->release(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBandwidth(double bandwidth) { demod.setBandwidth(bandwidth); }
|
void setBandwidth(double bandwidth) { demod.setBandwidth(bandwidth); }
|
||||||
@@ -86,6 +86,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return false; }
|
bool getNBAllowed() { return false; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return false; }
|
bool getNBAllowed() { return false; }
|
||||||
|
bool getHighPassAllowed() { return false; }
|
||||||
|
bool getSquelchAllowed() { return false; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return true; }
|
bool getNBAllowed() { return true; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return true; }
|
bool getNBAllowed() { return true; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -22,14 +22,11 @@ namespace demod {
|
|||||||
if (config->conf[name][getName()].contains("lowPass")) {
|
if (config->conf[name][getName()].contains("lowPass")) {
|
||||||
_lowPass = config->conf[name][getName()]["lowPass"];
|
_lowPass = config->conf[name][getName()]["lowPass"];
|
||||||
}
|
}
|
||||||
if (config->conf[name][getName()].contains("highPass")) {
|
|
||||||
_highPass = config->conf[name][getName()]["highPass"];
|
|
||||||
}
|
|
||||||
_config->release();
|
_config->release();
|
||||||
|
|
||||||
|
|
||||||
// Define structure
|
// Define structure
|
||||||
demod.init(input, getIFSampleRate(), bandwidth, _lowPass, _highPass);
|
demod.init(input, getIFSampleRate(), bandwidth, _lowPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
void start() { demod.start(); }
|
void start() { demod.start(); }
|
||||||
@@ -43,12 +40,6 @@ namespace demod {
|
|||||||
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
||||||
_config->release(true);
|
_config->release(true);
|
||||||
}
|
}
|
||||||
if (ImGui::Checkbox(("High Pass##_radio_wfm_highpass_" + name).c_str(), &_highPass)) {
|
|
||||||
demod.setHighPass(_highPass);
|
|
||||||
_config->acquire();
|
|
||||||
_config->conf[name][getName()]["highPass"] = _highPass;
|
|
||||||
_config->release(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBandwidth(double bandwidth) {
|
void setBandwidth(double bandwidth) {
|
||||||
@@ -75,6 +66,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return true; }
|
bool getFMIFNRAllowed() { return true; }
|
||||||
bool getNBAllowed() { return false; }
|
bool getNBAllowed() { return false; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -83,7 +76,6 @@ namespace demod {
|
|||||||
ConfigManager* _config = NULL;
|
ConfigManager* _config = NULL;
|
||||||
|
|
||||||
bool _lowPass = true;
|
bool _lowPass = true;
|
||||||
bool _highPass = false;
|
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return true; }
|
bool getNBAllowed() { return true; }
|
||||||
|
bool getHighPassAllowed() { return false; }
|
||||||
|
bool getSquelchAllowed() { return false; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -80,6 +80,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
bool getNBAllowed() { return true; }
|
bool getNBAllowed() { return true; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -100,18 +100,18 @@ namespace demod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void showMenu() {
|
void showMenu() {
|
||||||
if (ImGui::Checkbox(("Stereo##_radio_wfm_stereo_" + name).c_str(), &_stereo)) {
|
|
||||||
setStereo(_stereo);
|
|
||||||
_config->acquire();
|
|
||||||
_config->conf[name][getName()]["stereo"] = _stereo;
|
|
||||||
_config->release(true);
|
|
||||||
}
|
|
||||||
if (ImGui::Checkbox(("Low Pass##_radio_wfm_lowpass_" + name).c_str(), &_lowPass)) {
|
if (ImGui::Checkbox(("Low Pass##_radio_wfm_lowpass_" + name).c_str(), &_lowPass)) {
|
||||||
demod.setLowPass(_lowPass);
|
demod.setLowPass(_lowPass);
|
||||||
_config->acquire();
|
_config->acquire();
|
||||||
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
_config->conf[name][getName()]["lowPass"] = _lowPass;
|
||||||
_config->release(true);
|
_config->release(true);
|
||||||
}
|
}
|
||||||
|
if (ImGui::Checkbox(("Stereo##_radio_wfm_stereo_" + name).c_str(), &_stereo)) {
|
||||||
|
setStereo(_stereo);
|
||||||
|
_config->acquire();
|
||||||
|
_config->conf[name][getName()]["stereo"] = _stereo;
|
||||||
|
_config->release(true);
|
||||||
|
}
|
||||||
if (ImGui::Checkbox(("Decode RDS##_radio_wfm_rds_" + name).c_str(), &_rds)) {
|
if (ImGui::Checkbox(("Decode RDS##_radio_wfm_rds_" + name).c_str(), &_rds)) {
|
||||||
demod.setRDSOut(_rds);
|
demod.setRDSOut(_rds);
|
||||||
_config->acquire();
|
_config->acquire();
|
||||||
@@ -270,6 +270,8 @@ namespace demod {
|
|||||||
int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; }
|
int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; }
|
||||||
bool getFMIFNRAllowed() { return true; }
|
bool getFMIFNRAllowed() { return true; }
|
||||||
bool getNBAllowed() { return false; }
|
bool getNBAllowed() { return false; }
|
||||||
|
bool getHighPassAllowed() { return true; }
|
||||||
|
bool getSquelchAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
// ============= DEDICATED FUNCTIONS =============
|
// ============= DEDICATED FUNCTIONS =============
|
||||||
|
|||||||
@@ -5,10 +5,14 @@ enum {
|
|||||||
RADIO_IFACE_CMD_SET_MODE,
|
RADIO_IFACE_CMD_SET_MODE,
|
||||||
RADIO_IFACE_CMD_GET_BANDWIDTH,
|
RADIO_IFACE_CMD_GET_BANDWIDTH,
|
||||||
RADIO_IFACE_CMD_SET_BANDWIDTH,
|
RADIO_IFACE_CMD_SET_BANDWIDTH,
|
||||||
RADIO_IFACE_CMD_GET_SQUELCH_ENABLED,
|
RADIO_IFACE_CMD_GET_SQUELCH_MODE,
|
||||||
RADIO_IFACE_CMD_SET_SQUELCH_ENABLED,
|
RADIO_IFACE_CMD_SET_SQUELCH_MODE,
|
||||||
RADIO_IFACE_CMD_GET_SQUELCH_LEVEL,
|
RADIO_IFACE_CMD_GET_SQUELCH_LEVEL,
|
||||||
RADIO_IFACE_CMD_SET_SQUELCH_LEVEL,
|
RADIO_IFACE_CMD_SET_SQUELCH_LEVEL,
|
||||||
|
RADIO_IFACE_CMD_GET_CTCSS_TONE,
|
||||||
|
RADIO_IFACE_CMD_SET_CTCSS_TONE,
|
||||||
|
RADIO_IFACE_CMD_GET_HIGHPASS,
|
||||||
|
RADIO_IFACE_CMD_SET_HIGHPASS
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
#include <dsp/chain.h>
|
#include <dsp/chain.h>
|
||||||
#include <dsp/noise_reduction/noise_blanker.h>
|
#include <dsp/noise_reduction/noise_blanker.h>
|
||||||
#include <dsp/noise_reduction/fm_if.h>
|
#include <dsp/noise_reduction/fm_if.h>
|
||||||
#include <dsp/noise_reduction/squelch.h>
|
#include <dsp/noise_reduction/power_squelch.h>
|
||||||
|
#include <dsp/noise_reduction/ctcss_squelch.h>
|
||||||
#include <dsp/multirate/rational_resampler.h>
|
#include <dsp/multirate/rational_resampler.h>
|
||||||
#include <dsp/filter/deephasis.h>
|
#include <dsp/filter/deephasis.h>
|
||||||
#include <core.h>
|
#include <core.h>
|
||||||
@@ -49,6 +50,22 @@ public:
|
|||||||
ifnrPresets.define("Voice", IFNR_PRESET_VOICE);
|
ifnrPresets.define("Voice", IFNR_PRESET_VOICE);
|
||||||
ifnrPresets.define("Narrow Band", IFNR_PRESET_NARROW_BAND);
|
ifnrPresets.define("Narrow Band", IFNR_PRESET_NARROW_BAND);
|
||||||
|
|
||||||
|
squelchModes.define("off", "Off", SQUELCH_MODE_OFF);
|
||||||
|
squelchModes.define("power", "Power", SQUELCH_MODE_POWER);
|
||||||
|
//squelchModes.define("snr", "SNR", SQUELCH_MODE_SNR);
|
||||||
|
squelchModes.define("ctcss_mute", "CTCSS (Mute)", SQUELCH_MODE_CTCSS_MUTE);
|
||||||
|
squelchModes.define("ctcss_decode", "CTCSS (Decode Only)", SQUELCH_MODE_CTCSS_DECODE);
|
||||||
|
//squelchModes.define("dcs_mute", "DCS (Mute)", SQUELCH_MODE_DCS_MUTE);
|
||||||
|
//squelchModes.define("dcs_decode", "DCS (Decode Only)", SQUELCH_MODE_DCS_DECODE);
|
||||||
|
|
||||||
|
for (int i = 0; i < dsp::noise_reduction::_CTCSS_TONE_COUNT; i++) {
|
||||||
|
float tone = dsp::noise_reduction::CTCSS_TONES[i];
|
||||||
|
char buf[64];
|
||||||
|
sprintf(buf, "%.1fHz", tone);
|
||||||
|
ctcssTones.define((int)round(tone) * 10, buf, (dsp::noise_reduction::CTCSSTone)i);
|
||||||
|
}
|
||||||
|
ctcssTones.define(-1, "Any", dsp::noise_reduction::CTCSS_TONE_ANY);
|
||||||
|
|
||||||
// Initialize the config if it doesn't exist
|
// Initialize the config if it doesn't exist
|
||||||
bool created = false;
|
bool created = false;
|
||||||
config.acquire();
|
config.acquire();
|
||||||
@@ -72,19 +89,24 @@ public:
|
|||||||
|
|
||||||
nb.init(NULL, 500.0 / 24000.0, 10.0);
|
nb.init(NULL, 500.0 / 24000.0, 10.0);
|
||||||
fmnr.init(NULL, 32);
|
fmnr.init(NULL, 32);
|
||||||
squelch.init(NULL, MIN_SQUELCH);
|
powerSquelch.init(NULL, MIN_SQUELCH);
|
||||||
|
|
||||||
ifChain.addBlock(&nb, false);
|
ifChain.addBlock(&nb, false);
|
||||||
ifChain.addBlock(&squelch, false);
|
ifChain.addBlock(&powerSquelch, false);
|
||||||
ifChain.addBlock(&fmnr, false);
|
ifChain.addBlock(&fmnr, false);
|
||||||
|
|
||||||
// Initialize audio DSP chain
|
// Initialize audio DSP chain
|
||||||
afChain.init(&dummyAudioStream);
|
afChain.init(&dummyAudioStream);
|
||||||
|
|
||||||
|
ctcss.init(NULL, 50000.0);
|
||||||
resamp.init(NULL, 250000.0, 48000.0);
|
resamp.init(NULL, 250000.0, 48000.0);
|
||||||
|
hpTaps = dsp::taps::highPass(300.0, 100.0, 48000.0);
|
||||||
|
hpf.init(NULL, hpTaps);
|
||||||
deemp.init(NULL, 50e-6, 48000.0);
|
deemp.init(NULL, 50e-6, 48000.0);
|
||||||
|
|
||||||
|
afChain.addBlock(&ctcss, false);
|
||||||
afChain.addBlock(&resamp, true);
|
afChain.addBlock(&resamp, true);
|
||||||
|
afChain.addBlock(&hpf, false);
|
||||||
afChain.addBlock(&deemp, false);
|
afChain.addBlock(&deemp, false);
|
||||||
|
|
||||||
// Initialize the sink
|
// Initialize the sink
|
||||||
@@ -233,6 +255,33 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Squelch
|
||||||
|
if (_this->squelchAllowed) {
|
||||||
|
ImGui::LeftLabel("Squelch Mode");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Combo(("##_radio_sqelch_mode_" + _this->name).c_str(), &_this->squelchModeId, _this->squelchModes.txt)) {
|
||||||
|
_this->setSquelchMode(_this->squelchModes[_this->squelchModeId]);
|
||||||
|
}
|
||||||
|
switch (_this->squelchModes[_this->squelchModeId]) {
|
||||||
|
case SQUELCH_MODE_POWER:
|
||||||
|
ImGui::LeftLabel("Squelch Level");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::SliderFloat(("##_radio_sqelch_lvl_" + _this->name).c_str(), &_this->squelchLevel, _this->MIN_SQUELCH, _this->MAX_SQUELCH, "%.3fdB")) {
|
||||||
|
_this->setSquelchLevel(_this->squelchLevel);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_CTCSS_MUTE:
|
||||||
|
if (_this->squelchModes[_this->squelchModeId] == SQUELCH_MODE_CTCSS_MUTE) {
|
||||||
|
ImGui::LeftLabel("CTCSS Tone");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Combo(("##_radio_ctcss_tone_" + _this->name).c_str(), &_this->ctcssToneId, _this->ctcssTones.txt)) {
|
||||||
|
_this->setCTCSSTone(_this->ctcssTones[_this->ctcssToneId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Noise blanker
|
// Noise blanker
|
||||||
if (_this->nbAllowed) {
|
if (_this->nbAllowed) {
|
||||||
if (ImGui::Checkbox(("Noise blanker (W.I.P.)##_radio_nb_ena_" + _this->name).c_str(), &_this->nbEnabled)) {
|
if (ImGui::Checkbox(("Noise blanker (W.I.P.)##_radio_nb_ena_" + _this->name).c_str(), &_this->nbEnabled)) {
|
||||||
@@ -247,19 +296,6 @@ private:
|
|||||||
if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); }
|
if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Squelch
|
|
||||||
if (ImGui::Checkbox(("Squelch##_radio_sqelch_ena_" + _this->name).c_str(), &_this->squelchEnabled)) {
|
|
||||||
_this->setSquelchEnabled(_this->squelchEnabled);
|
|
||||||
}
|
|
||||||
if (!_this->squelchEnabled && _this->enabled) { style::beginDisabled(); }
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
|
||||||
if (ImGui::SliderFloat(("##_radio_sqelch_lvl_" + _this->name).c_str(), &_this->squelchLevel, _this->MIN_SQUELCH, _this->MAX_SQUELCH, "%.3fdB")) {
|
|
||||||
_this->setSquelchLevel(_this->squelchLevel);
|
|
||||||
}
|
|
||||||
if (!_this->squelchEnabled && _this->enabled) { style::endDisabled(); }
|
|
||||||
|
|
||||||
// FM IF Noise Reduction
|
// FM IF Noise Reduction
|
||||||
if (_this->FMIFNRAllowed) {
|
if (_this->FMIFNRAllowed) {
|
||||||
if (ImGui::Checkbox(("IF Noise Reduction##_radio_fmifnr_ena_" + _this->name).c_str(), &_this->FMIFNREnabled)) {
|
if (ImGui::Checkbox(("IF Noise Reduction##_radio_fmifnr_ena_" + _this->name).c_str(), &_this->FMIFNREnabled)) {
|
||||||
@@ -276,9 +312,53 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// High pass
|
||||||
|
if (_this->highPassAllowed) {
|
||||||
|
if (ImGui::Checkbox(("High Pass##_radio_hpf_" + _this->name).c_str(), &_this->highPass)) {
|
||||||
|
_this->setHighPass(_this->highPass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Demodulator specific menu
|
// Demodulator specific menu
|
||||||
_this->selectedDemod->showMenu();
|
_this->selectedDemod->showMenu();
|
||||||
|
|
||||||
|
// Display the squelch diagnostics
|
||||||
|
switch (_this->squelchModes[_this->squelchModeId]) {
|
||||||
|
case SQUELCH_MODE_CTCSS_MUTE:
|
||||||
|
ImGui::TextUnformatted("Received Tone:");
|
||||||
|
ImGui::SameLine();
|
||||||
|
{
|
||||||
|
auto ctone = _this->ctcss.getCurrentTone();
|
||||||
|
auto dtone = _this->ctcssTones[_this->ctcssToneId];
|
||||||
|
if (ctone != dsp::noise_reduction::CTCSS_TONE_NONE) {
|
||||||
|
if (dtone == dsp::noise_reduction::CTCSS_TONE_ANY || ctone == dtone) {
|
||||||
|
ImGui::TextColored(ImVec4(0, 1, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::TextColored(ImVec4(1, 0, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::TextUnformatted("None");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_CTCSS_DECODE:
|
||||||
|
ImGui::TextUnformatted("Received Tone:");
|
||||||
|
ImGui::SameLine();
|
||||||
|
{
|
||||||
|
auto ctone = _this->ctcss.getCurrentTone();
|
||||||
|
if (ctone != dsp::noise_reduction::CTCSS_TONE_NONE) {
|
||||||
|
ImGui::TextColored(ImVec4(0, 1, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ImGui::TextUnformatted("None");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_this->enabled) { style::endDisabled(); }
|
if (!_this->enabled) { style::endDisabled(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,15 +440,20 @@ private:
|
|||||||
maxBandwidth = selectedDemod->getMaxBandwidth();
|
maxBandwidth = selectedDemod->getMaxBandwidth();
|
||||||
bandwidthLocked = selectedDemod->getBandwidthLocked();
|
bandwidthLocked = selectedDemod->getBandwidthLocked();
|
||||||
snapInterval = selectedDemod->getDefaultSnapInterval();
|
snapInterval = selectedDemod->getDefaultSnapInterval();
|
||||||
squelchLevel = MIN_SQUELCH;
|
|
||||||
deempAllowed = selectedDemod->getDeempAllowed();
|
deempAllowed = selectedDemod->getDeempAllowed();
|
||||||
deempId = deempModes.valueId((DeemphasisMode)selectedDemod->getDefaultDeemphasisMode());
|
deempId = deempModes.valueId((DeemphasisMode)selectedDemod->getDefaultDeemphasisMode());
|
||||||
squelchEnabled = false;
|
squelchModeId = squelchModes.valueId(SQUELCH_MODE_OFF);
|
||||||
|
squelchLevel = MIN_SQUELCH;
|
||||||
|
ctcssToneId = ctcssTones.valueId(dsp::noise_reduction::CTCSS_TONE_67Hz);
|
||||||
|
highPass = false;
|
||||||
|
|
||||||
postProcEnabled = selectedDemod->getPostProcEnabled();
|
postProcEnabled = selectedDemod->getPostProcEnabled();
|
||||||
FMIFNRAllowed = selectedDemod->getFMIFNRAllowed();
|
FMIFNRAllowed = selectedDemod->getFMIFNRAllowed();
|
||||||
FMIFNREnabled = false;
|
FMIFNREnabled = false;
|
||||||
fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE);
|
fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE);
|
||||||
nbAllowed = selectedDemod->getNBAllowed();
|
nbAllowed = selectedDemod->getNBAllowed();
|
||||||
|
squelchAllowed = selectedDemod->getSquelchAllowed();
|
||||||
|
highPassAllowed = selectedDemod->getHighPassAllowed();
|
||||||
nbEnabled = false;
|
nbEnabled = false;
|
||||||
nbLevel = 0.0f;
|
nbLevel = 0.0f;
|
||||||
double ifSamplerate = selectedDemod->getIFSampleRate();
|
double ifSamplerate = selectedDemod->getIFSampleRate();
|
||||||
@@ -380,11 +465,24 @@ private:
|
|||||||
if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) {
|
if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) {
|
||||||
snapInterval = config.conf[name][selectedDemod->getName()]["snapInterval"];
|
snapInterval = config.conf[name][selectedDemod->getName()]["snapInterval"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.conf[name][selectedDemod->getName()].contains("squelchMode")) {
|
||||||
|
std::string squelchModeStr = config.conf[name][selectedDemod->getName()]["squelchMode"];
|
||||||
|
if (squelchModes.keyExists(squelchModeStr)) {
|
||||||
|
squelchModeId = squelchModes.keyId(squelchModeStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (config.conf[name][selectedDemod->getName()].contains("squelchLevel")) {
|
if (config.conf[name][selectedDemod->getName()].contains("squelchLevel")) {
|
||||||
squelchLevel = config.conf[name][selectedDemod->getName()]["squelchLevel"];
|
squelchLevel = config.conf[name][selectedDemod->getName()]["squelchLevel"];
|
||||||
}
|
}
|
||||||
if (config.conf[name][selectedDemod->getName()].contains("squelchEnabled")) {
|
if (config.conf[name][selectedDemod->getName()].contains("ctcssTone")) {
|
||||||
squelchEnabled = config.conf[name][selectedDemod->getName()]["squelchEnabled"];
|
int ctcssToneX10 = config.conf[name][selectedDemod->getName()]["ctcssTone"];
|
||||||
|
if (ctcssTones.keyExists(ctcssToneX10)) {
|
||||||
|
ctcssToneId = ctcssTones.keyId(ctcssToneX10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config.conf[name][selectedDemod->getName()].contains("highPass")) {
|
||||||
|
highPass = config.conf[name][selectedDemod->getName()]["highPass"];
|
||||||
}
|
}
|
||||||
if (config.conf[name][selectedDemod->getName()].contains("deempMode")) {
|
if (config.conf[name][selectedDemod->getName()].contains("deempMode")) {
|
||||||
if (!config.conf[name][selectedDemod->getName()]["deempMode"].is_string()) {
|
if (!config.conf[name][selectedDemod->getName()]["deempMode"].is_string()) {
|
||||||
@@ -434,16 +532,22 @@ private:
|
|||||||
setFMIFNREnabled(FMIFNRAllowed ? FMIFNREnabled : false);
|
setFMIFNREnabled(FMIFNRAllowed ? FMIFNREnabled : false);
|
||||||
|
|
||||||
// Configure squelch
|
// Configure squelch
|
||||||
|
setSquelchMode(squelchAllowed ? squelchModes[squelchModeId] : SQUELCH_MODE_OFF);
|
||||||
setSquelchLevel(squelchLevel);
|
setSquelchLevel(squelchLevel);
|
||||||
setSquelchEnabled(squelchEnabled);
|
setCTCSSTone(ctcssTones[ctcssToneId]);
|
||||||
|
|
||||||
// Configure AF chain
|
// Configure AF chain
|
||||||
if (postProcEnabled) {
|
if (postProcEnabled) {
|
||||||
// Configure resampler
|
// Configure resampler
|
||||||
afChain.stop();
|
afChain.stop();
|
||||||
resamp.setInSamplerate(selectedDemod->getAFSampleRate());
|
double afsr = selectedDemod->getAFSampleRate();
|
||||||
setAudioSampleRate(audioSampleRate);
|
ctcss.setSamplerate(afsr);
|
||||||
|
resamp.setInSamplerate(afsr);
|
||||||
afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
setAudioSampleRate(audioSampleRate);
|
||||||
|
|
||||||
|
// Configure the HPF
|
||||||
|
setHighPass(highPass && highPassAllowed);
|
||||||
|
|
||||||
// Configure deemphasis
|
// Configure deemphasis
|
||||||
setDeemphasisMode(deempModes[deempId]);
|
setDeemphasisMode(deempModes[deempId]);
|
||||||
@@ -489,12 +593,32 @@ private:
|
|||||||
// Configure resampler
|
// Configure resampler
|
||||||
resamp.setOutSamplerate(audioSampleRate);
|
resamp.setOutSamplerate(audioSampleRate);
|
||||||
|
|
||||||
|
// Configure the HPF sample rate
|
||||||
|
hpTaps = dsp::taps::highPass(300.0, 100.0, audioSampleRate);
|
||||||
|
hpf.setTaps(hpTaps);
|
||||||
|
|
||||||
// Configure deemphasis sample rate
|
// Configure deemphasis sample rate
|
||||||
deemp.setSamplerate(audioSampleRate);
|
deemp.setSamplerate(audioSampleRate);
|
||||||
|
|
||||||
afChain.start();
|
afChain.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setHighPass(bool enabled) {
|
||||||
|
// Update the state
|
||||||
|
highPass = enabled;
|
||||||
|
|
||||||
|
// Check if post-processing is enabled and that a demodulator is selected
|
||||||
|
if (!postProcEnabled || !selectedDemod) { return; }
|
||||||
|
|
||||||
|
// Set the state of the HPF in the AF chain
|
||||||
|
afChain.setBlockEnabled(&hpf, enabled, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
|
||||||
|
// Save config
|
||||||
|
config.acquire();
|
||||||
|
config.conf[name][selectedDemod->getName()]["highPass"] = enabled;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
void setDeemphasisMode(DeemphasisMode mode) {
|
void setDeemphasisMode(DeemphasisMode mode) {
|
||||||
deempId = deempModes.valueId(mode);
|
deempId = deempModes.valueId(mode);
|
||||||
if (!postProcEnabled || !selectedDemod) { return; }
|
if (!postProcEnabled || !selectedDemod) { return; }
|
||||||
@@ -522,6 +646,7 @@ private:
|
|||||||
void setNBLevel(float level) {
|
void setNBLevel(float level) {
|
||||||
nbLevel = std::clamp<float>(level, MIN_NB, MAX_NB);
|
nbLevel = std::clamp<float>(level, MIN_NB, MAX_NB);
|
||||||
nb.setLevel(nbLevel);
|
nb.setLevel(nbLevel);
|
||||||
|
if (!selectedDemod) { return; }
|
||||||
|
|
||||||
// Save config
|
// Save config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
@@ -529,20 +654,59 @@ private:
|
|||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSquelchEnabled(bool enable) {
|
void setSquelchMode(SquelchMode mode) {
|
||||||
squelchEnabled = enable;
|
squelchModeId = squelchModes.valueId(mode);
|
||||||
if (!selectedDemod) { return; }
|
if (!selectedDemod) { return; }
|
||||||
ifChain.setBlockEnabled(&squelch, squelchEnabled, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
|
|
||||||
|
// Disable all squelch blocks
|
||||||
|
ifChain.disableBlock(&powerSquelch, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
|
||||||
|
afChain.disableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
|
||||||
|
// Enable the block depending on the mode
|
||||||
|
switch (mode) {
|
||||||
|
case SQUELCH_MODE_OFF:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_POWER:
|
||||||
|
// Enable the power squelch block
|
||||||
|
ifChain.enableBlock(&powerSquelch, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_SNR:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_CTCSS_MUTE:
|
||||||
|
// Set the required tone and enable the CTCSS squelch block
|
||||||
|
ctcss.setRequiredTone(ctcssTones[ctcssToneId]);
|
||||||
|
afChain.enableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_CTCSS_DECODE:
|
||||||
|
// Set the required tone to none and enable the CTCSS squelch block
|
||||||
|
ctcss.setRequiredTone(dsp::noise_reduction::CTCSS_TONE_NONE);
|
||||||
|
afChain.enableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_DCS_MUTE:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQUELCH_MODE_DCS_DECODE:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Save config
|
// Save config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[name][selectedDemod->getName()]["squelchEnabled"] = squelchEnabled;
|
config.conf[name][selectedDemod->getName()]["squelchMode"] = squelchModes.key(squelchModeId);
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSquelchLevel(float level) {
|
void setSquelchLevel(float level) {
|
||||||
squelchLevel = std::clamp<float>(level, MIN_SQUELCH, MAX_SQUELCH);
|
squelchLevel = std::clamp<float>(level, MIN_SQUELCH, MAX_SQUELCH);
|
||||||
squelch.setLevel(squelchLevel);
|
powerSquelch.setLevel(squelchLevel);
|
||||||
|
if (!selectedDemod) { return; }
|
||||||
|
|
||||||
// Save config
|
// Save config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
@@ -550,6 +714,24 @@ private:
|
|||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCTCSSTone(dsp::noise_reduction::CTCSSTone tone) {
|
||||||
|
// Check for an invalid value
|
||||||
|
if (tone == dsp::noise_reduction::CTCSS_TONE_NONE) { return; }
|
||||||
|
|
||||||
|
// If not in CTCSS mute mode, do nothing
|
||||||
|
if (squelchModes[squelchModeId] != SQUELCH_MODE_CTCSS_MUTE) { return; }
|
||||||
|
|
||||||
|
// Set the tone
|
||||||
|
ctcssToneId = ctcssTones.valueId(tone);
|
||||||
|
ctcss.setRequiredTone(tone);
|
||||||
|
if (!selectedDemod) { return; }
|
||||||
|
|
||||||
|
// Save config
|
||||||
|
config.acquire();
|
||||||
|
config.conf[name][selectedDemod->getName()]["ctcssTone"] = ctcssTones.key(ctcssToneId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
void setFMIFNREnabled(bool enabled) {
|
void setFMIFNREnabled(bool enabled) {
|
||||||
FMIFNREnabled = enabled;
|
FMIFNREnabled = enabled;
|
||||||
if (!selectedDemod) { return; }
|
if (!selectedDemod) { return; }
|
||||||
@@ -619,13 +801,13 @@ private:
|
|||||||
if (_this->bandwidthLocked) { return; }
|
if (_this->bandwidthLocked) { return; }
|
||||||
_this->setBandwidth(*_in);
|
_this->setBandwidth(*_in);
|
||||||
}
|
}
|
||||||
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_ENABLED && out) {
|
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_MODE && out) {
|
||||||
bool* _out = (bool*)out;
|
SquelchMode* _out = (SquelchMode*)out;
|
||||||
*_out = _this->squelchEnabled;
|
*_out = _this->squelchModes[_this->squelchModeId];
|
||||||
}
|
}
|
||||||
else if (code == RADIO_IFACE_CMD_SET_SQUELCH_ENABLED && in && _this->enabled) {
|
else if (code == RADIO_IFACE_CMD_SET_SQUELCH_MODE && in && _this->enabled) {
|
||||||
bool* _in = (bool*)in;
|
SquelchMode* _in = (SquelchMode*)in;
|
||||||
_this->setSquelchEnabled(*_in);
|
_this->setSquelchMode(*_in);
|
||||||
}
|
}
|
||||||
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_LEVEL && out) {
|
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_LEVEL && out) {
|
||||||
float* _out = (float*)out;
|
float* _out = (float*)out;
|
||||||
@@ -635,6 +817,22 @@ private:
|
|||||||
float* _in = (float*)in;
|
float* _in = (float*)in;
|
||||||
_this->setSquelchLevel(*_in);
|
_this->setSquelchLevel(*_in);
|
||||||
}
|
}
|
||||||
|
else if (code == RADIO_IFACE_CMD_GET_CTCSS_TONE && out) {
|
||||||
|
dsp::noise_reduction::CTCSSTone* _out = (dsp::noise_reduction::CTCSSTone*)out;
|
||||||
|
*_out = _this->ctcssTones[_this->ctcssToneId];
|
||||||
|
}
|
||||||
|
else if (code == RADIO_IFACE_CMD_SET_CTCSS_TONE && in && _this->enabled) {
|
||||||
|
dsp::noise_reduction::CTCSSTone* _in = (dsp::noise_reduction::CTCSSTone*)in;
|
||||||
|
_this->setCTCSSTone(*_in);
|
||||||
|
}
|
||||||
|
else if (code == RADIO_IFACE_CMD_GET_HIGHPASS && out) {
|
||||||
|
bool* _out = (bool*)out;
|
||||||
|
*_out = _this->highPass;
|
||||||
|
}
|
||||||
|
else if (code == RADIO_IFACE_CMD_SET_HIGHPASS && in && _this->enabled) {
|
||||||
|
bool* _in = (bool*)in;
|
||||||
|
_this->setHighPass(*_in);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -655,12 +853,15 @@ private:
|
|||||||
dsp::chain<dsp::complex_t> ifChain;
|
dsp::chain<dsp::complex_t> ifChain;
|
||||||
dsp::noise_reduction::NoiseBlanker nb;
|
dsp::noise_reduction::NoiseBlanker nb;
|
||||||
dsp::noise_reduction::FMIF fmnr;
|
dsp::noise_reduction::FMIF fmnr;
|
||||||
dsp::noise_reduction::Squelch squelch;
|
dsp::noise_reduction::PowerSquelch powerSquelch;
|
||||||
|
|
||||||
// Audio chain
|
// Audio chain
|
||||||
dsp::stream<dsp::stereo_t> dummyAudioStream;
|
dsp::stream<dsp::stereo_t> dummyAudioStream;
|
||||||
dsp::chain<dsp::stereo_t> afChain;
|
dsp::chain<dsp::stereo_t> afChain;
|
||||||
|
dsp::noise_reduction::CTCSSSquelch ctcss;
|
||||||
dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
|
dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
|
||||||
|
dsp::tap<float> hpTaps;
|
||||||
|
dsp::filter::FIR<dsp::stereo_t, float> hpf;
|
||||||
dsp::filter::Deemphasis<dsp::stereo_t> deemp;
|
dsp::filter::Deemphasis<dsp::stereo_t> deemp;
|
||||||
|
|
||||||
SinkManager::Stream stream;
|
SinkManager::Stream stream;
|
||||||
@@ -669,6 +870,8 @@ private:
|
|||||||
|
|
||||||
OptionList<std::string, DeemphasisMode> deempModes;
|
OptionList<std::string, DeemphasisMode> deempModes;
|
||||||
OptionList<std::string, IFNRPreset> ifnrPresets;
|
OptionList<std::string, IFNRPreset> ifnrPresets;
|
||||||
|
OptionList<std::string, SquelchMode> squelchModes;
|
||||||
|
OptionList<int, dsp::noise_reduction::CTCSSTone> ctcssTones;
|
||||||
|
|
||||||
double audioSampleRate = 48000.0;
|
double audioSampleRate = 48000.0;
|
||||||
float minBandwidth;
|
float minBandwidth;
|
||||||
@@ -679,8 +882,13 @@ private:
|
|||||||
int selectedDemodID = 1;
|
int selectedDemodID = 1;
|
||||||
bool postProcEnabled;
|
bool postProcEnabled;
|
||||||
|
|
||||||
bool squelchEnabled = false;
|
int squelchModeId = 0;
|
||||||
float squelchLevel;
|
float squelchLevel;
|
||||||
|
int ctcssToneId = 0;
|
||||||
|
bool squelchAllowed = false;
|
||||||
|
|
||||||
|
bool highPass = false;
|
||||||
|
bool highPassAllowed = false;
|
||||||
|
|
||||||
int deempId = 0;
|
int deempId = 0;
|
||||||
bool deempAllowed;
|
bool deempAllowed;
|
||||||
|
|||||||
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -9,10 +9,11 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
|
|||||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
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
|
||||||
@@ -45,10 +46,32 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
|
|||||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
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
|
||||||
@@ -45,10 +46,32 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l
|
|||||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
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
|
||||||
@@ -45,10 +46,32 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_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
|
||||||
@@ -9,10 +9,11 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l
|
|||||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
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
|
||||||
@@ -45,10 +46,32 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
@@ -15,10 +15,11 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev
|
|||||||
libcodec2-dev libudev-dev autoconf libtool xxd libspdlog-dev
|
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
|
||||||
@@ -82,11 +83,33 @@ 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 ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
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 -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
# Generate package
|
# Generate package
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
|
|||||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
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
|
||||||
@@ -45,10 +46,32 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
|
|||||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
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
|
||||||
@@ -45,10 +46,32 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l
|
|||||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
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
|
||||||
@@ -45,10 +46,32 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_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
|
||||||
@@ -4,15 +4,16 @@ cd /root
|
|||||||
|
|
||||||
# Install dependencies and tools
|
# Install dependencies and tools
|
||||||
apt update
|
apt update
|
||||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-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 libspdlog-dev
|
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
|
||||||
@@ -45,11 +46,33 @@ make -j2
|
|||||||
make install
|
make install
|
||||||
cd ../../
|
cd ../../
|
||||||
|
|
||||||
|
# Install libhydrasdr
|
||||||
|
git clone https://github.com/hydrasdr/rfone_host
|
||||||
|
cd rfone_host
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
|
# Install libdlcr
|
||||||
|
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
|
||||||
|
mkdir dlcr_host
|
||||||
|
cd dlcr_host
|
||||||
|
7z x ../dlcr_host_v0.3.0.zip
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||||
|
make -j2
|
||||||
|
make install
|
||||||
|
cd ../../
|
||||||
|
|
||||||
cd SDRPlusPlus
|
cd SDRPlusPlus
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON
|
||||||
make VERBOSE=1 -j2
|
make VERBOSE=1 -j2
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk1-dev, librtaudio-dev, libzstd-dev'
|
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 =========================
|
||||||
|
|
||||||
@@ -34,11 +34,14 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Frameworks $BUILD_DIR/core/libsdr
|
|||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspy_source/airspy_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspy_source/airspy_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/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/dragonlabs_source/dragonlabs_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/file_source/fobossdr_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/rfnm_source/rfnm_source.dylib
|
||||||
@@ -56,6 +59,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
|
||||||
|
|||||||
@@ -32,9 +32,14 @@ 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/hydrasdr-host/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/
|
||||||
|
|
||||||
@@ -72,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/
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
cmake_minimum_required (VERSION 3.2.0)
|
cmake_minimum_required (VERSION 3.13.0)
|
||||||
project (DiscordRPC)
|
project (DiscordRPC)
|
||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|||||||
@@ -168,10 +168,9 @@ public:
|
|||||||
writer.setSamplerate(samplerate);
|
writer.setSamplerate(samplerate);
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
|
||||||
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
|
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
|
||||||
std::string extension = ".wav";
|
std::string extension = ".wav";
|
||||||
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
|
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, recMode, vfoName) + extension);
|
||||||
if (!writer.open(expandedPath)) {
|
if (!writer.open(expandedPath)) {
|
||||||
flog::error("Failed to open file for recording: {0}", expandedPath);
|
flog::error("Failed to open file for recording: {0}", expandedPath);
|
||||||
return;
|
return;
|
||||||
@@ -452,7 +451,7 @@ private:
|
|||||||
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string genFileName(std::string templ, std::string type, std::string name) {
|
std::string genFileName(std::string templ, int mode, std::string name) {
|
||||||
// Get data
|
// Get data
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
tm* ltm = localtime(&now);
|
tm* ltm = localtime(&now);
|
||||||
@@ -462,6 +461,9 @@ private:
|
|||||||
freq += gui::waterfall.vfos[name]->generalOffset;
|
freq += gui::waterfall.vfos[name]->generalOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Select the recording type string
|
||||||
|
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
||||||
|
|
||||||
// Format to string
|
// Format to string
|
||||||
char freqStr[128];
|
char freqStr[128];
|
||||||
char hourStr[128];
|
char hourStr[128];
|
||||||
@@ -470,7 +472,7 @@ private:
|
|||||||
char dayStr[128];
|
char dayStr[128];
|
||||||
char monStr[128];
|
char monStr[128];
|
||||||
char yearStr[128];
|
char yearStr[128];
|
||||||
const char* modeStr = "Unknown";
|
const char* modeStr = (recMode == RECORDER_MODE_AUDIO) ? "Unknown" : "IQ";
|
||||||
sprintf(freqStr, "%.0lfHz", freq);
|
sprintf(freqStr, "%.0lfHz", freq);
|
||||||
sprintf(hourStr, "%02d", ltm->tm_hour);
|
sprintf(hourStr, "%02d", ltm->tm_hour);
|
||||||
sprintf(minStr, "%02d", ltm->tm_min);
|
sprintf(minStr, "%02d", ltm->tm_min);
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
19
readme.md
19
readme.md
@@ -324,12 +324,13 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
|
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
|
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
|
||||||
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| fobossdr_source | Beta | libfobos | OPT_BUILD_FOBOSSDR_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 | ⛔ | ⛔ | ✅ |
|
| 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 | ⛔ | ✅ | ✅ |
|
||||||
@@ -351,8 +352,8 @@ 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
|
||||||
|
|
||||||
@@ -366,6 +367,7 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| 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
|
||||||
@@ -418,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
|
||||||
|
|
||||||
@@ -439,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
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
|
||||||
project(badgesdr_source)
|
|
||||||
|
|
||||||
file(GLOB SRC "src/*.cpp")
|
|
||||||
|
|
||||||
include(${SDRPP_MODULE_CMAKE})
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
find_package(libusb CONFIG REQUIRED)
|
|
||||||
target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES})
|
|
||||||
elseif (ANDROID)
|
|
||||||
target_link_libraries(badgesdr_source PUBLIC
|
|
||||||
/sdr-kit/${ANDROID_ABI}/lib/libusb1.0.so
|
|
||||||
/sdr-kit/${ANDROID_ABI}/lib/librtlsdr.so
|
|
||||||
)
|
|
||||||
else (MSVC)
|
|
||||||
find_package(PkgConfig)
|
|
||||||
|
|
||||||
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
|
|
||||||
|
|
||||||
target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS})
|
|
||||||
target_link_directories(badgesdr_source PRIVATE ${LIBUSB_LIBRARY_DIRS})
|
|
||||||
target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES})
|
|
||||||
endif ()
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
#include "badgesdr.h"
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <utils/flog.h>
|
|
||||||
|
|
||||||
#define R820T_I2C_ADDR 0x1A
|
|
||||||
|
|
||||||
namespace BadgeSDR {
|
|
||||||
enum Commands {
|
|
||||||
CMD_I2C_RW,
|
|
||||||
CMD_I2C_STATUS,
|
|
||||||
CMD_ADC_START,
|
|
||||||
CMD_ADC_STOP,
|
|
||||||
CMD_ADC_GET_SAMP_COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
libusb_context* ctx = NULL;
|
|
||||||
|
|
||||||
bool DeviceInfo::operator==(const DeviceInfo& b) const {
|
|
||||||
return serialNumber == b.serialNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::write_reg(uint8_t reg, uint8_t value, void* ctx) {
|
|
||||||
Device* dev = (Device*)ctx;
|
|
||||||
dev->writeR820TReg(reg, value);
|
|
||||||
dev->writeR820TReg(0x1F, 0); // TODO: Figure out why this is needed
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::read_reg(uint8_t* data, int len, void* ctx) {
|
|
||||||
Device* dev = (Device*)ctx;
|
|
||||||
dev->readI2C(R820T_I2C_ADDR, data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
Device::Device(libusb_device_handle* dev) {
|
|
||||||
// Save device handle
|
|
||||||
this->dev = dev;
|
|
||||||
|
|
||||||
// Init tuner
|
|
||||||
r820t = {
|
|
||||||
16000000, // xtal_freq => 16MHz
|
|
||||||
3000000, // Set at boot to airspy_m0_m4_conf_t conf0 -> r820t_if_freq
|
|
||||||
100000000, /* Default Freq 100Mhz */
|
|
||||||
{
|
|
||||||
/* 05 */ 0x9F, // LNA manual gain mode, init to 0
|
|
||||||
/* 06 */ 0x80,
|
|
||||||
/* 07 */ 0x60,
|
|
||||||
/* 08 */ 0x80, // Image Gain Adjustment
|
|
||||||
/* 09 */ 0x40, // Image Phase Adjustment
|
|
||||||
/* 0A */ 0xA8, // Channel filter [0..3]: 0 = widest, f = narrowest - Optimal. Don't touch!
|
|
||||||
/* 0B */ 0x0F, // High pass filter - Optimal. Don't touch!
|
|
||||||
/* 0C */ 0x4F, // VGA control by code, init at 0
|
|
||||||
/* 0D */ 0x63, // LNA AGC settings: [0..3]: Lower threshold; [4..7]: High threshold
|
|
||||||
/* 0E */ 0x75,
|
|
||||||
/* 0F */ 0xE8, // Filter Widest, LDO_5V OFF, clk out ON,
|
|
||||||
/* 10 */ 0x7C,
|
|
||||||
/* 11 */ 0x42,
|
|
||||||
/* 12 */ 0x06,
|
|
||||||
/* 13 */ 0x00,
|
|
||||||
/* 14 */ 0x0F,
|
|
||||||
/* 15 */ 0x00,
|
|
||||||
/* 16 */ 0xC0,
|
|
||||||
/* 17 */ 0xA0,
|
|
||||||
/* 18 */ 0x48,
|
|
||||||
/* 19 */ 0xCC,
|
|
||||||
/* 1A */ 0x60,
|
|
||||||
/* 1B */ 0x00,
|
|
||||||
/* 1C */ 0x54,
|
|
||||||
/* 1D */ 0xAE,
|
|
||||||
/* 1E */ 0x0A,
|
|
||||||
/* 1F */ 0xC0
|
|
||||||
},
|
|
||||||
0 /* uint16_t padding */
|
|
||||||
};
|
|
||||||
r820t_init(&r820t, 3000000, write_reg, read_reg, this);
|
|
||||||
r820t_set_mixer_gain(&r820t, 15);
|
|
||||||
r820t_set_vga_gain(&r820t, 15);
|
|
||||||
}
|
|
||||||
|
|
||||||
Device::~Device() {
|
|
||||||
// Release the bulk interface
|
|
||||||
libusb_release_interface(dev, 0);
|
|
||||||
|
|
||||||
// Close device
|
|
||||||
libusb_close(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::setFrequency(double freq) {
|
|
||||||
r820t_set_freq(&r820t, freq - 2125000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::setLNAGain(int gain) {
|
|
||||||
r820t_set_lna_gain(&r820t, gain);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::setMixerGain(int gain) {
|
|
||||||
r820t_set_mixer_gain(&r820t, gain);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::setVGAGain(int gain) {
|
|
||||||
r820t_set_vga_gain(&r820t, gain);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Device::start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx, int minBufferSize) {
|
|
||||||
// Do nothing if already running
|
|
||||||
if (run) { return; }
|
|
||||||
|
|
||||||
// Save handler
|
|
||||||
this->callback = callback;
|
|
||||||
this->ctx = ctx;
|
|
||||||
|
|
||||||
// Compute buffer size
|
|
||||||
int bufCount = minBufferSize / 64;
|
|
||||||
if (minBufferSize % 64) { bufCount++; }
|
|
||||||
bufferSize = bufCount * 64;
|
|
||||||
|
|
||||||
// Mark as running
|
|
||||||
run = true;
|
|
||||||
|
|
||||||
// Start the ADC
|
|
||||||
startADC();
|
|
||||||
|
|
||||||
// Start worker thread
|
|
||||||
workerThread = std::thread(&Device::worker, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::stop() {
|
|
||||||
// Do nothing if already stopped
|
|
||||||
if (!run) { return; }
|
|
||||||
|
|
||||||
// Mark as stopped
|
|
||||||
run = false;
|
|
||||||
|
|
||||||
// Wait for the worker to exit
|
|
||||||
if (workerThread.joinable()) { workerThread.join(); }
|
|
||||||
|
|
||||||
// Stop the ADC
|
|
||||||
stopADC();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Device::getI2CStatus() {
|
|
||||||
int status;
|
|
||||||
int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
|
|
||||||
if (ret <= 0 || status < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Device::readI2C(uint8_t addr, uint8_t* data, int len) {
|
|
||||||
// Do read
|
|
||||||
int bytes = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_RW, 0, addr, data, len, 1000);
|
|
||||||
if (bytes < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get status (TODO: Use function)
|
|
||||||
int status;
|
|
||||||
int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
|
|
||||||
if (ret <= 0 || status < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Device::writeI2C(uint8_t addr, const uint8_t* data, int len) {
|
|
||||||
// Do write
|
|
||||||
int bytes = libusb_control_transfer(dev, (2 << 5), CMD_I2C_RW, 0, addr, (uint8_t*)data, len, 1000);
|
|
||||||
if (bytes < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get status (TODO: Use function)
|
|
||||||
int status;
|
|
||||||
int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
|
|
||||||
if (ret <= 0 || status < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t bitrev(uint8_t val) {
|
|
||||||
return ((val & 0x01) << 7) | ((val & 0x02) << 5) | ((val & 0x04) << 3) | ((val & 0x08) << 1) | ((val & 0x10) >> 1) | ((val & 0x20) >> 3) | ((val & 0x40) >> 5) | ((val & 0x80) >> 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t Device::readR820TReg(uint8_t reg) {
|
|
||||||
// Read registers up to it (can't read single)
|
|
||||||
uint8_t regs[0x20];
|
|
||||||
readI2C(R820T_I2C_ADDR, regs, reg+1);
|
|
||||||
|
|
||||||
// Invert bit order
|
|
||||||
return bitrev(regs[reg]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::writeR820TReg(uint8_t reg, uint8_t val) {
|
|
||||||
// Write register id then value
|
|
||||||
uint8_t cmd[2] = { reg, val };
|
|
||||||
writeI2C(R820T_I2C_ADDR, cmd, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Device::startADC() {
|
|
||||||
return libusb_control_transfer(dev, (2 << 5), CMD_ADC_START, 0, 0, NULL, 0, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Device::stopADC() {
|
|
||||||
return libusb_control_transfer(dev, (2 << 5), CMD_ADC_STOP, 0, 0, NULL, 0, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Device::worker() {
|
|
||||||
// Allocate sample buffer
|
|
||||||
uint8_t* buffer = new uint8_t[bufferSize];
|
|
||||||
int sampleCount = 0;
|
|
||||||
|
|
||||||
while (run) {
|
|
||||||
// Receive data with bulk transfer
|
|
||||||
int recvLen = 0;
|
|
||||||
int val = libusb_bulk_transfer(dev, LIBUSB_ENDPOINT_IN | 1, &buffer[sampleCount], bufferSize - sampleCount, &recvLen, 1000);
|
|
||||||
|
|
||||||
// If timed out, try again. Otherwise, if an error occur, stop the thread
|
|
||||||
if (val == LIBUSB_ERROR_TIMEOUT) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (val) {
|
|
||||||
flog::error("USB Error: {}", val);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increment sample count
|
|
||||||
if (recvLen) { sampleCount += recvLen; }
|
|
||||||
|
|
||||||
// If the buffer is full, call handler and reset sample count
|
|
||||||
if (sampleCount >= bufferSize) {
|
|
||||||
callback(buffer, sampleCount, ctx);
|
|
||||||
sampleCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free buffer
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<DeviceInfo> list() {
|
|
||||||
// Init libusb if done yet
|
|
||||||
if (!ctx) {
|
|
||||||
libusb_init(&ctx);
|
|
||||||
libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
// List devices
|
|
||||||
std::vector<DeviceInfo> devList;
|
|
||||||
libusb_device** devices;
|
|
||||||
int devCount = libusb_get_device_list(ctx, &devices);
|
|
||||||
for (int i = 0; i < devCount; i++) {
|
|
||||||
// Get device info
|
|
||||||
DeviceInfo devInfo;
|
|
||||||
devInfo.dev = devices[i];
|
|
||||||
libusb_device_descriptor desc;
|
|
||||||
libusb_get_device_descriptor(devInfo.dev, &desc);
|
|
||||||
|
|
||||||
// Check the VID/PID and give up if not the right ones
|
|
||||||
if (desc.idVendor != 0xCAFE || desc.idProduct != 0x4010) {
|
|
||||||
libusb_unref_device(devInfo.dev);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open devices
|
|
||||||
libusb_device_handle* openDev;
|
|
||||||
int err = libusb_open(devInfo.dev, &openDev);
|
|
||||||
if (err) {
|
|
||||||
libusb_unref_device(devInfo.dev);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get serial number
|
|
||||||
char serial[128];
|
|
||||||
libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, (uint8_t*)serial, sizeof(serial));
|
|
||||||
devInfo.serialNumber = serial;
|
|
||||||
|
|
||||||
// TODO: The libusb device should be unreffed but would need to know when the list disappears
|
|
||||||
|
|
||||||
// Close device
|
|
||||||
libusb_close(openDev);
|
|
||||||
|
|
||||||
// Add to list
|
|
||||||
devList.push_back(devInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return devices
|
|
||||||
return devList;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Device> open(const DeviceInfo& dev) {
|
|
||||||
// Open device
|
|
||||||
libusb_device_handle* openDev;
|
|
||||||
int err = libusb_open(dev.dev, &openDev);
|
|
||||||
if (err) {
|
|
||||||
throw std::runtime_error("Failed to open device");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Claim the bulk transfer interface
|
|
||||||
if (libusb_claim_interface(openDev, 0)) {
|
|
||||||
throw std::runtime_error("Failed to claim bulk interface");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create device
|
|
||||||
return std::make_shared<Device>(openDev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
#include <libusb.h>
|
|
||||||
#include "r820t.h"
|
|
||||||
|
|
||||||
namespace BadgeSDR {
|
|
||||||
struct DeviceInfo {
|
|
||||||
std::string serialNumber;
|
|
||||||
libusb_device* dev;
|
|
||||||
bool operator==(const DeviceInfo& b) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Device {
|
|
||||||
public:
|
|
||||||
Device(libusb_device_handle* dev);
|
|
||||||
~Device();
|
|
||||||
|
|
||||||
void setFrequency(double freq);
|
|
||||||
void setLNAGain(int gain);
|
|
||||||
void setMixerGain(int gain);
|
|
||||||
void setVGAGain(int gain);
|
|
||||||
|
|
||||||
void start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx = NULL, int minBufferSize = 2500);
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int getI2CStatus();
|
|
||||||
int readI2C(uint8_t addr, uint8_t* data, int len);
|
|
||||||
int writeI2C(uint8_t addr, const uint8_t* data, int len);
|
|
||||||
uint8_t readR820TReg(uint8_t reg);
|
|
||||||
void writeR820TReg(uint8_t reg, uint8_t val);
|
|
||||||
int startADC();
|
|
||||||
int stopADC();
|
|
||||||
void worker();
|
|
||||||
|
|
||||||
libusb_device_handle* dev;
|
|
||||||
std::thread workerThread;
|
|
||||||
bool run = false;
|
|
||||||
int bufferSize = 0; // Must be multiple of 64 for best performance
|
|
||||||
void* ctx = NULL;
|
|
||||||
void (*callback)(const uint8_t* samples, int count, void* ctx);
|
|
||||||
|
|
||||||
static void write_reg(uint8_t reg, uint8_t value, void* ctx);
|
|
||||||
static void read_reg(uint8_t* data, int len, void* ctx);
|
|
||||||
r820t_priv_t r820t;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<DeviceInfo> list();
|
|
||||||
std::shared_ptr<Device> open(const DeviceInfo& dev);
|
|
||||||
}
|
|
||||||
@@ -1,272 +0,0 @@
|
|||||||
#include <imgui.h>
|
|
||||||
#include <module.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include <gui/smgui.h>
|
|
||||||
#include <signal_path/signal_path.h>
|
|
||||||
#include <core.h>
|
|
||||||
#include <utils/optionlist.h>
|
|
||||||
#include <dsp/channel/rx_vfo.h>
|
|
||||||
#include <dsp/correction/dc_blocker.h>
|
|
||||||
#include "badgesdr.h"
|
|
||||||
|
|
||||||
SDRPP_MOD_INFO{
|
|
||||||
/* Name: */ "badgesdr_source",
|
|
||||||
/* Description: */ "BadgeSDR Source Module",
|
|
||||||
/* Author: */ "Ryzerth",
|
|
||||||
/* Version: */ 0, 1, 0,
|
|
||||||
/* Max instances */ -1
|
|
||||||
};
|
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
|
||||||
|
|
||||||
class BadgeSDRSourceModule : public ModuleManager::Instance {
|
|
||||||
public:
|
|
||||||
BadgeSDRSourceModule(std::string name) {
|
|
||||||
this->name = name;
|
|
||||||
|
|
||||||
sampleRate = 250000.0;
|
|
||||||
|
|
||||||
// Initialize DSP
|
|
||||||
dcBlock.init(&input, 0.001);
|
|
||||||
ddc.init(&dcBlock.out, 500000, 250000, 250000, 125000);
|
|
||||||
|
|
||||||
handler.ctx = this;
|
|
||||||
handler.selectHandler = menuSelected;
|
|
||||||
handler.deselectHandler = menuDeselected;
|
|
||||||
handler.menuHandler = menuHandler;
|
|
||||||
handler.startHandler = start;
|
|
||||||
handler.stopHandler = stop;
|
|
||||||
handler.tuneHandler = tune;
|
|
||||||
handler.stream = &ddc.out;
|
|
||||||
|
|
||||||
// Refresh devices
|
|
||||||
refresh();
|
|
||||||
|
|
||||||
// Select first (TODO: Select from config)
|
|
||||||
select("");
|
|
||||||
|
|
||||||
sigpath::sourceManager.registerSource("BadgeSDR", &handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
~BadgeSDRSourceModule() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void postInit() {}
|
|
||||||
|
|
||||||
void enable() {
|
|
||||||
enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable() {
|
|
||||||
enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void refresh() {
|
|
||||||
devices.clear();
|
|
||||||
auto list = BadgeSDR::list();
|
|
||||||
for (const auto& info : list) {
|
|
||||||
// Format device name
|
|
||||||
std::string devName = "BadgeSDR ";
|
|
||||||
devName += " [";
|
|
||||||
devName += info.serialNumber;
|
|
||||||
devName += ']';
|
|
||||||
|
|
||||||
// Save device
|
|
||||||
devices.define(info.serialNumber, devName, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void select(const std::string& serial) {
|
|
||||||
// If there are no devices, give up
|
|
||||||
if (devices.empty()) {
|
|
||||||
selectedSerial.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the serial was not found, select the first available serial
|
|
||||||
if (!devices.keyExists(serial)) {
|
|
||||||
select(devices.key(0));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save serial number
|
|
||||||
selectedSerial = serial;
|
|
||||||
selectedDev = devices.value(devices.keyId(serial));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void menuSelected(void* ctx) {
|
|
||||||
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
|
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
|
||||||
flog::info("BadgeSDRSourceModule '{0}': Menu Select!", _this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void menuDeselected(void* ctx) {
|
|
||||||
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
|
|
||||||
flog::info("BadgeSDRSourceModule '{0}': Menu Deselect!", _this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void start(void* ctx) {
|
|
||||||
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
|
|
||||||
if (_this->running) { return; }
|
|
||||||
|
|
||||||
// Open the device
|
|
||||||
_this->openDev = BadgeSDR::open(_this->selectedDev);
|
|
||||||
|
|
||||||
// Configure the device
|
|
||||||
_this->openDev->setFrequency(_this->freq);
|
|
||||||
_this->openDev->setLNAGain(_this->lnaGain);
|
|
||||||
_this->openDev->setMixerGain(_this->mixerGain);
|
|
||||||
_this->openDev->setVGAGain(_this->vgaGain);
|
|
||||||
|
|
||||||
// Start DSP
|
|
||||||
_this->dcBlock.start();
|
|
||||||
_this->ddc.start();
|
|
||||||
|
|
||||||
// Start device
|
|
||||||
_this->openDev->start(callback, _this, 500000/200);
|
|
||||||
|
|
||||||
_this->running = true;
|
|
||||||
flog::info("BadgeSDRSourceModule '{0}': Start!", _this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void stop(void* ctx) {
|
|
||||||
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
|
|
||||||
if (!_this->running) { return; }
|
|
||||||
_this->running = false;
|
|
||||||
|
|
||||||
// Stop worker
|
|
||||||
_this->openDev->stop();
|
|
||||||
|
|
||||||
// Stop DSP
|
|
||||||
_this->dcBlock.stop();
|
|
||||||
_this->ddc.stop();
|
|
||||||
|
|
||||||
// Close device
|
|
||||||
_this->openDev.reset();
|
|
||||||
|
|
||||||
flog::info("BadgeSDRSourceModule '{0}': Stop!", _this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tune(double freq, void* ctx) {
|
|
||||||
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
|
|
||||||
if (_this->running) {
|
|
||||||
_this->openDev->setFrequency(freq);
|
|
||||||
}
|
|
||||||
_this->freq = freq;
|
|
||||||
flog::info("BadgeSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void menuHandler(void* ctx) {
|
|
||||||
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
|
|
||||||
|
|
||||||
if (_this->running) { SmGui::BeginDisabled(); }
|
|
||||||
|
|
||||||
SmGui::FillWidth();
|
|
||||||
SmGui::ForceSync();
|
|
||||||
if (SmGui::Combo(CONCAT("##_badgesdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
|
||||||
_this->select(_this->devices.key(_this->devId));
|
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
|
|
||||||
SmGui::FillWidth();
|
|
||||||
SmGui::ForceSync();
|
|
||||||
if (SmGui::Button(CONCAT("Refresh##_badgesdr_refr_", _this->name))) {
|
|
||||||
_this->refresh();
|
|
||||||
_this->select(_this->selectedSerial);
|
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_this->running) { SmGui::EndDisabled(); }
|
|
||||||
|
|
||||||
SmGui::LeftLabel("LNA Gain");
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::SliderInt(CONCAT("##_badgesdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
|
|
||||||
if (_this->running) {
|
|
||||||
_this->openDev->setLNAGain(_this->lnaGain);
|
|
||||||
}
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
|
|
||||||
SmGui::LeftLabel("Mixer Gain");
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::SliderInt(CONCAT("##_badgesdr_mixer_gain_", _this->name), &_this->mixerGain, 0, 15)) {
|
|
||||||
if (_this->running) {
|
|
||||||
_this->openDev->setMixerGain(_this->mixerGain);
|
|
||||||
}
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
|
|
||||||
SmGui::LeftLabel("VGA Gain");
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::SliderInt(CONCAT("##_badgesdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
|
|
||||||
if (_this->running) {
|
|
||||||
_this->openDev->setVGAGain(_this->vgaGain);
|
|
||||||
}
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void callback(const uint8_t* samples, int count, void* ctx) {
|
|
||||||
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
|
|
||||||
|
|
||||||
// Convert samples to float
|
|
||||||
dsp::complex_t* out = _this->input.writeBuf;
|
|
||||||
int min = 255, max = 0;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
if (samples[i] < min) { min = samples[i]; }
|
|
||||||
if (samples[i] > max) { max = samples[i]; }
|
|
||||||
|
|
||||||
out[i].re = ((float)samples[i] - 127.5f) * (1.0f/127.0f);
|
|
||||||
out[i].im = 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send out samples
|
|
||||||
_this->input.swap(count);
|
|
||||||
|
|
||||||
flog::debug("Amplitudes: {} -> {}", min, max);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
bool enabled = true;
|
|
||||||
double sampleRate;
|
|
||||||
SourceManager::SourceHandler handler;
|
|
||||||
bool running = false;
|
|
||||||
double freq;
|
|
||||||
|
|
||||||
OptionList<std::string, BadgeSDR::DeviceInfo> devices;
|
|
||||||
|
|
||||||
int devId = 0;
|
|
||||||
int lnaGain = 0;
|
|
||||||
int mixerGain = 0;
|
|
||||||
int vgaGain = 0;
|
|
||||||
std::string selectedSerial;
|
|
||||||
BadgeSDR::DeviceInfo selectedDev;
|
|
||||||
std::shared_ptr<BadgeSDR::Device> openDev;
|
|
||||||
|
|
||||||
dsp::stream<dsp::complex_t> input;
|
|
||||||
dsp::correction::DCBlocker<dsp::complex_t> dcBlock;
|
|
||||||
dsp::channel::RxVFO ddc;
|
|
||||||
};
|
|
||||||
|
|
||||||
MOD_EXPORT void _INIT_() {
|
|
||||||
// Nothing here
|
|
||||||
}
|
|
||||||
|
|
||||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
|
||||||
return new BadgeSDRSourceModule(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
|
||||||
delete (BadgeSDRSourceModule*)instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOD_EXPORT void _END_() {
|
|
||||||
// Nothing here
|
|
||||||
}
|
|
||||||
@@ -1,622 +0,0 @@
|
|||||||
/*
|
|
||||||
* Rafael Micro R820T driver for AIRSPY
|
|
||||||
*
|
|
||||||
* Copyright 2013 Youssef Touil <youssef@airspy.com>
|
|
||||||
* Copyright 2014-2016 Benjamin Vernoux <bvernoux@airspy.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "r820t.h"
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
static int r820t_read_cache_reg(r820t_priv_t *priv, int reg);
|
|
||||||
|
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
|
||||||
|
|
||||||
/* Tuner frequency ranges */
|
|
||||||
struct r820t_freq_range
|
|
||||||
{
|
|
||||||
uint8_t open_d;
|
|
||||||
uint8_t rf_mux_ploy;
|
|
||||||
uint8_t tf_c;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define R820T_READ_MAX_DATA 32
|
|
||||||
#define R820T_INIT_NB_REGS (32-5)
|
|
||||||
uint8_t r820t_read_data[R820T_READ_MAX_DATA]; /* Buffer for data read from I2C */
|
|
||||||
uint8_t r820t_state_standby = 1; /* 1=standby/power off 0=r820t initialized/power on */
|
|
||||||
|
|
||||||
/* Tuner frequency ranges
|
|
||||||
"Copyright (C) 2013 Mauro Carvalho Chehab"
|
|
||||||
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
|
|
||||||
part of freq_ranges()
|
|
||||||
*/
|
|
||||||
const struct r820t_freq_range freq_ranges[] =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
/* 0 MHz */
|
|
||||||
/* .open_d = */ 0x08, /* low */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0xdf, /* R27[7:0] band2,band0 */
|
|
||||||
}, {
|
|
||||||
/* 50 MHz */
|
|
||||||
/* .open_d = */ 0x08, /* low */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0xbe, /* R27[7:0] band4,band1 */
|
|
||||||
}, {
|
|
||||||
/* 55 MHz */
|
|
||||||
/* .open_d = */ 0x08, /* low */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x8b, /* R27[7:0] band7,band4 */
|
|
||||||
}, {
|
|
||||||
/* 60 MHz */
|
|
||||||
/* .open_d = */ 0x08, /* low */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x7b, /* R27[7:0] band8,band4 */
|
|
||||||
}, {
|
|
||||||
/* 65 MHz */
|
|
||||||
/* .open_d = */ 0x08, /* low */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x69, /* R27[7:0] band9,band6 */
|
|
||||||
}, {
|
|
||||||
/* 70 MHz */
|
|
||||||
/* .open_d = */ 0x08, /* low */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x58, /* R27[7:0] band10,band7 */
|
|
||||||
}, {
|
|
||||||
/* 75 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */
|
|
||||||
}, {
|
|
||||||
/* 80 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */
|
|
||||||
}, {
|
|
||||||
/* 90 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */
|
|
||||||
}, {
|
|
||||||
/* 100 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */
|
|
||||||
}, {
|
|
||||||
/* 110 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */
|
|
||||||
}, {
|
|
||||||
/* 120 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */
|
|
||||||
}, {
|
|
||||||
/* 140 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x14, /* R27[7:0] band14,band11 */
|
|
||||||
}, {
|
|
||||||
/* 180 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */
|
|
||||||
}, {
|
|
||||||
/* 220 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */
|
|
||||||
}, {
|
|
||||||
/* 250 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x11, /* R27[7:0] highest,highest */
|
|
||||||
}, {
|
|
||||||
/* 280 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
|
|
||||||
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
|
|
||||||
}, {
|
|
||||||
/* 310 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */
|
|
||||||
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
|
|
||||||
}, {
|
|
||||||
/* 450 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */
|
|
||||||
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
|
|
||||||
}, {
|
|
||||||
/* 588 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */
|
|
||||||
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
|
|
||||||
}, {
|
|
||||||
/* 650 MHz */
|
|
||||||
/* .open_d = */ 0x00, /* high */
|
|
||||||
/* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */
|
|
||||||
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FREQ_TO_IDX_SIZE (600)
|
|
||||||
const uint8_t freq_to_idx[FREQ_TO_IDX_SIZE]=
|
|
||||||
{
|
|
||||||
/* 50 */ 1,/* 51 */ 1,/* 52 */ 1,/* 53 */ 1,/* 54 */ 1,
|
|
||||||
/* 55 */ 2,/* 56 */ 2,/* 57 */ 2,/* 58 */ 2,/* 59 */ 2,
|
|
||||||
/* 60 */ 3,/* 61 */ 3,/* 62 */ 3,/* 63 */ 3,/* 64 */ 3,
|
|
||||||
/* 65 */ 4,/* 66 */ 4,/* 67 */ 4,/* 68 */ 4,/* 69 */ 4,
|
|
||||||
/* 70 */ 5,/* 71 */ 5,/* 72 */ 5,/* 73 */ 5,/* 74 */ 5,
|
|
||||||
/* 75 */ 6,/* 76 */ 6,/* 77 */ 6,/* 78 */ 6,/* 79 */ 6,
|
|
||||||
/* 80 */ 7,/* 81 */ 7,/* 82 */ 7,/* 83 */ 7,/* 84 */ 7,/* 85 */ 7,/* 86 */ 7,/* 87 */ 7,/* 88 */ 7,/* 89 */ 7,
|
|
||||||
/* 90 */ 8,/* 91 */ 8,/* 92 */ 8,/* 93 */ 8,/* 94 */ 8,/* 95 */ 8,/* 96 */ 8,/* 97 */ 8,/* 98 */ 8,/* 99 */ 8,
|
|
||||||
/* 100 */ 9,/* 101 */ 9,/* 102 */ 9,/* 103 */ 9,/* 104 */ 9,/* 105 */ 9,/* 106 */ 9,/* 107 */ 9,/* 108 */ 9,/* 109 */ 9,
|
|
||||||
/* 110 */ 10,/* 111 */ 10,/* 112 */ 10,/* 113 */ 10,/* 114 */ 10,/* 115 */ 10,/* 116 */ 10,/* 117 */ 10,/* 118 */ 10,/* 119 */ 10,
|
|
||||||
/* 120 */ 11,/* 121 */ 11,/* 122 */ 11,/* 123 */ 11,/* 124 */ 11,/* 125 */ 11,/* 126 */ 11,/* 127 */ 11,/* 128 */ 11,/* 129 */ 11,
|
|
||||||
/* 130 */ 11,/* 131 */ 11,/* 132 */ 11,/* 133 */ 11,/* 134 */ 11,/* 135 */ 11,/* 136 */ 11,/* 137 */ 11,/* 138 */ 11,/* 139 */ 11,
|
|
||||||
/* 140 */ 12,/* 141 */ 12,/* 142 */ 12,/* 143 */ 12,/* 144 */ 12,/* 145 */ 12,/* 146 */ 12,/* 147 */ 12,/* 148 */ 12,/* 149 */ 12,
|
|
||||||
/* 150 */ 12,/* 151 */ 12,/* 152 */ 12,/* 153 */ 12,/* 154 */ 12,/* 155 */ 12,/* 156 */ 12,/* 157 */ 12,/* 158 */ 12,/* 159 */ 12,
|
|
||||||
/* 160 */ 12,/* 161 */ 12,/* 162 */ 12,/* 163 */ 12,/* 164 */ 12,/* 165 */ 12,/* 166 */ 12,/* 167 */ 12,/* 168 */ 12,/* 169 */ 12,
|
|
||||||
/* 170 */ 12,/* 171 */ 12,/* 172 */ 12,/* 173 */ 12,/* 174 */ 12,/* 175 */ 12,/* 176 */ 12,/* 177 */ 12,/* 178 */ 12,/* 179 */ 12,
|
|
||||||
/* 180 */ 13,/* 181 */ 13,/* 182 */ 13,/* 183 */ 13,/* 184 */ 13,/* 185 */ 13,/* 186 */ 13,/* 187 */ 13,/* 188 */ 13,/* 189 */ 13,
|
|
||||||
/* 190 */ 13,/* 191 */ 13,/* 192 */ 13,/* 193 */ 13,/* 194 */ 13,/* 195 */ 13,/* 196 */ 13,/* 197 */ 13,/* 198 */ 13,/* 199 */ 13,
|
|
||||||
/* 200 */ 13,/* 201 */ 13,/* 202 */ 13,/* 203 */ 13,/* 204 */ 13,/* 205 */ 13,/* 206 */ 13,/* 207 */ 13,/* 208 */ 13,/* 209 */ 13,
|
|
||||||
/* 210 */ 13,/* 211 */ 13,/* 212 */ 13,/* 213 */ 13,/* 214 */ 13,/* 215 */ 13,/* 216 */ 13,/* 217 */ 13,/* 218 */ 13,/* 219 */ 13,
|
|
||||||
/* 220 */ 14,/* 221 */ 14,/* 222 */ 14,/* 223 */ 14,/* 224 */ 14,/* 225 */ 14,/* 226 */ 14,/* 227 */ 14,/* 228 */ 14,/* 229 */ 14,
|
|
||||||
/* 230 */ 14,/* 231 */ 14,/* 232 */ 14,/* 233 */ 14,/* 234 */ 14,/* 235 */ 14,/* 236 */ 14,/* 237 */ 14,/* 238 */ 14,/* 239 */ 14,
|
|
||||||
/* 240 */ 14,/* 241 */ 14,/* 242 */ 14,/* 243 */ 14,/* 244 */ 14,/* 245 */ 14,/* 246 */ 14,/* 247 */ 14,/* 248 */ 14,/* 249 */ 14,
|
|
||||||
/* 250 */ 15,/* 251 */ 15,/* 252 */ 15,/* 253 */ 15,/* 254 */ 15,/* 255 */ 15,/* 256 */ 15,/* 257 */ 15,/* 258 */ 15,/* 259 */ 15,
|
|
||||||
/* 260 */ 15,/* 261 */ 15,/* 262 */ 15,/* 263 */ 15,/* 264 */ 15,/* 265 */ 15,/* 266 */ 15,/* 267 */ 15,/* 268 */ 15,/* 269 */ 15,
|
|
||||||
/* 270 */ 15,/* 271 */ 15,/* 272 */ 15,/* 273 */ 15,/* 274 */ 15,/* 275 */ 15,/* 276 */ 15,/* 277 */ 15,/* 278 */ 15,/* 279 */ 15,
|
|
||||||
/* 280 */ 16,/* 281 */ 16,/* 282 */ 16,/* 283 */ 16,/* 284 */ 16,/* 285 */ 16,/* 286 */ 16,/* 287 */ 16,/* 288 */ 16,/* 289 */ 16,
|
|
||||||
/* 290 */ 16,/* 291 */ 16,/* 292 */ 16,/* 293 */ 16,/* 294 */ 16,/* 295 */ 16,/* 296 */ 16,/* 297 */ 16,/* 298 */ 16,/* 299 */ 16,
|
|
||||||
/* 300 */ 16,/* 301 */ 16,/* 302 */ 16,/* 303 */ 16,/* 304 */ 16,/* 305 */ 16,/* 306 */ 16,/* 307 */ 16,/* 308 */ 16,/* 309 */ 16,
|
|
||||||
/* 310 */ 17,/* 311 */ 17,/* 312 */ 17,/* 313 */ 17,/* 314 */ 17,/* 315 */ 17,/* 316 */ 17,/* 317 */ 17,/* 318 */ 17,/* 319 */ 17,
|
|
||||||
/* 320 */ 17,/* 321 */ 17,/* 322 */ 17,/* 323 */ 17,/* 324 */ 17,/* 325 */ 17,/* 326 */ 17,/* 327 */ 17,/* 328 */ 17,/* 329 */ 17,
|
|
||||||
/* 330 */ 17,/* 331 */ 17,/* 332 */ 17,/* 333 */ 17,/* 334 */ 17,/* 335 */ 17,/* 336 */ 17,/* 337 */ 17,/* 338 */ 17,/* 339 */ 17,
|
|
||||||
/* 340 */ 17,/* 341 */ 17,/* 342 */ 17,/* 343 */ 17,/* 344 */ 17,/* 345 */ 17,/* 346 */ 17,/* 347 */ 17,/* 348 */ 17,/* 349 */ 17,
|
|
||||||
/* 350 */ 17,/* 351 */ 17,/* 352 */ 17,/* 353 */ 17,/* 354 */ 17,/* 355 */ 17,/* 356 */ 17,/* 357 */ 17,/* 358 */ 17,/* 359 */ 17,
|
|
||||||
/* 360 */ 17,/* 361 */ 17,/* 362 */ 17,/* 363 */ 17,/* 364 */ 17,/* 365 */ 17,/* 366 */ 17,/* 367 */ 17,/* 368 */ 17,/* 369 */ 17,
|
|
||||||
/* 370 */ 17,/* 371 */ 17,/* 372 */ 17,/* 373 */ 17,/* 374 */ 17,/* 375 */ 17,/* 376 */ 17,/* 377 */ 17,/* 378 */ 17,/* 379 */ 17,
|
|
||||||
/* 380 */ 17,/* 381 */ 17,/* 382 */ 17,/* 383 */ 17,/* 384 */ 17,/* 385 */ 17,/* 386 */ 17,/* 387 */ 17,/* 388 */ 17,/* 389 */ 17,
|
|
||||||
/* 390 */ 17,/* 391 */ 17,/* 392 */ 17,/* 393 */ 17,/* 394 */ 17,/* 395 */ 17,/* 396 */ 17,/* 397 */ 17,/* 398 */ 17,/* 399 */ 17,
|
|
||||||
/* 400 */ 17,/* 401 */ 17,/* 402 */ 17,/* 403 */ 17,/* 404 */ 17,/* 405 */ 17,/* 406 */ 17,/* 407 */ 17,/* 408 */ 17,/* 409 */ 17,
|
|
||||||
/* 410 */ 17,/* 411 */ 17,/* 412 */ 17,/* 413 */ 17,/* 414 */ 17,/* 415 */ 17,/* 416 */ 17,/* 417 */ 17,/* 418 */ 17,/* 419 */ 17,
|
|
||||||
/* 420 */ 17,/* 421 */ 17,/* 422 */ 17,/* 423 */ 17,/* 424 */ 17,/* 425 */ 17,/* 426 */ 17,/* 427 */ 17,/* 428 */ 17,/* 429 */ 17,
|
|
||||||
/* 430 */ 17,/* 431 */ 17,/* 432 */ 17,/* 433 */ 17,/* 434 */ 17,/* 435 */ 17,/* 436 */ 17,/* 437 */ 17,/* 438 */ 17,/* 439 */ 17,
|
|
||||||
/* 440 */ 17,/* 441 */ 17,/* 442 */ 17,/* 443 */ 17,/* 444 */ 17,/* 445 */ 17,/* 446 */ 17,/* 447 */ 17,/* 448 */ 17,/* 449 */ 17,
|
|
||||||
/* 450 */ 18,/* 451 */ 18,/* 452 */ 18,/* 453 */ 18,/* 454 */ 18,/* 455 */ 18,/* 456 */ 18,/* 457 */ 18,/* 458 */ 18,/* 459 */ 18,
|
|
||||||
/* 460 */ 18,/* 461 */ 18,/* 462 */ 18,/* 463 */ 18,/* 464 */ 18,/* 465 */ 18,/* 466 */ 18,/* 467 */ 18,/* 468 */ 18,/* 469 */ 18,
|
|
||||||
/* 470 */ 18,/* 471 */ 18,/* 472 */ 18,/* 473 */ 18,/* 474 */ 18,/* 475 */ 18,/* 476 */ 18,/* 477 */ 18,/* 478 */ 18,/* 479 */ 18,
|
|
||||||
/* 480 */ 18,/* 481 */ 18,/* 482 */ 18,/* 483 */ 18,/* 484 */ 18,/* 485 */ 18,/* 486 */ 18,/* 487 */ 18,/* 488 */ 18,/* 489 */ 18,
|
|
||||||
/* 490 */ 18,/* 491 */ 18,/* 492 */ 18,/* 493 */ 18,/* 494 */ 18,/* 495 */ 18,/* 496 */ 18,/* 497 */ 18,/* 498 */ 18,/* 499 */ 18,
|
|
||||||
/* 500 */ 18,/* 501 */ 18,/* 502 */ 18,/* 503 */ 18,/* 504 */ 18,/* 505 */ 18,/* 506 */ 18,/* 507 */ 18,/* 508 */ 18,/* 509 */ 18,
|
|
||||||
/* 510 */ 18,/* 511 */ 18,/* 512 */ 18,/* 513 */ 18,/* 514 */ 18,/* 515 */ 18,/* 516 */ 18,/* 517 */ 18,/* 518 */ 18,/* 519 */ 18,
|
|
||||||
/* 520 */ 18,/* 521 */ 18,/* 522 */ 18,/* 523 */ 18,/* 524 */ 18,/* 525 */ 18,/* 526 */ 18,/* 527 */ 18,/* 528 */ 18,/* 529 */ 18,
|
|
||||||
/* 530 */ 18,/* 531 */ 18,/* 532 */ 18,/* 533 */ 18,/* 534 */ 18,/* 535 */ 18,/* 536 */ 18,/* 537 */ 18,/* 538 */ 18,/* 539 */ 18,
|
|
||||||
/* 540 */ 18,/* 541 */ 18,/* 542 */ 18,/* 543 */ 18,/* 544 */ 18,/* 545 */ 18,/* 546 */ 18,/* 547 */ 18,/* 548 */ 18,/* 549 */ 18,
|
|
||||||
/* 550 */ 18,/* 551 */ 18,/* 552 */ 18,/* 553 */ 18,/* 554 */ 18,/* 555 */ 18,/* 556 */ 18,/* 557 */ 18,/* 558 */ 18,/* 559 */ 18,
|
|
||||||
/* 560 */ 18,/* 561 */ 18,/* 562 */ 18,/* 563 */ 18,/* 564 */ 18,/* 565 */ 18,/* 566 */ 18,/* 567 */ 18,/* 568 */ 18,/* 569 */ 18,
|
|
||||||
/* 570 */ 18,/* 571 */ 18,/* 572 */ 18,/* 573 */ 18,/* 574 */ 18,/* 575 */ 18,/* 576 */ 18,/* 577 */ 18,/* 578 */ 18,/* 579 */ 18,
|
|
||||||
/* 580 */ 18,/* 581 */ 18,/* 582 */ 18,/* 583 */ 18,/* 584 */ 18,/* 585 */ 18,/* 586 */ 18,/* 587 */ 18,
|
|
||||||
/* 588 */ 19,/* 589 */ 19,/* 590 */ 19,/* 591 */ 19,/* 592 */ 19,/* 593 */ 19,/* 594 */ 19,/* 595 */ 19,/* 596 */ 19,/* 597 */ 19,
|
|
||||||
/* 598 */ 19,/* 599 */ 19,/* 600 */ 19,/* 601 */ 19,/* 602 */ 19,/* 603 */ 19,/* 604 */ 19,/* 605 */ 19,/* 606 */ 19,/* 607 */ 19,
|
|
||||||
/* 608 */ 19,/* 609 */ 19,/* 610 */ 19,/* 611 */ 19,/* 612 */ 19,/* 613 */ 19,/* 614 */ 19,/* 615 */ 19,/* 616 */ 19,/* 617 */ 19,
|
|
||||||
/* 618 */ 19,/* 619 */ 19,/* 620 */ 19,/* 621 */ 19,/* 622 */ 19,/* 623 */ 19,/* 624 */ 19,/* 625 */ 19,/* 626 */ 19,/* 627 */ 19,
|
|
||||||
/* 628 */ 19,/* 629 */ 19,/* 630 */ 19,/* 631 */ 19,/* 632 */ 19,/* 633 */ 19,/* 634 */ 19,/* 635 */ 19,/* 636 */ 19,/* 637 */ 19,
|
|
||||||
/* 638 */ 19,/* 639 */ 19,/* 640 */ 19,/* 641 */ 19,/* 642 */ 19,/* 643 */ 19,/* 644 */ 19,/* 645 */ 19,/* 646 */ 19,/* 647 */ 19,
|
|
||||||
/* 648 */ 19,/* 649 */ 19
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FREQ_50MHZ (50)
|
|
||||||
#define FREQ_TO_IDX_0_TO_49MHZ (0)
|
|
||||||
#define FREQ_TO_IDX_650_TO_1800MHZ (20)
|
|
||||||
|
|
||||||
int r820t_freq_get_idx(uint32_t freq_mhz)
|
|
||||||
{
|
|
||||||
uint32_t freq_mhz_fix;
|
|
||||||
|
|
||||||
if(freq_mhz < FREQ_50MHZ)
|
|
||||||
{
|
|
||||||
/* Frequency Less than 50MHz */
|
|
||||||
return FREQ_TO_IDX_0_TO_49MHZ;
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
/* Frequency Between 50 to 649MHz use table */
|
|
||||||
/* Fix the frequency for the table */
|
|
||||||
freq_mhz_fix = freq_mhz - FREQ_50MHZ;
|
|
||||||
if(freq_mhz_fix < FREQ_TO_IDX_SIZE)
|
|
||||||
{
|
|
||||||
|
|
||||||
return freq_to_idx[freq_mhz_fix];
|
|
||||||
}else
|
|
||||||
{
|
|
||||||
/* Frequency Between 650 to 1800MHz */
|
|
||||||
return FREQ_TO_IDX_650_TO_1800MHZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool r820t_is_power_enabled(void)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Write regs 5 to 32 (R820T_INIT_NB_REGS values) using data parameter and write last reg to 0
|
|
||||||
*/
|
|
||||||
void airspy_r820t_write_init(r820t_priv_t *priv, const uint8_t* data)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < R820T_INIT_NB_REGS; i++) {
|
|
||||||
priv->write_reg(i+REG_SHADOW_START, data[i], priv->ctx);
|
|
||||||
}
|
|
||||||
priv->write_reg(0x1F, 0, priv->ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read from one or more contiguous registers. data[0] should be the first
|
|
||||||
* register number, one or more values follow.
|
|
||||||
*/
|
|
||||||
const uint8_t lut[16] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
|
|
||||||
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf };
|
|
||||||
|
|
||||||
static uint8_t r82xx_bitrev(uint8_t byte)
|
|
||||||
{
|
|
||||||
return (lut[byte & 0xf] << 4) | lut[byte >> 4];
|
|
||||||
}
|
|
||||||
|
|
||||||
static int r820t_write_reg(r820t_priv_t *priv, uint8_t reg, uint8_t val)
|
|
||||||
{
|
|
||||||
if (r820t_read_cache_reg(priv, reg) == val)
|
|
||||||
return 0;
|
|
||||||
priv->write_reg(reg, val, priv->ctx);
|
|
||||||
priv->regs[reg - REG_SHADOW_START] = val;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int r820t_read_cache_reg(r820t_priv_t *priv, int reg)
|
|
||||||
{
|
|
||||||
reg -= REG_SHADOW_START;
|
|
||||||
|
|
||||||
if (reg >= 0 && reg < NUM_REGS)
|
|
||||||
return priv->regs[reg];
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int r820t_write_reg_mask(r820t_priv_t *priv, uint8_t reg, uint8_t val, uint8_t bit_mask)
|
|
||||||
{
|
|
||||||
int rc = r820t_read_cache_reg(priv, reg);
|
|
||||||
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
val = (rc & ~bit_mask) | (val & bit_mask);
|
|
||||||
|
|
||||||
return r820t_write_reg(priv, reg, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int r820t_read(r820t_priv_t *priv, uint8_t *val, int len)
|
|
||||||
{
|
|
||||||
/* reg not used and assumed to be always 0 because start from reg0 to reg0+len */
|
|
||||||
priv->read_reg(val, len, priv->ctx);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* r820t tuning logic
|
|
||||||
*/
|
|
||||||
#ifdef OPTIM_SET_MUX
|
|
||||||
int r820t_set_mux_freq_idx = -1; /* Default set to invalid value in order to force set_mux */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
"inspired by Mauro Carvalho Chehab set_mux technique"
|
|
||||||
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
|
|
||||||
part of r820t_set_mux() (set tracking filter)
|
|
||||||
*/
|
|
||||||
static int r820t_set_tf(r820t_priv_t *priv, uint32_t freq)
|
|
||||||
{
|
|
||||||
const struct r820t_freq_range *range;
|
|
||||||
int freq_idx;
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
/* Get the proper frequency range in MHz instead of Hz */
|
|
||||||
/* Fast divide freq by 1000000 */
|
|
||||||
freq = (uint32_t)((uint64_t)freq * 4295 >> 32);
|
|
||||||
|
|
||||||
freq_idx = r820t_freq_get_idx(freq);
|
|
||||||
range = &freq_ranges[freq_idx];
|
|
||||||
|
|
||||||
/* Only reconfigure mux freq if modified vs previous range */
|
|
||||||
#ifdef OPTIM_SET_MUX
|
|
||||||
if(freq_idx != r820t_set_mux_freq_idx)
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
/* Open Drain */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* RF_MUX,Polymux */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* TF BAND */
|
|
||||||
rc = r820t_write_reg(priv, 0x1b, range->tf_c);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* XTAL CAP & Drive */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x10, 0x08, 0x0b);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x08, 0x00, 0x3f);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x09, 0x00, 0x3f);
|
|
||||||
#ifdef OPTIM_SET_MUX
|
|
||||||
}
|
|
||||||
r820t_set_mux_freq_idx = freq_idx;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int r820t_set_pll(r820t_priv_t *priv, uint32_t freq)
|
|
||||||
{
|
|
||||||
const uint32_t vco_min = 1770000000;
|
|
||||||
const uint32_t vco_max = 3900000000;
|
|
||||||
uint32_t ref = priv->xtal_freq >> 1;
|
|
||||||
|
|
||||||
int rc;
|
|
||||||
uint32_t div_num;
|
|
||||||
uint32_t vco;
|
|
||||||
uint32_t rem;
|
|
||||||
uint32_t mask;
|
|
||||||
uint16_t sdm;
|
|
||||||
uint8_t nint;
|
|
||||||
uint8_t ni;
|
|
||||||
uint8_t si;
|
|
||||||
uint8_t div_found;
|
|
||||||
|
|
||||||
/* Find a suitable divider */
|
|
||||||
div_found = 0;
|
|
||||||
for (div_num = 0; div_num <= 5; div_num++)
|
|
||||||
{
|
|
||||||
vco = freq << (div_num + 1);
|
|
||||||
if (vco >= vco_min && vco <= vco_max)
|
|
||||||
{
|
|
||||||
div_found = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!div_found)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
vco += ref >> 16;
|
|
||||||
ref <<= 8;
|
|
||||||
mask = 1 << 23;
|
|
||||||
rem = 0;
|
|
||||||
while (mask > 0 && vco > 0)
|
|
||||||
{
|
|
||||||
if (vco >= ref)
|
|
||||||
{
|
|
||||||
rem |= mask;
|
|
||||||
vco -= ref;
|
|
||||||
}
|
|
||||||
ref >>= 1;
|
|
||||||
mask >>= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
nint = rem >> 16;
|
|
||||||
sdm = rem & 0xffff;
|
|
||||||
|
|
||||||
nint -= 13;
|
|
||||||
ni = nint >> 2;
|
|
||||||
si = nint & 3;
|
|
||||||
|
|
||||||
/* Set the phase splitter */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x10, (uint8_t) (div_num << 5), 0xe0);
|
|
||||||
if(rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* Set the integer part of the PLL */
|
|
||||||
rc = r820t_write_reg(priv, 0x14, (uint8_t) (ni + (si << 6)));
|
|
||||||
if(rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
if (sdm == 0)
|
|
||||||
{
|
|
||||||
/* Disable SDM */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x12, 0x08, 0x08);
|
|
||||||
if(rc < 0)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Write SDM */
|
|
||||||
rc = r820t_write_reg(priv, 0x15, (uint8_t)(sdm & 0xff));
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = r820t_write_reg(priv, 0x16, (uint8_t)(sdm >> 8));
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* Enable SDM */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x12, 0x00, 0x08);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int r820t_set_freq(r820t_priv_t *priv, uint32_t freq)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
uint32_t lo_freq = freq + priv->if_freq;
|
|
||||||
|
|
||||||
rc = r820t_set_tf(priv, freq);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = r820t_set_pll(priv, lo_freq);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
priv->freq = freq;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index)
|
|
||||||
{
|
|
||||||
return r820t_write_reg_mask(priv, 0x05, gain_index, 0x0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index)
|
|
||||||
{
|
|
||||||
return r820t_write_reg_mask(priv, 0x07, gain_index, 0x0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index)
|
|
||||||
{
|
|
||||||
return r820t_write_reg_mask(priv, 0x0c, gain_index, 0x0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value)
|
|
||||||
{
|
|
||||||
value = value != 0 ? 0x00 : 0x10;
|
|
||||||
return r820t_write_reg_mask(priv, 0x05, value, 0x10);
|
|
||||||
}
|
|
||||||
|
|
||||||
int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value)
|
|
||||||
{
|
|
||||||
value = value != 0 ? 0x10 : 0x00;
|
|
||||||
return r820t_write_reg_mask(priv, 0x07, value, 0x10);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
"inspired by Mauro Carvalho Chehab calibration technique"
|
|
||||||
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
|
|
||||||
part of r820t_set_tv_standard()
|
|
||||||
*/
|
|
||||||
int r820t_calibrate(r820t_priv_t *priv)
|
|
||||||
{
|
|
||||||
int i, rc, cal_code;
|
|
||||||
uint8_t data[5];
|
|
||||||
|
|
||||||
for (i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
/* Set filt_cap */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x0b, 0x08, 0x60);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* set cali clk =on */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* X'tal cap 0pF for PLL */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
rc = r820t_set_pll(priv, CALIBRATION_LO * 1000);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* Start Trigger */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
||||||
|
|
||||||
/* Stop Trigger */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* set cali clk =off */
|
|
||||||
rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* Check if calibration worked */
|
|
||||||
rc = r820t_read(priv, data, sizeof(data));
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
cal_code = data[4] & 0x0f;
|
|
||||||
if (cal_code && cal_code != 0x0f)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
uint32_t saved_freq;
|
|
||||||
|
|
||||||
r820t_state_standby = 0;
|
|
||||||
priv->if_freq = if_freq;
|
|
||||||
priv->write_reg = write_reg;
|
|
||||||
priv->read_reg = read_reg;
|
|
||||||
priv->ctx = ctx;
|
|
||||||
/* Initialize registers */
|
|
||||||
airspy_r820t_write_init(priv, priv->regs);
|
|
||||||
|
|
||||||
r820t_set_freq(priv, priv->freq);
|
|
||||||
|
|
||||||
/* Calibrate the IF filter */
|
|
||||||
saved_freq = priv->freq;
|
|
||||||
rc = r820t_calibrate(priv);
|
|
||||||
priv->freq = saved_freq;
|
|
||||||
if (rc < 0)
|
|
||||||
{
|
|
||||||
saved_freq = priv->freq;
|
|
||||||
r820t_calibrate(priv);
|
|
||||||
priv->freq = saved_freq;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restore freq as it has been modified by r820t_calibrate() */
|
|
||||||
rc = r820t_set_freq(priv, priv->freq);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw)
|
|
||||||
{
|
|
||||||
const uint8_t modes[] = { 0xE0, 0x80, 0x60, 0x00 };
|
|
||||||
const uint8_t opt[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
|
|
||||||
uint8_t a = 0xB0 | opt[bw & 0x0F];
|
|
||||||
uint8_t b = 0x0F | modes[bw >> 4];
|
|
||||||
r820t_write_reg(priv, 0x0A, a);
|
|
||||||
r820t_write_reg(priv, 0x0B, b);
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2016 Benjamin Vernoux <bvernoux@airspy.com>
|
|
||||||
*
|
|
||||||
* This file is part of AirSpy.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2, or (at your option)
|
|
||||||
* any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
|
||||||
* Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define REG_SHADOW_START 5
|
|
||||||
#define NUM_REGS 30
|
|
||||||
|
|
||||||
/* R820T Clock */
|
|
||||||
#define CALIBRATION_LO 88000
|
|
||||||
|
|
||||||
typedef void (*r820t_write_reg_f)(uint8_t reg, uint8_t value, void* ctx);
|
|
||||||
typedef void (*r820t_read_f)(uint8_t* data, int len, void* ctx);
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint32_t xtal_freq; /* XTAL_FREQ_HZ */
|
|
||||||
uint32_t freq;
|
|
||||||
uint32_t if_freq;
|
|
||||||
uint8_t regs[NUM_REGS];
|
|
||||||
uint16_t padding;
|
|
||||||
|
|
||||||
r820t_write_reg_f write_reg;
|
|
||||||
r820t_read_f read_reg;
|
|
||||||
void* ctx;
|
|
||||||
|
|
||||||
} r820t_priv_t;
|
|
||||||
|
|
||||||
void airspy_r820t_write_single(r820t_priv_t *priv, uint8_t reg, uint8_t val);
|
|
||||||
uint8_t airspy_r820t_read_single(r820t_priv_t *priv, uint8_t reg);
|
|
||||||
|
|
||||||
int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx);
|
|
||||||
int r820t_set_freq(r820t_priv_t *priv, uint32_t freq);
|
|
||||||
int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index);
|
|
||||||
int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index);
|
|
||||||
int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index);
|
|
||||||
int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value);
|
|
||||||
int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value);
|
|
||||||
void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw);
|
|
||||||
@@ -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;
|
||||||
|
|||||||
28
source_modules/dragonlabs_source/CMakeLists.txt
Normal file
28
source_modules/dragonlabs_source/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(dragonlabs_source)
|
||||||
|
|
||||||
|
file(GLOB SRC "src/*.cpp")
|
||||||
|
|
||||||
|
include(${SDRPP_MODULE_CMAKE})
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Debugging only
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
|
||||||
|
target_link_libraries(dragonlabs_source PRIVATE PkgConfig::libusb)
|
||||||
|
target_include_directories(dragonlabs_source PRIVATE "C:/Users/ryzerth/Documents/DragonLabs/products/KrakenSlayer/host/src")
|
||||||
|
|
||||||
|
target_include_directories(dragonlabs_source PRIVATE "C:/Program Files/DragonLabs/CR/include")
|
||||||
|
target_link_directories(dragonlabs_source PRIVATE "C:/Program Files/DragonLabs/CR/lib")
|
||||||
|
target_link_libraries(dragonlabs_source PRIVATE dlcr)
|
||||||
|
elseif (ANDROID)
|
||||||
|
# Not supported yet...
|
||||||
|
else (MSVC)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
|
||||||
|
pkg_check_modules(LIBDLCR REQUIRED libdlcr)
|
||||||
|
|
||||||
|
target_include_directories(dragonlabs_source PRIVATE ${LIBDLCR_INCLUDE_DIRS})
|
||||||
|
target_link_directories(dragonlabs_source PRIVATE ${LIBDLCR_LIBRARY_DIRS})
|
||||||
|
target_link_libraries(dragonlabs_source PRIVATE ${LIBDLCR_LIBRARIES})
|
||||||
|
endif ()
|
||||||
453
source_modules/dragonlabs_source/src/main.cpp
Normal file
453
source_modules/dragonlabs_source/src/main.cpp
Normal file
@@ -0,0 +1,453 @@
|
|||||||
|
#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 <fstream>
|
||||||
|
#include <dlcr.h>
|
||||||
|
|
||||||
|
#ifdef DRAGONLABS_SOURCE_ENABLE_DEBUG
|
||||||
|
#include <dlcr_internal.h>
|
||||||
|
#include <lmx2572.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SDRPP_MOD_INFO{
|
||||||
|
/* Name: */ "dragonlabs_source",
|
||||||
|
/* Description: */ "Dragon Labs Source Module",
|
||||||
|
/* Author: */ "Ryzerth",
|
||||||
|
/* Version: */ 0, 1, 0,
|
||||||
|
/* Max instances */ -1
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
class DragonLabsSourceModule : public ModuleManager::Instance {
|
||||||
|
public:
|
||||||
|
DragonLabsSourceModule(std::string name) {
|
||||||
|
this->name = name;
|
||||||
|
|
||||||
|
// Load the register debugging values
|
||||||
|
strcpy(clkRegStr, "00");
|
||||||
|
strcpy(clkValStr, "--");
|
||||||
|
strcpy(synRegStr, "00");
|
||||||
|
strcpy(synValStr, "--");
|
||||||
|
strcpy(adcRegStr, "00");
|
||||||
|
strcpy(adcValStr, "--");
|
||||||
|
strcpy(tunRegStr, "00");
|
||||||
|
strcpy(tunValStr, "--");
|
||||||
|
|
||||||
|
// Define the clock sources
|
||||||
|
clockSources.define("internal", "Internal", DLCR_CLOCK_INTERNAL);
|
||||||
|
clockSources.define("external", "External", DLCR_CLOCK_EXTERNAL);
|
||||||
|
|
||||||
|
// Define the channels
|
||||||
|
channels.define("chan1", "Channel 1", 0);
|
||||||
|
channels.define("chan2", "Channel 2", 1);
|
||||||
|
channels.define("chan3", "Channel 3", 2);
|
||||||
|
channels.define("chan4", "Channel 4", 3);
|
||||||
|
channels.define("chan5", "Channel 5", 4);
|
||||||
|
channels.define("chan6", "Channel 6", 5);
|
||||||
|
channels.define("chan7", "Channel 7", 6);
|
||||||
|
channels.define("chan8", "Channel 8", 7);
|
||||||
|
|
||||||
|
// Hardcode the samplerate
|
||||||
|
sampleRate = 12.5e6;
|
||||||
|
|
||||||
|
// Fill out the source handler
|
||||||
|
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("Dragon Labs", &handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
~DragonLabsSourceModule() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void postInit() {}
|
||||||
|
|
||||||
|
void enable() {
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void refresh() {
|
||||||
|
devices.clear();
|
||||||
|
|
||||||
|
// Get the list of devices
|
||||||
|
dlcr_info_t* list;
|
||||||
|
int count = dlcr_list_devices(&list);
|
||||||
|
if (count < 0) {
|
||||||
|
flog::error("Failed to list devices");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the menu list
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
// Format device name
|
||||||
|
std::string devName = "CR8-1725 ";
|
||||||
|
devName += " [";
|
||||||
|
devName += list[i].serial;
|
||||||
|
devName += ']';
|
||||||
|
|
||||||
|
// Save device
|
||||||
|
devices.define(list[i].serial, devName, list[i].serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the device list
|
||||||
|
dlcr_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save serial number
|
||||||
|
selectedSerial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuSelected(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Menu Select!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void menuDeselected(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Menu Deselect!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::high_resolution_clock::time_point last;
|
||||||
|
|
||||||
|
static void start(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
if (_this->running) { return; }
|
||||||
|
|
||||||
|
// Open the device
|
||||||
|
int err = dlcr_open(&_this->openDev, _this->selectedSerial.c_str());
|
||||||
|
if (err) {
|
||||||
|
flog::error("Failed to open device '{}': {}", _this->selectedSerial, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flog::debug("Device open");
|
||||||
|
|
||||||
|
// Configure the device
|
||||||
|
dlcr_set_freq(_this->openDev, DLCR_CHAN_ALL, _this->freq, true);
|
||||||
|
dlcr_set_lna_gain(_this->openDev, DLCR_CHAN_ALL, _this->lnaGain);
|
||||||
|
dlcr_set_mixer_gain(_this->openDev, DLCR_CHAN_ALL, _this->mixerGain);
|
||||||
|
dlcr_set_vga_gain(_this->openDev, DLCR_CHAN_ALL, _this->vgaGain);
|
||||||
|
|
||||||
|
// Start device
|
||||||
|
dlcr_start(_this->openDev, 0/*TODO*/, callback, _this);
|
||||||
|
|
||||||
|
_this->running = true;
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Start!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
if (!_this->running) { return; }
|
||||||
|
_this->running = false;
|
||||||
|
|
||||||
|
// Stop device
|
||||||
|
dlcr_stop(_this->openDev);
|
||||||
|
|
||||||
|
// Close device
|
||||||
|
dlcr_close(_this->openDev);
|
||||||
|
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Stop!", _this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tune(double freq, void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_freq(_this->openDev, DLCR_CHAN_ALL, freq, _this->docal);
|
||||||
|
}
|
||||||
|
_this->calibrated = false;
|
||||||
|
_this->freq = freq;
|
||||||
|
flog::info("DragonLabsSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
double lmxFreq = 100e6;
|
||||||
|
|
||||||
|
static void menuHandler(void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::BeginDisabled(); }
|
||||||
|
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Combo(CONCAT("##_dlcr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
||||||
|
_this->select(_this->devices.key(_this->devId));
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::ForceSync();
|
||||||
|
if (SmGui::Button(CONCAT("Refresh##_dlcr_refr_", _this->name))) {
|
||||||
|
_this->refresh();
|
||||||
|
_this->select(_this->selectedSerial);
|
||||||
|
core::setInputSampleRate(_this->sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Clock Source");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Combo(CONCAT("##_dlcr_clock_", _this->name), &_this->clockSourceId, _this->clockSources.txt)) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_clock_source(_this->openDev, _this->clockSources[_this->clockSourceId]);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Channel");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::Combo(CONCAT("##_dlcr_chan_", _this->name), &_this->channelId, _this->channels.txt);
|
||||||
|
|
||||||
|
SmGui::LeftLabel("LNA Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_dlcr_lna_gain_", _this->name), &_this->lnaGain, 0, 14)) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_lna_gain(_this->openDev, DLCR_CHAN_ALL, _this->lnaGain);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Mixer Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_dlcr_mixer_gain_", _this->name), &_this->mixerGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_mixer_gain(_this->openDev, DLCR_CHAN_ALL, _this->mixerGain);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
SmGui::LeftLabel("VGA Gain");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::SliderInt(CONCAT("##_dlcr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_set_vga_gain(_this->openDev, DLCR_CHAN_ALL, _this->vgaGain);
|
||||||
|
}
|
||||||
|
// TODO: Save
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DRAGONLABS_SOURCE_ENABLE_DEBUG
|
||||||
|
SmGui::Checkbox(CONCAT("Debug##_dlcr_debug_", _this->name), &_this->debug);
|
||||||
|
|
||||||
|
if (_this->debug) {
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Clockgen Reg");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_clk_reg_", _this->name), _this->clkRegStr, 256);
|
||||||
|
SmGui::LeftLabel("Clockgen Value");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_clk_val_", _this->name), _this->clkValStr, 256);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Read##_dlcr_clk_rd_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
uint8_t val;
|
||||||
|
dlcr_si5351c_read_reg(_this->openDev, std::stoi(_this->clkRegStr, NULL, 16), &val);
|
||||||
|
sprintf(_this->clkValStr, "%02X", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Write##_dlcr_clk_wr_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_si5351c_write_reg(_this->openDev, std::stoi(_this->clkRegStr, NULL, 16), std::stoi(_this->clkValStr, NULL, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Synth Reg");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_syn_reg_", _this->name), _this->synRegStr, 256);
|
||||||
|
SmGui::LeftLabel("Synth Value");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_syn_val_", _this->name), _this->synValStr, 256);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Read##_dlcr_syn_rd_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
uint16_t val;
|
||||||
|
dlcr_lmx2572_read_reg(_this->openDev, std::stoi(_this->synRegStr, NULL, 16), &val);
|
||||||
|
sprintf(_this->synValStr, "%02X", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Write##_dlcr_syn_wr_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_lmx2572_write_reg(_this->openDev, std::stoi(_this->synRegStr, NULL, 16), std::stoi(_this->synValStr, NULL, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("ADC Reg");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_adc_reg_", _this->name), _this->adcRegStr, 256);
|
||||||
|
SmGui::LeftLabel("ADC Value");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_adc_val_", _this->name), _this->adcValStr, 256);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Read##_dlcr_adc_rd_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
uint8_t val;
|
||||||
|
dlcr_mcp37211_read_reg(_this->openDev, std::stoi(_this->adcRegStr, NULL, 16), &val);
|
||||||
|
sprintf(_this->adcValStr, "%02X", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Write##_dlcr_adc_wr_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_mcp37211_write_reg(_this->openDev, std::stoi(_this->adcRegStr, NULL, 16), std::stoi(_this->adcValStr, NULL, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Tuner Reg");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_tun_reg_", _this->name), _this->tunRegStr, 256);
|
||||||
|
SmGui::LeftLabel("Tuner Value");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
SmGui::InputText(CONCAT("##_dlcr_tun_val_", _this->name), _this->tunValStr, 256);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Read##_dlcr_tun_rd_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
uint8_t val[8];
|
||||||
|
dlcr_r860_read_reg(_this->openDev, DLCR_TUNER_ALL, std::stoi(_this->tunRegStr, NULL, 16), val);
|
||||||
|
sprintf(_this->tunValStr, "%02X", val[0]);
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
printf("TUNER[%d][0x%02X] = 0x%02X\n", i, std::stoi(_this->tunRegStr, NULL, 16), val[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Write##_dlcr_tun_wr_", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
dlcr_r860_write_reg(_this->openDev, DLCR_TUNER_ALL, std::stoi(_this->tunRegStr, NULL, 16), std::stoi(_this->tunValStr, NULL, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::LeftLabel("Synth Freq");
|
||||||
|
SmGui::FillWidth();
|
||||||
|
ImGui::InputDouble("##_dlcr_synth_freq", &_this->synthFreq);
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Tune##_dlcr_synth_freq", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
lmx2572_tune(_this->openDev, _this->synthFreq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmGui::FillWidth();
|
||||||
|
if (SmGui::Button(CONCAT("Shutdown##_dlcr_synth_freq", _this->name))) {
|
||||||
|
if (_this->running) {
|
||||||
|
lmx2572_shutdown(_this->openDev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
SmGui::Checkbox("Calibrate##_dlcr_cal", &_this->docal);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void callback(dlcr_complex_t* samples[DLCR_CHANNEL_COUNT], size_t count, size_t drops, void* ctx) {
|
||||||
|
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
|
||||||
|
|
||||||
|
// Copy the data to the stream
|
||||||
|
memcpy(_this->stream.writeBuf, samples[_this->channelId], count * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
|
// Send the samples
|
||||||
|
_this->stream.swap(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
bool enabled = true;
|
||||||
|
double sampleRate;
|
||||||
|
SourceManager::SourceHandler handler;
|
||||||
|
bool running = false;
|
||||||
|
double freq = 100e6;
|
||||||
|
|
||||||
|
OptionList<std::string, std::string> devices;
|
||||||
|
|
||||||
|
bool debug = false;
|
||||||
|
char clkRegStr[256];
|
||||||
|
char clkValStr[256];
|
||||||
|
char synRegStr[256];
|
||||||
|
char synValStr[256];
|
||||||
|
char adcRegStr[256];
|
||||||
|
char adcValStr[256];
|
||||||
|
char tunRegStr[256];
|
||||||
|
char tunValStr[256];
|
||||||
|
double synthFreq = 100e6;
|
||||||
|
bool docal = false;
|
||||||
|
int devId = 0;
|
||||||
|
int clockSourceId = 0;
|
||||||
|
int channelId = 0;
|
||||||
|
int lnaGain = 0;
|
||||||
|
int mixerGain = 0;
|
||||||
|
int vgaGain = 0;
|
||||||
|
std::string selectedSerial;
|
||||||
|
dlcr_t* openDev = NULL;
|
||||||
|
|
||||||
|
bool calibrated = false;
|
||||||
|
|
||||||
|
dsp::stream<dsp::complex_t> stream;
|
||||||
|
OptionList<std::string, dlcr_clock_t> clockSources;
|
||||||
|
OptionList<std::string, int> channels;
|
||||||
|
};
|
||||||
|
|
||||||
|
MOD_EXPORT void _INIT_() {
|
||||||
|
// Nothing here
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||||
|
return new DragonLabsSourceModule(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||||
|
delete (DragonLabsSourceModule*)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/hydrasdr-host/lib/")
|
||||||
|
|
||||||
|
target_include_directories(hydrasdr_source PUBLIC "C:/Program Files/hydrasdr-host/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", HYDRASDR_RF_PORT_RX0);
|
||||||
|
ports.define("rx1", "RX1", HYDRASDR_RF_PORT_RX1);
|
||||||
|
ports.define("rx2", "RX2", HYDRASDR_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,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/
|
||||||
87
source_modules/sddc_source/libsddc/CMakeLists.txt
Normal file
87
source_modules/sddc_source/libsddc/CMakeLists.txt
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
project(libsddc VERSION 0.2.0)
|
||||||
|
|
||||||
|
# Options
|
||||||
|
option(BUILD_SDDC_UTILS "Build SDDC utilities such as sddc_info" ON)
|
||||||
|
option(INSTALL_UDEV_RULES "Install UDEV rules (Linux only)" ON)
|
||||||
|
|
||||||
|
# List all source files
|
||||||
|
file(GLOB_RECURSE SRC "src/*.c")
|
||||||
|
|
||||||
|
# On windows, all symbols must be exported
|
||||||
|
if (MSVC)
|
||||||
|
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Create dynamic libs
|
||||||
|
add_library(sddc SHARED ${SRC})
|
||||||
|
|
||||||
|
# # Set optimisation flags
|
||||||
|
# if (${CMAKE_BUILD_TYPE} MATCHES "Debug")
|
||||||
|
# # Debug Flags
|
||||||
|
# if (MSVC)
|
||||||
|
# target_compile_options(sddc PRIVATE /EHsc)
|
||||||
|
# else ()
|
||||||
|
# target_compile_options(sddc PRIVATE -g -Og)
|
||||||
|
# endif ()
|
||||||
|
# else()
|
||||||
|
# # Normal Flags
|
||||||
|
# if (MSVC)
|
||||||
|
# target_compile_options(sddc PRIVATE /O2 /Ob2 /EHsc)
|
||||||
|
# else ()
|
||||||
|
# target_compile_options(sddc PRIVATE -O3)
|
||||||
|
# endif ()
|
||||||
|
# endif()
|
||||||
|
|
||||||
|
# Include the include folder
|
||||||
|
target_include_directories(sddc PUBLIC "include/")
|
||||||
|
|
||||||
|
# Find libusb
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
|
||||||
|
|
||||||
|
# Link to libusb
|
||||||
|
target_link_libraries(sddc PRIVATE PkgConfig::libusb)
|
||||||
|
|
||||||
|
# TODO: Have it default instead of override
|
||||||
|
if (MSVC)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "C:/Program Files/SDDC/")
|
||||||
|
set(CMAKE_INSTALL_BINDIR "bin")
|
||||||
|
set(CMAKE_INSTALL_LIBDIR "lib")
|
||||||
|
set(CMAKE_INSTALL_INCLUDEDIR "include")
|
||||||
|
else ()
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (NOT MSVC)
|
||||||
|
# Configure pkgconfig file
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/libsddc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libsddc.pc @ONLY)
|
||||||
|
|
||||||
|
# Install pkgconfig file
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libsddc.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Install the library
|
||||||
|
if (MSVC)
|
||||||
|
install(TARGETS sddc)
|
||||||
|
else ()
|
||||||
|
install(TARGETS sddc DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Install the headers
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
|
|
||||||
|
# On Windows, install dependencies
|
||||||
|
if (MSVC)
|
||||||
|
install(FILES $<TARGET_FILE_DIR:sddc>/libusb-1.0.dll DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Build utils if enabled
|
||||||
|
if (BUILD_SDDC_UTILS)
|
||||||
|
add_subdirectory("utils/sddc_info")
|
||||||
|
add_subdirectory("utils/sddc_rx")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# # Create uninstall target
|
||||||
|
# configure_file(${CMAKE_SOURCE_DIR}/cmake/uninstall.cmake ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake @ONLY)
|
||||||
|
# add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake)
|
||||||
32
source_modules/sddc_source/libsddc/cmake/uninstall.cmake
Normal file
32
source_modules/sddc_source/libsddc/cmake/uninstall.cmake
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F
|
||||||
|
|
||||||
|
IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
|
||||||
|
ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
|
||||||
|
FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||||
|
STRING(REGEX REPLACE "\n" ";" files "${files}")
|
||||||
|
FOREACH(file ${files})
|
||||||
|
MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
|
||||||
|
IF(EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
EXEC_PROGRAM(
|
||||||
|
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||||
|
OUTPUT_VARIABLE rm_out
|
||||||
|
RETURN_VALUE rm_retval
|
||||||
|
)
|
||||||
|
IF(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
|
||||||
|
ENDIF(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}")
|
||||||
|
EXEC_PROGRAM(
|
||||||
|
"@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
|
||||||
|
OUTPUT_VARIABLE rm_out
|
||||||
|
RETURN_VALUE rm_retval
|
||||||
|
)
|
||||||
|
IF(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
|
||||||
|
ENDIF(NOT "${rm_retval}" STREQUAL 0)
|
||||||
|
ELSE(EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
|
||||||
|
ENDIF(EXISTS "$ENV{DESTDIR}${file}")
|
||||||
|
ENDFOREACH(file)
|
||||||
177
source_modules/sddc_source/libsddc/include/sddc.h
Normal file
177
source_modules/sddc_source/libsddc/include/sddc.h
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// Handle inclusion from C++ code
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SDDC_SERIAL_MAX_LEN 256
|
||||||
|
|
||||||
|
enum sddc_model {
|
||||||
|
SDDC_MODEL_UNKNOWN = 0x00,
|
||||||
|
SDDC_MODEL_BBRF103 = 0x01,
|
||||||
|
SDDC_MODEL_HF103 = 0x02,
|
||||||
|
SDDC_MODEL_RX888 = 0x03,
|
||||||
|
SDDC_MODEL_RX888_MK2 = 0x04,
|
||||||
|
SDDC_MODEL_RX999 = 0x05,
|
||||||
|
SDDC_MODEL_RXLUCY = 0x06,
|
||||||
|
SDDC_MODEL_RX888_MK3 = 0x07
|
||||||
|
};
|
||||||
|
typedef enum sddc_model sddc_model_t;
|
||||||
|
|
||||||
|
enum sddc_error {
|
||||||
|
SDDC_ERROR_UNKNOWN = -99,
|
||||||
|
SDDC_ERROR_NOT_IMPLEMENTED = -98,
|
||||||
|
|
||||||
|
SDDC_ERROR_FIRMWARE_UPLOAD_FAILED = -4,
|
||||||
|
SDDC_ERROR_NOT_FOUND = -3,
|
||||||
|
SDDC_ERROR_USB_ERROR = -2,
|
||||||
|
SDDC_ERROR_TIMEOUT = -1,
|
||||||
|
|
||||||
|
SDDC_SUCCESS = 0
|
||||||
|
};
|
||||||
|
typedef enum sddc_error sddc_error_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device instance.
|
||||||
|
*/
|
||||||
|
struct sddc_dev;
|
||||||
|
typedef struct sddc_dev sddc_dev_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Device information.
|
||||||
|
*/
|
||||||
|
struct sddc_devinfo {
|
||||||
|
const char serial[SDDC_SERIAL_MAX_LEN];
|
||||||
|
sddc_model_t model;
|
||||||
|
int firmwareMajor;
|
||||||
|
int firmwareMinor;
|
||||||
|
};
|
||||||
|
typedef struct sddc_devinfo sddc_devinfo_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter range. A step size of zero means infinitely variable.
|
||||||
|
*/
|
||||||
|
struct sddc_range {
|
||||||
|
double start;
|
||||||
|
double end;
|
||||||
|
double step;
|
||||||
|
};
|
||||||
|
typedef struct sddc_range sddc_range_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the string representation of a device model.
|
||||||
|
* @param model Model to get the string representation of.
|
||||||
|
* @return String representation of the model.
|
||||||
|
*/
|
||||||
|
const char* sddc_model_to_string(sddc_model_t model);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the string representation of an error.
|
||||||
|
* @param model Error to get the string representation of.
|
||||||
|
* @return String representation of the error.
|
||||||
|
*/
|
||||||
|
const char* sddc_error_to_string(sddc_error_t error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the path to the firmware image.
|
||||||
|
* @param path Path to the firmware image.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_set_firmware_path(const char* path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of connected devices. The returned list has to be freed using `sddc_free_device_list()` if it isn't empty.
|
||||||
|
* @param dev_list Pointer to a list of devices.
|
||||||
|
* @return Number of devices in the list or an error code if an error occured.
|
||||||
|
*/
|
||||||
|
int sddc_get_device_list(sddc_devinfo_t** dev_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a device list returned by `sddc_get_device_list()`. Attempting to free a list returned empty has no effect.
|
||||||
|
* @param dev_list Device list to free.
|
||||||
|
*/
|
||||||
|
void sddc_free_device_list(sddc_devinfo_t* dev_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a device by its serial number.
|
||||||
|
* @param serial Serial number of the device to open.
|
||||||
|
* @param dev Pointer to a SDDC device pointer to populate once open.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_open(const char* serial, sddc_dev_t** dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close an opened SDDC device.
|
||||||
|
* @param dev SDDC Device to close.
|
||||||
|
*/
|
||||||
|
void sddc_close(sddc_dev_t* dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the range of samplerate supported by a device.
|
||||||
|
* @param dev SDDC device.
|
||||||
|
* @return Range of supported samplerates.
|
||||||
|
*/
|
||||||
|
sddc_range_t sddc_get_samplerate_range(sddc_dev_t* dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the device's sampling rate.
|
||||||
|
* @param dev SDDC device.
|
||||||
|
* @param samplerate Sampling rate.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_set_samplerate(sddc_dev_t* dev, uint32_t samplerate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the ADC's dithering feature.
|
||||||
|
* @param dev SDDC device.
|
||||||
|
* @param enabled True to enable dithering, false to disable it.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_set_dithering(sddc_dev_t* dev, bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the ADC's randomizer feature.
|
||||||
|
* @param dev SDDC device.
|
||||||
|
* @param enabled True to enable randomization, false to disable it.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_set_randomizer(sddc_dev_t* dev, bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the LO of the tuner.
|
||||||
|
* @param dev SDDC device.
|
||||||
|
* @param frequency Frequency of the LO.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_set_tuner_frequency(sddc_dev_t* dev, uint64_t frequency);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the device.
|
||||||
|
* @param dev SDDC device.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_start(sddc_dev_t* dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the device.
|
||||||
|
* @param dev SDDC device.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_stop(sddc_dev_t* dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive samples.
|
||||||
|
* @param dev SDDC device.
|
||||||
|
* @param samples Buffer to write the samples to.
|
||||||
|
* @param count Number of samples to read.
|
||||||
|
* @return SDDC_SUCCESS on success or an error code otherwise.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_rx(sddc_dev_t* dev, int16_t* samples, int count);
|
||||||
|
|
||||||
|
// Handle inclusion from C++ code
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
165
source_modules/sddc_source/libsddc/license
Normal file
165
source_modules/sddc_source/libsddc/license
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
116
source_modules/sddc_source/libsddc/src/fx3_boot.c
Normal file
116
source_modules/sddc_source/libsddc/src/fx3_boot.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include "fx3_boot.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define FX3_TIMEOUT 1000
|
||||||
|
#define FX3_VENDOR_REQUEST 0xA0
|
||||||
|
#define FX3_MAX_BLOCK_SIZE 0x1000
|
||||||
|
|
||||||
|
int sddc_fx3_boot_mem_read(libusb_device_handle* dev, uint32_t addr, uint8_t* data, uint16_t len) {
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, addr & 0xFFFF, addr >> 16, data, len, FX3_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_boot_mem_write(libusb_device_handle* dev, uint32_t addr, const uint8_t* data, uint16_t len) {
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, addr & 0xFFFF, addr >> 16, data, len, FX3_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_boot_run(libusb_device_handle* dev, uint32_t entry) {
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, FX3_VENDOR_REQUEST, entry & 0xFFFF, entry >> 16, NULL, 0, FX3_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_boot_upload_firmware(libusb_device_handle* dev, const char* path) {
|
||||||
|
// Open the firmware image
|
||||||
|
FILE* fw = fopen(path, "rb");
|
||||||
|
if (!fw) {
|
||||||
|
fprintf(stderr, "Failed to open firmware image\n");
|
||||||
|
return LIBUSB_ERROR_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the signature
|
||||||
|
char sign[2];
|
||||||
|
int read = fread(sign, 2, 1, fw);
|
||||||
|
if (read != 1) {
|
||||||
|
fprintf(stderr, "Failed to read firmware image signature: %d\n", read);
|
||||||
|
fclose(fw);
|
||||||
|
return LIBUSB_ERROR_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the signature
|
||||||
|
if (sign[0] != 'C' || sign[1] != 'Y') {
|
||||||
|
fprintf(stderr, "Firmware image has invalid signature\n");
|
||||||
|
fclose(fw);
|
||||||
|
return LIBUSB_ERROR_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip useless metadata
|
||||||
|
int err = fseek(fw, 2, SEEK_CUR);
|
||||||
|
if (err) {
|
||||||
|
fprintf(stderr, "Invalid firmware image: %d\n", err);
|
||||||
|
fclose(fw);
|
||||||
|
return LIBUSB_ERROR_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preallocate data buffer
|
||||||
|
int bufferSize = 0x10000;
|
||||||
|
uint8_t* buffer = malloc(bufferSize);
|
||||||
|
|
||||||
|
// Read every section
|
||||||
|
while (1) {
|
||||||
|
// Read the section size
|
||||||
|
uint32_t sizeWords;
|
||||||
|
read = fread(&sizeWords, sizeof(uint32_t), 1, fw);
|
||||||
|
if (read != 1) {
|
||||||
|
fprintf(stderr, "Invalid firmware image section size\n");
|
||||||
|
fclose(fw);
|
||||||
|
return LIBUSB_ERROR_OTHER;
|
||||||
|
}
|
||||||
|
uint32_t size = sizeWords << 2;
|
||||||
|
|
||||||
|
// Read the section address
|
||||||
|
uint32_t addr;
|
||||||
|
read = fread(&addr, sizeof(uint32_t), 1, fw);
|
||||||
|
if (read != 1) {
|
||||||
|
fprintf(stderr, "Invalid firmware image section address\n");
|
||||||
|
fclose(fw);
|
||||||
|
return LIBUSB_ERROR_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the section is a termination section, run the code at the given address
|
||||||
|
if (!size) {
|
||||||
|
sddc_fx3_boot_run(dev, addr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-allocate buffer if needed
|
||||||
|
if (size > bufferSize) {
|
||||||
|
bufferSize = size;
|
||||||
|
realloc(buffer, bufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the section data
|
||||||
|
read = fread(buffer, 1, size, fw);
|
||||||
|
if (read != size) {
|
||||||
|
fprintf(stderr, "Failed to read section data: %d\n", read);
|
||||||
|
fclose(fw);
|
||||||
|
return LIBUSB_ERROR_OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload it to the chip
|
||||||
|
for (int i = 0; i < size; i += FX3_MAX_BLOCK_SIZE) {
|
||||||
|
int left = size - i;
|
||||||
|
err = sddc_fx3_boot_mem_write(dev, addr + i, &buffer[i], (left > FX3_MAX_BLOCK_SIZE) ? FX3_MAX_BLOCK_SIZE : left);
|
||||||
|
if (err < LIBUSB_SUCCESS) {
|
||||||
|
fprintf(stderr, "Failed to write to device memory: %d\n", err);
|
||||||
|
fclose(fw);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Checksum stuff and verification ideally
|
||||||
|
|
||||||
|
// Close the firmware image
|
||||||
|
fclose(fw);
|
||||||
|
|
||||||
|
// Return successfully
|
||||||
|
return LIBUSB_SUCCESS;
|
||||||
|
}
|
||||||
39
source_modules/sddc_source/libsddc/src/fx3_boot.h
Normal file
39
source_modules/sddc_source/libsddc/src/fx3_boot.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <libusb.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the device's memory.
|
||||||
|
* @param dev Device to read data from.
|
||||||
|
* @param addr Start address of the data in the device's memory.
|
||||||
|
* @param data Buffer to write the data into.
|
||||||
|
* @param len Number of bytes to read.
|
||||||
|
* @return libusb error code.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_boot_mem_read(libusb_device_handle* dev, uint32_t addr, uint8_t* data, uint16_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the device's memory.
|
||||||
|
* @param dev Device to write data to.
|
||||||
|
* @param addr Start address of the data in the device's memory.
|
||||||
|
* @param data Buffer to write the data into.
|
||||||
|
* @param len Number of bytes to read.
|
||||||
|
* @return libusb error code.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_boot_mem_write(libusb_device_handle* dev, uint32_t addr, const uint8_t* data, uint16_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute code on the device.
|
||||||
|
* @param dev Device to execute code on.
|
||||||
|
* @param entry Entry point of the code.
|
||||||
|
* @return libusb error code.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_boot_run(libusb_device_handle* dev, uint32_t entry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse, upload and execute a firmware image.
|
||||||
|
* @param dev Device to upload the firmware to.
|
||||||
|
* @param path Path to the firmware image.
|
||||||
|
* @return libusb error code.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_boot_upload_firmware(libusb_device_handle* dev, const char* path);
|
||||||
451
source_modules/sddc_source/libsddc/src/sddc.c
Normal file
451
source_modules/sddc_source/libsddc/src/sddc.c
Normal file
@@ -0,0 +1,451 @@
|
|||||||
|
#include <sddc.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <libusb.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "usb_interface.h"
|
||||||
|
|
||||||
|
struct sddc_dev {
|
||||||
|
// USB handles
|
||||||
|
struct libusb_device_handle* openDev;
|
||||||
|
|
||||||
|
// Device info
|
||||||
|
sddc_devinfo_t info;
|
||||||
|
|
||||||
|
// Device state
|
||||||
|
bool running;
|
||||||
|
uint32_t samplerate;
|
||||||
|
uint64_t tunerFreq;
|
||||||
|
sddc_gpio_t gpioState;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct libusb_context* ctx = NULL;
|
||||||
|
char* sddc_firmware_path = NULL;
|
||||||
|
bool sddc_is_init = false;
|
||||||
|
|
||||||
|
void sddc_init() {
|
||||||
|
// If already initialized, do nothing
|
||||||
|
if (sddc_is_init) { return; }
|
||||||
|
|
||||||
|
// If the firmware isn't already found, find it
|
||||||
|
if (!sddc_firmware_path) {
|
||||||
|
// TODO: Find the firmware
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init libusb
|
||||||
|
libusb_init(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* sddc_model_to_string(sddc_model_t model) {
|
||||||
|
switch (model) {
|
||||||
|
case SDDC_MODEL_BBRF103: return "BBRF103";
|
||||||
|
case SDDC_MODEL_HF103: return "HF103";
|
||||||
|
case SDDC_MODEL_RX888: return "RX888";
|
||||||
|
case SDDC_MODEL_RX888_MK2: return "RX888 MK2";
|
||||||
|
case SDDC_MODEL_RX999: return "RX999";
|
||||||
|
case SDDC_MODEL_RXLUCY: return "RXLUCY";
|
||||||
|
case SDDC_MODEL_RX888_MK3: return "RX888 MK3";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* sddc_error_to_string(sddc_error_t error) {
|
||||||
|
switch (error) {
|
||||||
|
case SDDC_ERROR_NOT_IMPLEMENTED: return "Not Implemented";
|
||||||
|
case SDDC_ERROR_FIRMWARE_UPLOAD_FAILED: return "Firmware Upload Failed";
|
||||||
|
case SDDC_ERROR_NOT_FOUND: return "Not Found";
|
||||||
|
case SDDC_ERROR_USB_ERROR: return "USB Error";
|
||||||
|
case SDDC_ERROR_TIMEOUT: return "Timeout";
|
||||||
|
case SDDC_SUCCESS: return "Success";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_set_firmware_path(const char* path) {
|
||||||
|
// Free the old path if it exists
|
||||||
|
if (sddc_firmware_path) { free(sddc_firmware_path); }
|
||||||
|
|
||||||
|
// Allocate the new path
|
||||||
|
sddc_firmware_path = malloc(strlen(path) + 1);
|
||||||
|
|
||||||
|
// Copy the new path
|
||||||
|
strcpy(sddc_firmware_path, path);
|
||||||
|
|
||||||
|
// TODO: Check if the file path exists
|
||||||
|
return SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_get_device_list(sddc_devinfo_t** dev_list) {
|
||||||
|
// Initialize libsddc in case it isn't already
|
||||||
|
sddc_init();
|
||||||
|
|
||||||
|
// Get a list of USB devices
|
||||||
|
libusb_device** devices;
|
||||||
|
int devCount = libusb_get_device_list(ctx, &devices);
|
||||||
|
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Initialize all uninitialized devices
|
||||||
|
bool uninit = false;
|
||||||
|
for (int i = 0; i < devCount; i++) {
|
||||||
|
// Get the device from the list
|
||||||
|
libusb_device* dev = devices[i];
|
||||||
|
|
||||||
|
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
int err = libusb_get_device_descriptor(dev, &desc);
|
||||||
|
if (err != LIBUSB_SUCCESS) { continue; }
|
||||||
|
|
||||||
|
// If it's not an uninitialized device, go to next device
|
||||||
|
if (desc.idVendor != SDDC_UNINIT_VID || desc.idProduct != SDDC_UNINIT_PID) { continue; }
|
||||||
|
|
||||||
|
// Initialize the device
|
||||||
|
printf("Found uninitialized device, initializing...\n");
|
||||||
|
// TODO: Check that the firmware path is valid
|
||||||
|
sddc_error_t serr = sddc_init_device(dev, sddc_firmware_path);
|
||||||
|
if (serr != SDDC_SUCCESS) { continue; }
|
||||||
|
|
||||||
|
// Set the flag to wait the devices to start up
|
||||||
|
uninit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If some uninitialized devices were found
|
||||||
|
if (uninit) {
|
||||||
|
// Free the device list
|
||||||
|
libusb_free_device_list(devices, 1);
|
||||||
|
|
||||||
|
// Wait for the devices to show back up
|
||||||
|
#ifdef _WIN32
|
||||||
|
Sleep(SDDC_INIT_SEARCH_DELAY_MS);
|
||||||
|
#else
|
||||||
|
usleep(SDDC_INIT_SEARCH_DELAY_MS * 1000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Attempt to list devices again
|
||||||
|
devCount = libusb_get_device_list(ctx, &devices);
|
||||||
|
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate the device list
|
||||||
|
*dev_list = malloc(devCount * sizeof(sddc_devinfo_t));
|
||||||
|
|
||||||
|
// Check each device
|
||||||
|
int found = 0;
|
||||||
|
libusb_device_handle* openDev;
|
||||||
|
for (int i = 0; i < devCount; i++) {
|
||||||
|
// Get the device from the list
|
||||||
|
libusb_device* dev = devices[i];
|
||||||
|
|
||||||
|
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
int err = libusb_get_device_descriptor(dev, &desc);
|
||||||
|
if (err != LIBUSB_SUCCESS) { continue; }
|
||||||
|
|
||||||
|
// If the device is not an SDDC device, go to next device
|
||||||
|
if (desc.idVendor != SDDC_VID || desc.idProduct != SDDC_PID) { continue; }
|
||||||
|
|
||||||
|
// Open the device
|
||||||
|
err = libusb_open(dev, &openDev);
|
||||||
|
if (err != LIBUSB_SUCCESS) {
|
||||||
|
fprintf(stderr, "Failed to open device: %d\n", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create entry
|
||||||
|
sddc_devinfo_t* info = &((*dev_list)[found]);
|
||||||
|
|
||||||
|
// Get the serial number
|
||||||
|
err = libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, info->serial, SDDC_SERIAL_MAX_LEN-1);
|
||||||
|
if (err < LIBUSB_SUCCESS) {
|
||||||
|
printf("Failed to get descriptor: %d\n", err);
|
||||||
|
libusb_close(openDev);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the hardware info
|
||||||
|
sddc_hwinfo_t hwinfo;
|
||||||
|
err = sddc_fx3_get_info(openDev, &hwinfo, 0);
|
||||||
|
if (err < LIBUSB_SUCCESS) {
|
||||||
|
printf("Failed to get device info: %d\n", err);
|
||||||
|
libusb_close(openDev);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the hardware info
|
||||||
|
info->model = (sddc_model_t)hwinfo.model;
|
||||||
|
info->firmwareMajor = hwinfo.firmwareConfigH;
|
||||||
|
info->firmwareMinor = hwinfo.firmwareConfigL;
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
libusb_close(openDev);
|
||||||
|
|
||||||
|
// Increment device counter
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the libusb device list
|
||||||
|
libusb_free_device_list(devices, 1);
|
||||||
|
|
||||||
|
// Return the number of devices found
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sddc_free_device_list(sddc_devinfo_t* dev_list) {
|
||||||
|
// Free the device list if it exists
|
||||||
|
if (dev_list) { free(dev_list); };
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_open(const char* serial, sddc_dev_t** dev) {
|
||||||
|
// Initialize libsddc in case it isn't already
|
||||||
|
sddc_init();
|
||||||
|
|
||||||
|
// Get a list of USB devices
|
||||||
|
libusb_device** devices;
|
||||||
|
int devCount = libusb_get_device_list(ctx, &devices);
|
||||||
|
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Initialize all uninitialized devices
|
||||||
|
bool uninit = false;
|
||||||
|
for (int i = 0; i < devCount; i++) {
|
||||||
|
// Get the device from the list
|
||||||
|
libusb_device* dev = devices[i];
|
||||||
|
|
||||||
|
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
int err = libusb_get_device_descriptor(dev, &desc);
|
||||||
|
if (err != LIBUSB_SUCCESS) { continue; }
|
||||||
|
|
||||||
|
// If it's not an uninitialized device, go to next device
|
||||||
|
if (desc.idVendor != SDDC_UNINIT_VID || desc.idProduct != SDDC_UNINIT_PID) { continue; }
|
||||||
|
|
||||||
|
// Initialize the device
|
||||||
|
printf("Found uninitialized device, initializing...\n");
|
||||||
|
// TODO: Check that the firmware path is valid
|
||||||
|
sddc_error_t serr = sddc_init_device(dev, sddc_firmware_path);
|
||||||
|
if (serr != SDDC_SUCCESS) { continue; }
|
||||||
|
|
||||||
|
// Set the flag to wait the devices to start up
|
||||||
|
uninit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If some uninitialized devices were found
|
||||||
|
if (uninit) {
|
||||||
|
// Free the device list
|
||||||
|
libusb_free_device_list(devices, 1);
|
||||||
|
|
||||||
|
// Wait for the devices to show back up
|
||||||
|
#ifdef _WIN32
|
||||||
|
Sleep(SDDC_INIT_SEARCH_DELAY_MS);
|
||||||
|
#else
|
||||||
|
usleep(SDDC_INIT_SEARCH_DELAY_MS * 1000);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Attempt to list devices again
|
||||||
|
devCount = libusb_get_device_list(ctx, &devices);
|
||||||
|
if (devCount < 0 || !devices) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search through all USB device
|
||||||
|
bool found = false;
|
||||||
|
libusb_device_handle* openDev;
|
||||||
|
for (int i = 0; i < devCount; i++) {
|
||||||
|
// Get the device from the list
|
||||||
|
libusb_device* dev = devices[i];
|
||||||
|
|
||||||
|
// Get the device descriptor. Fail silently on error as it might not even be a SDDC device.
|
||||||
|
struct libusb_device_descriptor desc;
|
||||||
|
int err = libusb_get_device_descriptor(dev, &desc);
|
||||||
|
if (err != LIBUSB_SUCCESS) { continue; }
|
||||||
|
|
||||||
|
// If the device is not an SDDC device, go to next device
|
||||||
|
if (desc.idVendor != SDDC_VID || desc.idProduct != SDDC_PID) { continue; }
|
||||||
|
|
||||||
|
// Open the device
|
||||||
|
err = libusb_open(dev, &openDev);
|
||||||
|
if (err != LIBUSB_SUCCESS) {
|
||||||
|
fprintf(stderr, "Failed to open device: %d\n", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the serial number
|
||||||
|
char dserial[SDDC_SERIAL_MAX_LEN];
|
||||||
|
err = libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, dserial, SDDC_SERIAL_MAX_LEN-1);
|
||||||
|
if (err < LIBUSB_SUCCESS) {
|
||||||
|
printf("Failed to get descriptor: %d\n", err);
|
||||||
|
libusb_close(openDev);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare the serial number and give up if not a match
|
||||||
|
if (strcmp(dserial, serial)) { continue; }
|
||||||
|
|
||||||
|
// Get the device info
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// Set the found flag and stop searching
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the libusb device list
|
||||||
|
libusb_free_device_list(devices, true);
|
||||||
|
|
||||||
|
// If the device was not found, give up
|
||||||
|
if (!found) { return SDDC_ERROR_NOT_FOUND; }
|
||||||
|
|
||||||
|
// Claim the interface
|
||||||
|
libusb_claim_interface(openDev, 0);
|
||||||
|
|
||||||
|
// Allocate the device object
|
||||||
|
*dev = malloc(sizeof(sddc_dev_t));
|
||||||
|
|
||||||
|
// Initialize the device object
|
||||||
|
(*dev)->openDev = openDev;
|
||||||
|
//(*dev)->info = ; //TODO
|
||||||
|
(*dev)->running = false;
|
||||||
|
(*dev)->samplerate = 128e6;
|
||||||
|
(*dev)->tunerFreq = 100e6;
|
||||||
|
(*dev)->gpioState = SDDC_GPIO_SHUTDOWN | SDDC_GPIO_SEL0; // ADC shutdown and HF port selected
|
||||||
|
|
||||||
|
// Stop everything in case the device is partially started
|
||||||
|
printf("Stopping...\n");
|
||||||
|
sddc_stop(*dev);
|
||||||
|
|
||||||
|
// TODO: Setup all of the other state
|
||||||
|
sddc_gpio_put(*dev, SDDC_GPIO_SEL0, false);
|
||||||
|
sddc_gpio_put(*dev, SDDC_GPIO_SEL1, true);
|
||||||
|
sddc_gpio_put(*dev, SDDC_GPIO_VHF_EN, true);
|
||||||
|
sddc_tuner_start((*dev)->openDev, 16e6);
|
||||||
|
sddc_tuner_tune((*dev)->openDev, 100e6);
|
||||||
|
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_R82XX_ATT, 15);
|
||||||
|
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_R83XX_VGA, 9);
|
||||||
|
sddc_fx3_set_param((*dev)->openDev, SDDC_PARAM_AD8340_VGA, 5);
|
||||||
|
|
||||||
|
return SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sddc_close(sddc_dev_t* dev) {
|
||||||
|
// Stop everything
|
||||||
|
sddc_stop(dev);
|
||||||
|
|
||||||
|
// Release the interface
|
||||||
|
libusb_release_interface(dev->openDev, 0);
|
||||||
|
|
||||||
|
// Close the USB device
|
||||||
|
libusb_close(dev->openDev);
|
||||||
|
|
||||||
|
// Free the device struct
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_range_t sddc_get_samplerate_range(sddc_dev_t* dev) {
|
||||||
|
// All devices have the same samplerate range
|
||||||
|
sddc_range_t range = { 8e6, 128e6, 0 };
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_gpio_set(sddc_dev_t* dev, sddc_gpio_t gpios) {
|
||||||
|
// Update the state
|
||||||
|
dev->gpioState = gpios;
|
||||||
|
|
||||||
|
// Push to the device
|
||||||
|
sddc_fx3_gpio(dev->openDev, gpios);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_gpio_put(sddc_dev_t* dev, sddc_gpio_t gpios, bool value) {
|
||||||
|
// Update the state of the given GPIOs only
|
||||||
|
return sddc_gpio_set(dev, (dev->gpioState & (~gpios)) | (value ? gpios : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_set_samplerate(sddc_dev_t* dev, uint32_t samplerate) {
|
||||||
|
// Update the state
|
||||||
|
dev->samplerate = samplerate;
|
||||||
|
|
||||||
|
// If running, send the new sampling rate to the device
|
||||||
|
if (dev->running) {
|
||||||
|
int err = sddc_adc_set_samplerate(dev->openDev, samplerate);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return successfully
|
||||||
|
return SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_set_dithering(sddc_dev_t* dev, bool enabled) {
|
||||||
|
// Update the GPIOs according to the desired state
|
||||||
|
int err = sddc_gpio_put(dev, SDDC_GPIO_DITHER, enabled);
|
||||||
|
return (err < LIBUSB_SUCCESS) ? SDDC_ERROR_USB_ERROR : SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_set_randomizer(sddc_dev_t* dev, bool enabled) {
|
||||||
|
// Update the GPIOs according to the desired state
|
||||||
|
int err = sddc_gpio_put(dev, SDDC_GPIO_RANDOM, enabled);
|
||||||
|
return (err < LIBUSB_SUCCESS) ? SDDC_ERROR_USB_ERROR : SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_set_tuner_frequency(sddc_dev_t* dev, uint64_t frequency) {
|
||||||
|
// Update the state
|
||||||
|
dev->tunerFreq = frequency;
|
||||||
|
|
||||||
|
// If running, send the new frequency to the device
|
||||||
|
if (dev->running) {
|
||||||
|
int err = sddc_tuner_tune(dev->openDev, frequency);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return successfully
|
||||||
|
return SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_start(sddc_dev_t* dev) {
|
||||||
|
// De-assert the shutdown pin
|
||||||
|
int err = sddc_gpio_put(dev, SDDC_GPIO_SHUTDOWN, false);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Start the tuner (TODO: Check if in VHF mode)
|
||||||
|
|
||||||
|
// Start the ADC
|
||||||
|
err = sddc_adc_set_samplerate(dev->openDev, dev->samplerate);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Start the FX3
|
||||||
|
err = sddc_fx3_start(dev->openDev);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Update the state
|
||||||
|
dev->running = true;
|
||||||
|
|
||||||
|
// Return successfully
|
||||||
|
return SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_stop(sddc_dev_t* dev) {
|
||||||
|
// Stop the FX3
|
||||||
|
int err = sddc_fx3_stop(dev->openDev);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Stop the tuner
|
||||||
|
err = sddc_tuner_stop(dev->openDev);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Stop the ADC
|
||||||
|
err = sddc_adc_set_samplerate(dev->openDev, 0);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Set the GPIOs for standby mode
|
||||||
|
err = sddc_gpio_put(dev, SDDC_GPIO_SHUTDOWN, true);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
|
||||||
|
// Update the state
|
||||||
|
dev->running = false;
|
||||||
|
|
||||||
|
// Return successfully
|
||||||
|
return SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
sddc_error_t sddc_rx(sddc_dev_t* dev, int16_t* samples, int count) {
|
||||||
|
// Read samples from the device
|
||||||
|
int bytesRead = 0;
|
||||||
|
int err = libusb_bulk_transfer(dev->openDev, LIBUSB_ENDPOINT_IN | 1, samples, count * sizeof(int16_t), &bytesRead, SDDC_TIMEOUT_MS);
|
||||||
|
if (err < LIBUSB_SUCCESS) { return SDDC_ERROR_USB_ERROR; }
|
||||||
|
return SDDC_SUCCESS;
|
||||||
|
}
|
||||||
92
source_modules/sddc_source/libsddc/src/usb_interface.c
Normal file
92
source_modules/sddc_source/libsddc/src/usb_interface.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#include "usb_interface.h"
|
||||||
|
#include "fx3_boot.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
sddc_error_t sddc_init_device(libusb_device* dev, const char* firmware) {
|
||||||
|
// Open the device
|
||||||
|
libusb_device_handle* openDev;
|
||||||
|
int err = libusb_open(dev, &openDev);
|
||||||
|
if (err != LIBUSB_SUCCESS) {
|
||||||
|
printf("Failed to open device: %d\n", err);
|
||||||
|
return SDDC_ERROR_USB_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to upload the firmware
|
||||||
|
err = sddc_fx3_boot_upload_firmware(openDev, firmware);
|
||||||
|
if (err) {
|
||||||
|
fprintf(stderr, "Failed to upload firmware to uninitialized device\n");
|
||||||
|
return SDDC_ERROR_FIRMWARE_UPLOAD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the device
|
||||||
|
libusb_close(openDev);
|
||||||
|
|
||||||
|
// Return successfully
|
||||||
|
return SDDC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_start(libusb_device_handle* dev) {
|
||||||
|
// Send the start command
|
||||||
|
uint32_t dummy;
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_START, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_stop(libusb_device_handle* dev) {
|
||||||
|
// Send the stop command
|
||||||
|
uint32_t dummy;
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_STOP, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_get_info(libusb_device_handle* dev, sddc_hwinfo_t* info, char enableDebug) {
|
||||||
|
// Fetch the info data
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_GET_INFO, enableDebug, 0, info, sizeof(sddc_hwinfo_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_gpio(libusb_device_handle* dev, sddc_gpio_t gpio) {
|
||||||
|
// Send the GPIO state
|
||||||
|
uint32_t dword = gpio;
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_GPIO, 0, 0, &dword, sizeof(uint32_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_i2c_write(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len) {
|
||||||
|
// Send the I2C data
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_I2C_W, addr, reg, data, len, SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_i2c_read(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len) {
|
||||||
|
// Fetch the I2C data
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_I2C_R, addr, reg, data, len, SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_reset(libusb_device_handle* dev) {
|
||||||
|
// Send the reset command
|
||||||
|
uint32_t dummy;
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_RESET, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_fx3_set_param(libusb_device_handle* dev, sddc_param_t param, uint16_t value) {
|
||||||
|
// Send the parameter
|
||||||
|
uint32_t dummy;
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_FX3_SET_PARAM, value, param, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_adc_set_samplerate(libusb_device_handle* dev, uint32_t samplerate) {
|
||||||
|
// Send the samplerate
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_ADC_SET_RATE, 0, 0, &samplerate, sizeof(uint32_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_tuner_start(libusb_device_handle* dev, uint32_t refFreq) {
|
||||||
|
// Send the reset command
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_START, 0, 0, &refFreq, sizeof(uint32_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_tuner_tune(libusb_device_handle* dev, uint64_t frequency) {
|
||||||
|
// Send the reset command
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_TUNE, 0, 0, &frequency, sizeof(uint64_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
int sddc_tuner_stop(libusb_device_handle* dev) {
|
||||||
|
// Send the reset command
|
||||||
|
uint32_t dummy; // Because the shitty firmware absolute wants data to stop the tuner, this is dumb...
|
||||||
|
return libusb_control_transfer(dev, LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, SDDC_CMD_TUNER_STOP, 0, 0, &dummy, sizeof(uint32_t), SDDC_TIMEOUT_MS);
|
||||||
|
}
|
||||||
168
source_modules/sddc_source/libsddc/src/usb_interface.h
Normal file
168
source_modules/sddc_source/libsddc/src/usb_interface.h
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <sddc.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <libusb.h>
|
||||||
|
|
||||||
|
#define SDDC_UNINIT_VID 0x04B4
|
||||||
|
#define SDDC_UNINIT_PID 0x00F3
|
||||||
|
#define SDDC_VID 0x04B4
|
||||||
|
#define SDDC_PID 0x00F1
|
||||||
|
|
||||||
|
#define SDDC_INIT_SEARCH_DELAY_MS 1000
|
||||||
|
#define SDDC_TIMEOUT_MS 1000
|
||||||
|
|
||||||
|
enum sddc_command {
|
||||||
|
SDDC_CMD_FX3_START = 0xAA,
|
||||||
|
SDDC_CMD_FX3_STOP = 0xAB,
|
||||||
|
SDDC_CMD_FX3_GET_INFO = 0xAC,
|
||||||
|
SDDC_CMD_FX3_GPIO = 0xAD,
|
||||||
|
SDDC_CMD_FX3_I2C_W = 0xAE,
|
||||||
|
SDDC_CMD_FX3_I2C_R = 0xAF,
|
||||||
|
SDDC_CMD_FX3_RESET = 0xB1,
|
||||||
|
SDDC_CMD_FX3_SET_PARAM = 0xB6,
|
||||||
|
SDDC_CMD_ADC_SET_RATE = 0xB2,
|
||||||
|
SDDC_CMD_TUNER_START = 0xB4,
|
||||||
|
SDDC_CMD_TUNER_TUNE = 0xB5,
|
||||||
|
SDDC_CMD_TUNER_STOP = 0xB8,
|
||||||
|
SDDC_CMD_READ_DEBUG = 0xBA
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct sddc_hwinfo {
|
||||||
|
uint8_t model;
|
||||||
|
uint8_t firmwareConfigH;
|
||||||
|
uint8_t firmwareConfigL;
|
||||||
|
uint8_t vendorRequestCount;
|
||||||
|
};
|
||||||
|
typedef struct sddc_hwinfo sddc_hwinfo_t;
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
enum sddc_gpio {
|
||||||
|
SDDC_GPIO_NONE = 0,
|
||||||
|
SDDC_GPIO_ATT_LE = (1 << 0),
|
||||||
|
SDDC_GPIO_ATT_CLK = (1 << 1),
|
||||||
|
SDDC_GPIO_ATT_DATA = (1 << 2),
|
||||||
|
SDDC_GPIO_SEL0 = (1 << 3),
|
||||||
|
SDDC_GPIO_SEL1 = (1 << 4),
|
||||||
|
SDDC_GPIO_SHUTDOWN = (1 << 5),
|
||||||
|
SDDC_GPIO_DITHER = (1 << 6),
|
||||||
|
SDDC_GPIO_RANDOM = (1 << 7),
|
||||||
|
SDDC_GPIO_BIAST_HF = (1 << 8),
|
||||||
|
SDDC_GPIO_BIAST_VHF = (1 << 9),
|
||||||
|
SDDC_GPIO_LED_YELLOW = (1 << 10),
|
||||||
|
SDDC_GPIO_LED_RED = (1 << 11),
|
||||||
|
SDDC_GPIO_LED_BLUE = (1 << 12),
|
||||||
|
SDDC_GPIO_ATT_SEL0 = (1 << 13),
|
||||||
|
SDDC_GPIO_ATT_SEL1 = (1 << 14),
|
||||||
|
SDDC_GPIO_VHF_EN = (1 << 15),
|
||||||
|
SDDC_GPIO_PGA_EN = (1 << 16),
|
||||||
|
SDDC_GPIO_ALL = ((1 << 17)-1)
|
||||||
|
};
|
||||||
|
typedef enum sddc_gpio sddc_gpio_t;
|
||||||
|
|
||||||
|
enum sddc_param {
|
||||||
|
SDDC_PARAM_R82XX_ATT = 1,
|
||||||
|
SDDC_PARAM_R83XX_VGA = 2,
|
||||||
|
SDDC_PARAM_R83XX_SIDEBAND = 3,
|
||||||
|
SDDC_PARAM_R83XX_HARMONIC = 4,
|
||||||
|
SDDC_PARAM_DAT31_ATT = 10,
|
||||||
|
SDDC_PARAM_AD8340_VGA = 11,
|
||||||
|
SDDC_PARAM_PRESELECTOR = 12,
|
||||||
|
SDDC_PARAM_VHF_ATT = 13
|
||||||
|
};
|
||||||
|
typedef enum sddc_param sddc_param_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a device with a given firmware.
|
||||||
|
* @param dev SDDC USB device entry to initialize.
|
||||||
|
* @param firmware Path to the firmware image.
|
||||||
|
*/
|
||||||
|
sddc_error_t sddc_init_device(libusb_device* dev, const char* firmware);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the FX3 streaming.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_start(libusb_device_handle* dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the FX3 streaming.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_stop(libusb_device_handle* dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the hardware info of a device.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
* @param info Hardware info struct to write the return info into.
|
||||||
|
* @param enableDebug 1 to enable hardware debug mode, 0 otherwise.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_get_info(libusb_device_handle* dev, sddc_hwinfo_t* info, char enableDebug);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the state of the GPIO pins.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
* @param gpio State of the GPIO pins.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_gpio(libusb_device_handle* dev, sddc_gpio_t gpio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to the I2C port.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
* @param addr Address of the target I2C device.
|
||||||
|
* @param reg Register to write data to.
|
||||||
|
* @param data Data buffer to write.
|
||||||
|
* @param len Number of bytes to write.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_i2c_write(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from the I2C port.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
* @param addr Address of the target I2C device.
|
||||||
|
* @param reg Register to read data from.
|
||||||
|
* @param data Data buffer to read data into.
|
||||||
|
* @param len Number of bytes to read.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_i2c_write(libusb_device_handle* dev, uint8_t addr, uint16_t reg, const uint8_t* data, uint16_t len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the FX3 into bootloader mode.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_reset(libusb_device_handle* dev);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a device parameter.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
* @param arg Parameter to set.
|
||||||
|
* @param value Value to set the parameter to.
|
||||||
|
*/
|
||||||
|
int sddc_fx3_set_param(libusb_device_handle* dev, sddc_param_t param, uint16_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the ADC sampling rate.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
* @param samplerate Sampling rate. 0 to stop the ADC.
|
||||||
|
*/
|
||||||
|
int sddc_adc_set_samplerate(libusb_device_handle* dev, uint32_t samplerate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the tuner.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
* @param frequency Initial LO frequency.
|
||||||
|
*/
|
||||||
|
int sddc_tuner_start(libusb_device_handle* dev, uint32_t refFreq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the tuner's LO frequency.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
* @param frequency New LO frequency.
|
||||||
|
*/
|
||||||
|
int sddc_tuner_tune(libusb_device_handle* dev, uint64_t frequency);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the tuner.
|
||||||
|
* @param dev SDDC USB device.
|
||||||
|
*/
|
||||||
|
int sddc_tuner_stop(libusb_device_handle* dev);
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user