mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-07-08 18:15:21 +02:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
15112c63b7 | |||
115cb23672 | |||
101f6777ee | |||
82a2a4c04a | |||
c4086f5719 | |||
5f77718d75 | |||
e613087e97 | |||
beb18972ea | |||
1b0a5ed88e | |||
b1ad7590cc | |||
9537ccf2d2 | |||
0ac1bd56bc | |||
936c99dc40 |
287
.github/workflows/build_all.yml
vendored
287
.github/workflows/build_all.yml
vendored
@ -36,13 +36,6 @@ jobs:
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
|
||||
|
||||
- name: Download librtlsdr
|
||||
run: Invoke-WebRequest -Uri "https://ftp.osmocom.org/binaries/windows/rtl-sdr/rtl-sdr-64bit-20240623.zip" -OutFile ${{runner.workspace}}/rtl-sdr.zip
|
||||
|
||||
- name: Patch Pothos with newer librtlsdr version
|
||||
working-directory: ${{runner.workspace}}
|
||||
run: 7z x rtl-sdr.zip ; rm "C:/Program Files/PothosSDR/bin/rtlsdr.dll" ; cp "rtl-sdr-64bit-20240623/librtlsdr.dll" "C:/Program Files/PothosSDR/bin/rtlsdr.dll"
|
||||
|
||||
- name: Download SDRPlay API
|
||||
run: Invoke-WebRequest -Uri "https://www.sdrpp.org/SDRplay.zip" -OutFile ${{runner.workspace}}/SDRplay.zip
|
||||
|
||||
@ -50,7 +43,7 @@ jobs:
|
||||
run: 7z x ${{runner.workspace}}/SDRplay.zip -o"C:/Program Files/"
|
||||
|
||||
- name: Download codec2
|
||||
run: git clone https://github.com/drowe67/codec2
|
||||
run: git clone https://github.com/AlexandreRouma/codec2
|
||||
|
||||
- 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"
|
||||
@ -65,27 +58,25 @@ jobs:
|
||||
run: mkdir "C:/Program Files/codec2" ; mkdir "C:/Program Files/codec2/include" ; mkdir "C:/Program Files/codec2/include/codec2" ; mkdir "C:/Program Files/codec2/lib" ; cd "codec2" ; xcopy "src" "C:/Program Files/codec2/include" ; cd "build" ; xcopy "src" "C:/Program Files/codec2/lib" ; xcopy "codec2" "C:/Program Files/codec2/include/codec2"
|
||||
|
||||
- name: Install vcpkg dependencies
|
||||
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows spdlog:x64-windows
|
||||
run: vcpkg install fftw3:x64-windows glfw3:x64-windows portaudio:x64-windows zstd:x64-windows libusb:x64-windows
|
||||
|
||||
- name: Install rtaudio
|
||||
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
|
||||
|
||||
- name: Install libperseus-sdr
|
||||
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake -DCMAKE_BUILD_TYPE=Release "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
|
||||
|
||||
- name: Install librfnm
|
||||
run: git clone https://github.com/AlexandreRouma/librfnm ; cd librfnm ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
|
||||
|
||||
- name: Install libfobos
|
||||
run: git clone https://github.com/AlexandreRouma/libfobos ; cd libfobos ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
|
||||
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
|
||||
|
||||
- name: Prepare CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
run: cmake "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DCMAKE_BUILD_TYPE=Debug -DCOPY_MSVC_REDISTRIBUTABLES=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake --build . --config Release --verbose
|
||||
run: cmake --build . --config Debug --verbose
|
||||
|
||||
- name: Run tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: ./Debug/min_broken.exe
|
||||
|
||||
- name: Create Archive
|
||||
working-directory: ${{runner.workspace}}
|
||||
@ -98,7 +89,7 @@ jobs:
|
||||
path: ${{runner.workspace}}/sdrpp_windows_x64.zip
|
||||
|
||||
build_macos_intel:
|
||||
runs-on: macos-13
|
||||
runs-on: macos-12
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -107,7 +98,7 @@ jobs:
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako
|
||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
|
||||
|
||||
- name: Install volk
|
||||
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
@ -125,20 +116,14 @@ jobs:
|
||||
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
|
||||
- name: Install libperseus
|
||||
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && sudo make install && cd ..
|
||||
|
||||
- name: Install librfnm
|
||||
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||
|
||||
- name: Install libfobos
|
||||
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
|
||||
|
||||
- name: Install more recent librtlsdr
|
||||
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
|
||||
|
||||
- name: Prepare CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@ -164,7 +149,7 @@ jobs:
|
||||
run: cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
- name: Install dependencies
|
||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool spdlog && pip3 install mako --break-system-packages
|
||||
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako --break-system-packages
|
||||
|
||||
- name: Install volk
|
||||
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
|
||||
@ -184,18 +169,12 @@ jobs:
|
||||
# - name: Install libperseus
|
||||
# run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
|
||||
|
||||
- name: Install librfnm
|
||||
run: git clone https://github.com/AlexandreRouma/librfnm && cd librfnm && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||
|
||||
- name: Install libfobos
|
||||
run: git clone https://github.com/AlexandreRouma/libfobos && cd libfobos && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install && cd ..
|
||||
|
||||
- name: Install more recent librtlsdr
|
||||
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
|
||||
|
||||
- name: Prepare CMake
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
|
||||
|
||||
- name: Build
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
@ -211,7 +190,7 @@ jobs:
|
||||
name: sdrpp_macos_arm
|
||||
path: ${{runner.workspace}}/sdrpp_macos_arm.zip
|
||||
|
||||
build_debian_buster_amd64:
|
||||
build_debian_buster:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -233,29 +212,7 @@ jobs:
|
||||
name: sdrpp_debian_buster_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_debian_buster_aarch64:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
|
||||
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_aarch64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_debian_bullseye_amd64:
|
||||
build_debian_bullseye:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -277,29 +234,7 @@ jobs:
|
||||
name: sdrpp_debian_bullseye_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
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:
|
||||
build_debian_bookworm:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -321,29 +256,7 @@ jobs:
|
||||
name: sdrpp_debian_bookworm_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
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_sid_amd64:
|
||||
build_debian_sid:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -365,29 +278,7 @@ jobs:
|
||||
name: sdrpp_debian_sid_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
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:
|
||||
build_ubuntu_focal:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -409,29 +300,7 @@ jobs:
|
||||
name: sdrpp_ubuntu_focal_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
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:
|
||||
build_ubuntu_jammy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -452,15 +321,15 @@ jobs:
|
||||
with:
|
||||
name: sdrpp_ubuntu_jammy_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_ubuntu_jammy_aarch64:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
|
||||
build_ubuntu_mantic:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Create Docker Image
|
||||
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_jammy && docker build . --tag sdrpp_build
|
||||
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_mantic && 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
|
||||
@ -472,10 +341,10 @@ jobs:
|
||||
- name: Save Deb Archive
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sdrpp_ubuntu_jammy_aarch64
|
||||
name: sdrpp_ubuntu_mantic_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_ubuntu_noble_amd64:
|
||||
build_ubuntu_noble:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -497,72 +366,6 @@ jobs:
|
||||
name: sdrpp_ubuntu_noble_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_ubuntu_noble_aarch64:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Create Docker Image
|
||||
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_noble && 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_noble_aarch64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_ubuntu_oracular_amd64:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Create Docker Image
|
||||
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_oracular && 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_oracular_amd64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_ubuntu_oracular_aarch64:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Create Docker Image
|
||||
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_oracular && 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_oracular_aarch64
|
||||
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
|
||||
|
||||
build_raspios_bullseye_armhf:
|
||||
runs-on: ARM
|
||||
|
||||
@ -618,29 +421,7 @@ jobs:
|
||||
path: ${{runner.workspace}}/sdrpp.apk
|
||||
|
||||
create_full_archive:
|
||||
needs: [
|
||||
'build_windows',
|
||||
'build_macos_intel',
|
||||
'build_macos_arm',
|
||||
'build_debian_buster_amd64',
|
||||
'build_debian_buster_aarch64',
|
||||
'build_debian_bullseye_amd64',
|
||||
'build_debian_bullseye_aarch64',
|
||||
'build_debian_bookworm_amd64',
|
||||
'build_debian_bookworm_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_ubuntu_oracular_amd64',
|
||||
'build_ubuntu_oracular_aarch64',
|
||||
'build_raspios_bullseye_armhf',
|
||||
'build_android'
|
||||
]
|
||||
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']
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
@ -654,21 +435,13 @@ jobs:
|
||||
mv sdrpp_macos_intel/sdrpp_macos_intel.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_buster_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_aarch64.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_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_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_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_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_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_aarch64.deb &&
|
||||
mv sdrpp_ubuntu_mantic_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_mantic_amd64.deb &&
|
||||
mv sdrpp_ubuntu_noble_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_noble_amd64.deb &&
|
||||
mv sdrpp_ubuntu_noble_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_noble_aarch64.deb &&
|
||||
mv sdrpp_ubuntu_oracular_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_oracular_amd64.deb &&
|
||||
mv sdrpp_ubuntu_oracular_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_oracular_aarch64.deb &&
|
||||
mv sdrpp_raspios_bullseye_armhf/sdrpp_debian_armhf.deb sdrpp_all/sdrpp_raspios_bullseye_armhf.deb &&
|
||||
mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk
|
||||
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -16,8 +16,4 @@ poggers_decoder
|
||||
m17_decoder/libcorrect
|
||||
SDR++.app
|
||||
android/deps
|
||||
android/app/assets
|
||||
source_modules/dragonlabs_source
|
||||
source_modules/badgesdr_source
|
||||
decoder_modules/mystery_decoder
|
||||
decoder_modules/tetra_decoder
|
||||
android/app/assets
|
@ -15,12 +15,8 @@ option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)
|
||||
option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF)
|
||||
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF)
|
||||
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
|
||||
option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF)
|
||||
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
||||
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
|
||||
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_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_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
|
||||
@ -46,15 +42,12 @@ option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: port
|
||||
|
||||
# Decoders
|
||||
option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" OFF)
|
||||
option(OPT_BUILD_DAB_DECODER "Build the DAB/DAB+ decoder (no dependencies required)" OFF)
|
||||
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
|
||||
option(OPT_BUILD_KG_SSTV_DECODER "Build the KG SSTV (KG-STV) decoder module (no dependencies required)" OFF)
|
||||
option(OPT_BUILD_M17_DECODER "Build the M17 decoder module (Dependencies: codec2)" OFF)
|
||||
option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON)
|
||||
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
|
||||
option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF)
|
||||
option(OPT_BUILD_VOR_RECEIVER "VOR beacon receiver" OFF)
|
||||
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
|
||||
|
||||
# Misc
|
||||
@ -147,30 +140,14 @@ if (OPT_BUILD_FILE_SOURCE)
|
||||
add_subdirectory("source_modules/file_source")
|
||||
endif (OPT_BUILD_FILE_SOURCE)
|
||||
|
||||
if (OPT_BUILD_FOBOSSDR_SOURCE)
|
||||
add_subdirectory("source_modules/fobossdr_source")
|
||||
endif (OPT_BUILD_FOBOSSDR_SOURCE)
|
||||
|
||||
if (OPT_BUILD_HACKRF_SOURCE)
|
||||
add_subdirectory("source_modules/hackrf_source")
|
||||
endif (OPT_BUILD_HACKRF_SOURCE)
|
||||
|
||||
if (OPT_BUILD_HAROGIC_SOURCE)
|
||||
add_subdirectory("source_modules/harogic_source")
|
||||
endif (OPT_BUILD_HAROGIC_SOURCE)
|
||||
|
||||
if (OPT_BUILD_HERMES_SOURCE)
|
||||
add_subdirectory("source_modules/hermes_source")
|
||||
endif (OPT_BUILD_HERMES_SOURCE)
|
||||
|
||||
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)
|
||||
add_subdirectory("source_modules/limesdr_source")
|
||||
endif (OPT_BUILD_LIMESDR_SOURCE)
|
||||
@ -203,10 +180,6 @@ if (OPT_BUILD_RTL_TCP_SOURCE)
|
||||
add_subdirectory("source_modules/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)
|
||||
add_subdirectory("source_modules/sdrpp_server_source")
|
||||
endif (OPT_BUILD_SDRPP_SERVER_SOURCE)
|
||||
@ -263,10 +236,6 @@ if (OPT_BUILD_ATV_DECODER)
|
||||
add_subdirectory("decoder_modules/atv_decoder")
|
||||
endif (OPT_BUILD_ATV_DECODER)
|
||||
|
||||
if (OPT_BUILD_DAB_DECODER)
|
||||
add_subdirectory("decoder_modules/dab_decoder")
|
||||
endif (OPT_BUILD_DAB_DECODER)
|
||||
|
||||
if (OPT_BUILD_FALCON9_DECODER)
|
||||
add_subdirectory("decoder_modules/falcon9_decoder")
|
||||
endif (OPT_BUILD_FALCON9_DECODER)
|
||||
@ -291,14 +260,6 @@ if (OPT_BUILD_RADIO)
|
||||
add_subdirectory("decoder_modules/radio")
|
||||
endif (OPT_BUILD_RADIO)
|
||||
|
||||
if (OPT_BUILD_RYFI_DECODER)
|
||||
add_subdirectory("decoder_modules/ryfi_decoder")
|
||||
endif (OPT_BUILD_RYFI_DECODER)
|
||||
|
||||
if (OPT_BUILD_VOR_RECEIVER)
|
||||
add_subdirectory("decoder_modules/vor_receiver")
|
||||
endif (OPT_BUILD_VOR_RECEIVER)
|
||||
|
||||
if (OPT_BUILD_WEATHER_SAT_DECODER)
|
||||
add_subdirectory("decoder_modules/weather_sat_decoder")
|
||||
endif (OPT_BUILD_WEATHER_SAT_DECODER)
|
||||
@ -339,6 +300,7 @@ endif (OPT_BUILD_SCHEDULER)
|
||||
|
||||
if (MSVC)
|
||||
add_executable(sdrpp "src/main.cpp" "win32/resources.rc")
|
||||
add_executable(min_broken "min_broken/main.cpp" "win32/resources.rc")
|
||||
else ()
|
||||
add_executable(sdrpp "src/main.cpp")
|
||||
endif ()
|
||||
@ -347,12 +309,13 @@ target_link_libraries(sdrpp PRIVATE sdrpp_core)
|
||||
|
||||
# Compiler arguments
|
||||
target_compile_options(sdrpp PRIVATE ${SDRPP_COMPILER_FLAGS})
|
||||
target_compile_options(min_broken PRIVATE ${SDRPP_COMPILER_FLAGS})
|
||||
|
||||
# Copy dynamic libs over
|
||||
if (MSVC)
|
||||
add_custom_target(do_always ALL xcopy /s \"$<TARGET_FILE_DIR:sdrpp_core>\\*.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
||||
add_custom_target(do_always_volk ALL xcopy /s \"C:/Program Files/PothosSDR/bin\\volk.dll\" \"$<TARGET_FILE_DIR:sdrpp>\" /Y)
|
||||
|
||||
|
||||
if (COPY_MSVC_REDISTRIBUTABLES)
|
||||
# Get the list of Visual C++ runtime DLLs
|
||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP True)
|
||||
@ -369,7 +332,6 @@ if (MSVC)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
|
||||
add_custom_target(do_always ALL cp \"$<TARGET_FILE_DIR:sdrpp_core>/libsdrpp_core.so\" \"$<TARGET_FILE_DIR:sdrpp>\")
|
||||
endif ()
|
||||
@ -407,6 +369,4 @@ endif ()
|
||||
|
||||
# Create uninstall target
|
||||
configure_file(${CMAKE_SOURCE_DIR}/cmake_uninstall.cmake ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY)
|
||||
add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
|
||||
# Create headers target
|
||||
add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
@ -10,7 +10,7 @@ android {
|
||||
minSdkVersion 28
|
||||
targetSdkVersion 28
|
||||
versionCode 1
|
||||
versionName "1.2.1"
|
||||
versionName "1.2.0"
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
|
@ -99,10 +99,7 @@ namespace backend {
|
||||
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_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
|
||||
monitor = glfwGetPrimaryMonitor();
|
||||
window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);
|
||||
|
@ -147,11 +147,11 @@ int sdrpp_main(int argc, char* argv[]) {
|
||||
defConfig["menuElements"][3]["name"] = "Sinks";
|
||||
defConfig["menuElements"][3]["open"] = true;
|
||||
|
||||
defConfig["menuElements"][4]["name"] = "Frequency Manager";
|
||||
defConfig["menuElements"][4]["open"] = true;
|
||||
defConfig["menuElements"][3]["name"] = "Frequency Manager";
|
||||
defConfig["menuElements"][3]["open"] = true;
|
||||
|
||||
defConfig["menuElements"][5]["name"] = "VFO Color";
|
||||
defConfig["menuElements"][5]["open"] = true;
|
||||
defConfig["menuElements"][4]["name"] = "VFO Color";
|
||||
defConfig["menuElements"][4]["open"] = true;
|
||||
|
||||
defConfig["menuElements"][6]["name"] = "Band Plan";
|
||||
defConfig["menuElements"][6]["open"] = true;
|
||||
@ -173,26 +173,16 @@ int sdrpp_main(int argc, char* argv[]) {
|
||||
defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["File Source"]["module"] = "file_source";
|
||||
defConfig["moduleInstances"]["File Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["FobosSDR Source"]["module"] = "fobossdr_source";
|
||||
defConfig["moduleInstances"]["FobosSDR Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["HackRF Source"]["module"] = "hackrf_source";
|
||||
defConfig["moduleInstances"]["HackRF Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["Harogic Source"]["module"] = "harogic_source";
|
||||
defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["Hermes Source"]["module"] = "hermes_source";
|
||||
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["HydraSDR Source"]["module"] = "hydrasdr_source";
|
||||
defConfig["moduleInstances"]["HydraSDR Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
|
||||
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"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
|
||||
defConfig["moduleInstances"]["PlutoSDR Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["RFNM Source"]["module"] = "rfnm_source";
|
||||
defConfig["moduleInstances"]["RFNM Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
|
||||
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["RFspace Source"]["module"] = "rfspace_source";
|
||||
defConfig["moduleInstances"]["RFspace Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["RTL-SDR Source"]["module"] = "rtl_sdr_source";
|
||||
@ -203,12 +193,8 @@ int sdrpp_main(int argc, char* argv[]) {
|
||||
defConfig["moduleInstances"]["SDRplay Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["SDR++ Server Source"]["module"] = "sdrpp_server_source";
|
||||
defConfig["moduleInstances"]["SDR++ Server Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["Spectran HTTP Source"]["module"] = "spectran_http_source";
|
||||
defConfig["moduleInstances"]["Spectran HTTP Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["SpyServer Source"]["module"] = "spyserver_source";
|
||||
defConfig["moduleInstances"]["SpyServer Source"]["enabled"] = true;
|
||||
defConfig["moduleInstances"]["USRP Source"]["module"] = "usrp_source";
|
||||
defConfig["moduleInstances"]["USRP Source"]["enabled"] = true;
|
||||
|
||||
defConfig["moduleInstances"]["Audio Sink"] = "audio_sink";
|
||||
defConfig["moduleInstances"]["Network Sink"] = "network_sink";
|
||||
@ -234,19 +220,12 @@ int sdrpp_main(int argc, char* argv[]) {
|
||||
|
||||
defConfig["modules"] = json::array();
|
||||
|
||||
defConfig["offsets"]["SpyVerter"] = 120000000.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["offsetMode"] = (int)0; // Off
|
||||
defConfig["offset"] = 0.0;
|
||||
defConfig["showMenu"] = true;
|
||||
defConfig["showWaterfall"] = true;
|
||||
defConfig["source"] = "";
|
||||
defConfig["decimation"] = 1;
|
||||
defConfig["decimationPower"] = 0;
|
||||
defConfig["iqCorrection"] = false;
|
||||
defConfig["invertIQ"] = false;
|
||||
|
||||
@ -327,18 +306,12 @@ int sdrpp_main(int argc, char* argv[]) {
|
||||
|
||||
// Remove unused elements
|
||||
auto items = core::configManager.conf.items();
|
||||
auto newConf = core::configManager.conf;
|
||||
bool configCorrected = false;
|
||||
for (auto const& item : items) {
|
||||
if (!defConfig.contains(item.key())) {
|
||||
flog::info("Unused key in config {0}, repairing", item.key());
|
||||
newConf.erase(item.key());
|
||||
configCorrected = true;
|
||||
core::configManager.conf.erase(item.key());
|
||||
}
|
||||
}
|
||||
if (configCorrected) {
|
||||
core::configManager.conf = newConf;
|
||||
}
|
||||
|
||||
// Update to new module representation in config if needed
|
||||
for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {
|
||||
|
@ -37,12 +37,9 @@ namespace sdrpp_credits {
|
||||
const char* hardwareDonators[] = {
|
||||
"Aaronia AG",
|
||||
"Airspy",
|
||||
"Alex 4Z5LV",
|
||||
"Analog Devices",
|
||||
"CaribouLabs",
|
||||
"Deepace",
|
||||
"Ettus Research",
|
||||
"Harogic",
|
||||
"Howard Su",
|
||||
"MicroPhase",
|
||||
"Microtelecom",
|
||||
@ -50,7 +47,6 @@ namespace sdrpp_credits {
|
||||
"Nuand",
|
||||
"RFNM",
|
||||
"RFspace",
|
||||
"RigExpert",
|
||||
"RTL-SDRblog",
|
||||
"SDRplay"
|
||||
};
|
||||
@ -70,18 +66,15 @@ namespace sdrpp_credits {
|
||||
"Flinger Films",
|
||||
"Frank Werner (HB9FXQ)",
|
||||
"gringogrigio",
|
||||
"Jandro",
|
||||
"Jeff Moe",
|
||||
"Joe Cupano",
|
||||
"KD1SQ",
|
||||
"Kezza",
|
||||
"Krys Kamieniecki",
|
||||
"Lee Donaghy",
|
||||
"Lee (KD1SQ)",
|
||||
"Lee KD1SQ",
|
||||
".lozenge. (Hank Hill)",
|
||||
"Martin Herren (HB9FXX)",
|
||||
"NeoVilsonWong",
|
||||
"Nitin (VU2JEK)",
|
||||
"ON4MU",
|
||||
"Passion-Radio.com",
|
||||
"Paul Maine",
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <volk/volk.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace dsp::buffer {
|
||||
template<class T>
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <volk/volk.h>
|
||||
#include "../buffer/buffer.h"
|
||||
|
||||
namespace dsp {
|
||||
template<class T>
|
||||
|
@ -5,120 +5,113 @@
|
||||
#include <gui/main_window.h>
|
||||
#include <gui/style.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <utils/optionlist.h>
|
||||
#include <gui/dialogs/dialog_box.h>
|
||||
|
||||
namespace sourcemenu {
|
||||
int offsetMode = 0;
|
||||
int sourceId = 0;
|
||||
EventHandler<std::string> sourcesChangedHandler;
|
||||
EventHandler<std::string> sourceUnregisterHandler;
|
||||
OptionList<std::string, std::string> sources;
|
||||
std::string selectedSource;
|
||||
|
||||
int decimId = 0;
|
||||
OptionList<int, int> decimations;
|
||||
|
||||
double customOffset = 0.0;
|
||||
double effectiveOffset = 0.0;
|
||||
int decimationPower = 0;
|
||||
bool iqCorrection = false;
|
||||
bool invertIQ = false;
|
||||
|
||||
int offsetId = 0;
|
||||
double manualOffset = 0.0;
|
||||
std::string selectedOffset;
|
||||
double effectiveOffset = 0.0;
|
||||
OptionList<std::string, double> offsets;
|
||||
std::map<std::string, double> namedOffsets;
|
||||
EventHandler<std::string> sourceRegisteredHandler;
|
||||
EventHandler<std::string> sourceUnregisterHandler;
|
||||
EventHandler<std::string> sourceUnregisteredHandler;
|
||||
|
||||
bool showAddOffsetDialog = false;
|
||||
char newOffsetName[1024];
|
||||
double newOffset = 0.0;
|
||||
std::vector<std::string> sourceNames;
|
||||
std::string sourceNamesTxt;
|
||||
std::string selectedSource;
|
||||
|
||||
bool showDelOffsetDialog = false;
|
||||
std::string delOffsetName = "";
|
||||
|
||||
// Offset IDs
|
||||
enum {
|
||||
OFFSET_ID_NONE,
|
||||
OFFSET_ID_MANUAL,
|
||||
OFFSET_ID_CUSTOM_BASE
|
||||
OFFSET_MODE_NONE,
|
||||
OFFSET_MODE_CUSTOM,
|
||||
OFFSET_MODE_SPYVERTER,
|
||||
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
|
||||
};
|
||||
|
||||
void updateOffset() {
|
||||
// Compute the effective offset
|
||||
switch (offsetId) {
|
||||
case OFFSET_ID_NONE:
|
||||
effectiveOffset = 0;
|
||||
break;
|
||||
case OFFSET_ID_MANUAL:
|
||||
effectiveOffset = manualOffset;
|
||||
break;
|
||||
default:
|
||||
effectiveOffset = namedOffsets[offsets.name(offsetId)];
|
||||
break;
|
||||
}
|
||||
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";
|
||||
|
||||
// Apply it
|
||||
const char* decimationStages = "None\0"
|
||||
"2\0"
|
||||
"4\0"
|
||||
"8\0"
|
||||
"16\0"
|
||||
"32\0"
|
||||
"64\0";
|
||||
|
||||
void updateOffset() {
|
||||
if (offsetMode == OFFSET_MODE_CUSTOM) { effectiveOffset = customOffset; }
|
||||
else if (offsetMode == OFFSET_MODE_SPYVERTER) {
|
||||
effectiveOffset = 120000000;
|
||||
} // 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;
|
||||
}
|
||||
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() {
|
||||
// Get sources
|
||||
auto sourceNames = sigpath::sourceManager.getSourceNames();
|
||||
|
||||
// Define source options
|
||||
sources.clear();
|
||||
sourceNames = sigpath::sourceManager.getSourceNames();
|
||||
sourceNamesTxt.clear();
|
||||
for (auto name : sourceNames) {
|
||||
sources.define(name, name, name);
|
||||
sourceNamesTxt += name;
|
||||
sourceNamesTxt += '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void selectSource(std::string name) {
|
||||
// If there is no source, give up
|
||||
if (sources.empty()) {
|
||||
sourceId = 0;
|
||||
if (sourceNames.empty()) {
|
||||
selectedSource.clear();
|
||||
return;
|
||||
}
|
||||
auto it = std::find(sourceNames.begin(), sourceNames.end(), name);
|
||||
if (it == sourceNames.end()) {
|
||||
selectSource(sourceNames[0]);
|
||||
return;
|
||||
}
|
||||
sourceId = std::distance(sourceNames.begin(), it);
|
||||
selectedSource = sourceNames[sourceId];
|
||||
sigpath::sourceManager.selectSource(sourceNames[sourceId]);
|
||||
}
|
||||
|
||||
// If a source with the given name doesn't exist, select the first source instead
|
||||
if (!sources.valueExists(name)) {
|
||||
selectSource(sources.value(0));
|
||||
void onSourceRegistered(std::string name, void* ctx) {
|
||||
refreshSources();
|
||||
|
||||
if (selectedSource.empty()) {
|
||||
sourceId = 0;
|
||||
selectSource(sourceNames[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the GUI variables
|
||||
sourceId = sources.valueId(name);
|
||||
selectedSource = name;
|
||||
|
||||
// Select the source module
|
||||
sigpath::sourceManager.selectSource(name);
|
||||
}
|
||||
|
||||
void onSourcesChanged(std::string name, void* ctx) {
|
||||
// Update the source list
|
||||
refreshSources();
|
||||
|
||||
// Reselect the current source
|
||||
selectSource(selectedSource);
|
||||
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
|
||||
}
|
||||
|
||||
void onSourceUnregister(std::string name, void* ctx) {
|
||||
@ -127,173 +120,60 @@ namespace sourcemenu {
|
||||
// TODO: Stop everything
|
||||
}
|
||||
|
||||
void reloadOffsets() {
|
||||
// Clear list
|
||||
offsets.clear();
|
||||
namedOffsets.clear();
|
||||
void onSourceUnregistered(std::string name, void* ctx) {
|
||||
refreshSources();
|
||||
|
||||
// Define special offset modes
|
||||
offsets.define("None", OFFSET_ID_NONE);
|
||||
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 (sourceNames.empty()) {
|
||||
selectedSource = "";
|
||||
return;
|
||||
}
|
||||
|
||||
// Define custom offsets
|
||||
for (auto& [name, offset] : namedOffsets) {
|
||||
offsets.define(name, offsets.size());
|
||||
if (name == selectedSource) {
|
||||
sourceId = std::clamp<int>(sourceId, 0, sourceNames.size() - 1);
|
||||
selectSource(sourceNames[sourceId]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Release the config file
|
||||
core::configManager.release();
|
||||
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Load other settings
|
||||
std::string selectedSource = core::configManager.conf["source"];
|
||||
manualOffset = core::configManager.conf["manualOffset"];
|
||||
std::string selectedOffset = core::configManager.conf["selectedOffset"];
|
||||
std::string selected = core::configManager.conf["source"];
|
||||
customOffset = core::configManager.conf["offset"];
|
||||
offsetMode = core::configManager.conf["offsetMode"];
|
||||
decimationPower = core::configManager.conf["decimationPower"];
|
||||
iqCorrection = core::configManager.conf["iqCorrection"];
|
||||
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.setInvertIQ(invertIQ);
|
||||
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
|
||||
selectOffsetByName(selectedOffset);
|
||||
updateOffset();
|
||||
|
||||
// Register handlers
|
||||
sourcesChangedHandler.handler = onSourcesChanged;
|
||||
refreshSources();
|
||||
selectSource(selected);
|
||||
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
|
||||
|
||||
sourceRegisteredHandler.handler = onSourceRegistered;
|
||||
sourceUnregisterHandler.handler = onSourceUnregister;
|
||||
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourcesChangedHandler);
|
||||
sourceUnregisteredHandler.handler = onSourceUnregistered;
|
||||
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
|
||||
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
|
||||
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourcesChangedHandler);
|
||||
}
|
||||
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
|
||||
|
||||
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;
|
||||
core::configManager.release();
|
||||
}
|
||||
|
||||
void draw(void* ctx) {
|
||||
float itemWidth = ImGui::GetContentRegionAvail().x;
|
||||
float lineHeight = ImGui::GetTextLineHeightWithSpacing();
|
||||
float spacing = lineHeight - ImGui::GetTextLineHeight();
|
||||
bool running = gui::mainWindow.sdrIsRunning();
|
||||
|
||||
if (running) { style::beginDisabled(); }
|
||||
|
||||
ImGui::SetNextItemWidth(itemWidth);
|
||||
if (ImGui::Combo("##source", &sourceId, sources.txt)) {
|
||||
std::string newSource = sources.value(sourceId);
|
||||
selectSource(newSource);
|
||||
if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
|
||||
selectSource(sourceNames[sourceId]);
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["source"] = newSource;
|
||||
core::configManager.conf["source"] = sourceNames[sourceId];
|
||||
core::configManager.release(true);
|
||||
}
|
||||
|
||||
@ -316,45 +196,21 @@ namespace sourcemenu {
|
||||
}
|
||||
|
||||
ImGui::LeftLabel("Offset mode");
|
||||
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX() - 2.0f*(lineHeight + 1.5f*spacing));
|
||||
if (ImGui::Combo("##_sdrpp_offset", &offsetId, offsets.txt)) {
|
||||
selectOffsetById(offsetId);
|
||||
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) {
|
||||
updateOffset();
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["selectedOffset"] = offsets.key(offsetId);
|
||||
core::configManager.conf["offsetMode"] = offsetMode;
|
||||
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::FillWidth();
|
||||
if (offsetId == OFFSET_ID_MANUAL) {
|
||||
if (ImGui::InputDouble("##freq_offset", &manualOffset, 1.0, 100.0)) {
|
||||
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
||||
if (offsetMode == OFFSET_MODE_CUSTOM) {
|
||||
if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) {
|
||||
updateOffset();
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["manualOffset"] = manualOffset;
|
||||
core::configManager.conf["offset"] = customOffset;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
}
|
||||
@ -366,11 +222,11 @@ namespace sourcemenu {
|
||||
|
||||
if (running) { style::beginDisabled(); }
|
||||
ImGui::LeftLabel("Decimation");
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::Combo("##source_decim", &decimId, decimations.txt)) {
|
||||
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId));
|
||||
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
||||
if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) {
|
||||
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
|
||||
core::configManager.acquire();
|
||||
core::configManager.conf["decimation"] = decimations.key(decimId);
|
||||
core::configManager.conf["decimationPower"] = decimationPower;
|
||||
core::configManager.release(true);
|
||||
}
|
||||
if (running) { style::endDisabled(); }
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include <gui/style.h>
|
||||
#include <gui/gui.h>
|
||||
#include <backend.h>
|
||||
#include <utils/hrfreq.h>
|
||||
|
||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
@ -91,7 +90,6 @@ void FrequencySelect::moveCursorToDigit(int i) {
|
||||
|
||||
void FrequencySelect::draw() {
|
||||
auto window = ImGui::GetCurrentWindow();
|
||||
auto io = ImGui::GetIO();
|
||||
widgetPos = ImGui::GetWindowContentRegionMin();
|
||||
ImVec2 cursorPos = ImGui::GetCursorPos();
|
||||
widgetPos.x += window->Pos.x + cursorPos.x;
|
||||
@ -134,7 +132,7 @@ void FrequencySelect::draw() {
|
||||
ImVec2 mousePos = ImGui::GetMousePos();
|
||||
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
|
||||
int mw = io.MouseWheel;
|
||||
int mw = ImGui::GetIO().MouseWheel;
|
||||
bool onDigit = false;
|
||||
bool hovered = false;
|
||||
|
||||
@ -176,7 +174,7 @@ void FrequencySelect::draw() {
|
||||
moveCursorToDigit(i + 1);
|
||||
}
|
||||
|
||||
auto chars = io.InputQueueCharacters;
|
||||
auto chars = ImGui::GetIO().InputQueueCharacters;
|
||||
|
||||
// For each keyboard characters, type it
|
||||
for (int j = 0; j < chars.Size; j++) {
|
||||
@ -196,34 +194,6 @@ void FrequencySelect::draw() {
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
@ -52,4 +52,15 @@ namespace ImGui {
|
||||
bufferMtx.unlock();
|
||||
}
|
||||
|
||||
void SymbolDiagram::setCount(int count) {
|
||||
std::lock_guard<std::mutex> lck(bufferMtx);
|
||||
delete[] buffer;
|
||||
buffer = new float[count];
|
||||
sampleCount = count;
|
||||
memset(buffer, 0, sampleCount * sizeof(float));
|
||||
}
|
||||
|
||||
int SymbolDiagram::getCount() {
|
||||
return sampleCount;
|
||||
}
|
||||
}
|
@ -18,6 +18,10 @@ namespace ImGui {
|
||||
|
||||
void releaseBuffer();
|
||||
|
||||
void setCount(int count);
|
||||
|
||||
int getCount();
|
||||
|
||||
std::vector<float> lines;
|
||||
|
||||
private:
|
||||
|
@ -110,7 +110,7 @@ namespace ImGui {
|
||||
viewBandwidth = 1.0;
|
||||
wholeBandwidth = 1.0;
|
||||
|
||||
updatePallette(DEFAULT_COLOR_MAP, 13);
|
||||
//updatePallette(DEFAULT_COLOR_MAP, 13);
|
||||
}
|
||||
|
||||
void WaterFall::init() {
|
||||
|
@ -85,7 +85,7 @@ void SourceManager::tune(double freq) {
|
||||
return;
|
||||
}
|
||||
// TODO: No need to always retune the hardware in Panadapter mode
|
||||
selectedHandler->tuneHandler(abs(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset), selectedHandler->ctx);
|
||||
selectedHandler->tuneHandler(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset, selectedHandler->ctx);
|
||||
onRetune.emit(freq);
|
||||
currentFreq = freq;
|
||||
}
|
||||
|
@ -1,120 +0,0 @@
|
||||
#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
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
#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
|
||||
|
||||
#define VERSION_STR "1.2.1"
|
||||
#define VERSION_STR "1.2.0"
|
@ -1,65 +0,0 @@
|
||||
#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);
|
||||
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;
|
||||
};
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
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
|
63
decoder_modules/atv_decoder/src/chroma_pll.h
Normal file
63
decoder_modules/atv_decoder/src/chroma_pll.h
Normal file
@ -0,0 +1,63 @@
|
||||
#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;
|
||||
}
|
||||
};
|
||||
}
|
239
decoder_modules/atv_decoder/src/chrominance_filter.h
Normal file
239
decoder_modules/atv_decoder/src/chrominance_filter.h
Normal file
@ -0,0 +1,239 @@
|
||||
#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)
|
@ -1,131 +0,0 @@
|
||||
#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,33 +5,6 @@
|
||||
#include <dsp/multirate/polyphase_bank.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> {
|
||||
using base_type = dsp::Processor<float, float>;
|
||||
public:
|
||||
@ -54,17 +27,41 @@ public:
|
||||
_interpPhaseCount = interpPhaseCount;
|
||||
_interpTapCount = interpTapCount;
|
||||
|
||||
pcl.init(_muGain, _omegaGain, 0.0, 0.0, 1.0, _omega, _omega * (1.0 - omegaRelLimit), _omega * (1.0 + omegaRelLimit));
|
||||
generateInterpTaps();
|
||||
buffer = dsp::buffer::alloc<float>(STREAM_BUFFER_SIZE + _interpTapCount);
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
@ -84,7 +81,8 @@ public:
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
base_type::tempStop();
|
||||
offset = 0;
|
||||
phase = 0;
|
||||
pcl.phase = 0.0f;
|
||||
pcl.freq = _omega;
|
||||
base_type::tempStart();
|
||||
}
|
||||
|
||||
@ -95,120 +93,61 @@ public:
|
||||
// Copy data to work buffer
|
||||
memcpy(bufStart, base_type::_in->readBuf, count * sizeof(float));
|
||||
|
||||
// Process samples while they are available
|
||||
if (test2) {
|
||||
test2 = false;
|
||||
offset += 5;
|
||||
}
|
||||
|
||||
// Process all samples
|
||||
while (offset < count) {
|
||||
// While the offset is negative, out put zeros
|
||||
while (offset < 0 && pixel < LINE_SIZE) {
|
||||
// Output a zero
|
||||
base_type::out.writeBuf[pixel++] = 0.0f;
|
||||
// Calculate new output value
|
||||
int phase = std::clamp<int>(floorf(pcl.phase * (float)_interpPhaseCount), 0, _interpPhaseCount - 1);
|
||||
float outVal;
|
||||
volk_32f_x2_dot_prod_32f(&outVal, &buffer[offset], interpBank.phases[phase], _interpTapCount);
|
||||
base_type::out.writeBuf[outCount++] = outVal;
|
||||
|
||||
// Increment the phase
|
||||
phase += period;
|
||||
offset += (phase >> 30);
|
||||
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)
|
||||
// If the end of the line is reached, process it and determin error
|
||||
float error = 0;
|
||||
if (outCount >= 720) {
|
||||
// Compute averages.
|
||||
float left = 0.0f, right = 0.0f;
|
||||
int lc = 0, rc = 0;
|
||||
for (int i = SYNC_L_START; i < LINE_SIZE; i++) {
|
||||
for (int i = (720-17); i < 720; i++) {
|
||||
left += base_type::out.writeBuf[i];
|
||||
lc++;
|
||||
}
|
||||
for (int i = 0; i < SYNC_R_START; i++) {
|
||||
for (int i = 0; i < 27; i++) {
|
||||
left += base_type::out.writeBuf[i];
|
||||
lc++;
|
||||
}
|
||||
for (int i = SYNC_R_START; i < SYNC_R_END; i++) {
|
||||
for (int i = 27; i < (54+17); i++) {
|
||||
right += base_type::out.writeBuf[i];
|
||||
rc++;
|
||||
}
|
||||
left *= (1.0f/44.0f);
|
||||
right *= (1.0f/44.0f);
|
||||
|
||||
// If the sync is present, compute error
|
||||
if ((left < syncLevel && right < syncLevel) && !forceLock) {
|
||||
error = (left + syncBias - right);
|
||||
locked = true;
|
||||
}
|
||||
else {
|
||||
locked = false;
|
||||
}
|
||||
|
||||
// Compute the error
|
||||
float error = (left - right) * (1.0f/((float)SYNC_HALF_LEN));
|
||||
|
||||
// Compute the change in phase and frequency due to the error
|
||||
float periodDelta = error * _omegaGain;
|
||||
float phaseDelta = error * _muGain;
|
||||
|
||||
// Normalize the phase delta (TODO: Make faster)
|
||||
while (phaseDelta <= -1.0f) {
|
||||
phaseDelta += 1.0f;
|
||||
offset--;
|
||||
}
|
||||
while (phaseDelta >= 1.0f) {
|
||||
phaseDelta -= 1.0f;
|
||||
offset++;
|
||||
}
|
||||
|
||||
// Update the period (TODO: Clamp error*omegaGain to prevent weird shit with corrupt samples)
|
||||
period += (int32_t)(periodDelta * (float)(1 << 30));
|
||||
period = std::clamp<uint32_t>(period, minPeriod, maxPeriod);
|
||||
|
||||
// Update the phase
|
||||
phase += (int32_t)(phaseDelta * (float)(1 << 30));
|
||||
|
||||
// Normalize the phase
|
||||
uint32_t overflow = phase >> 30;
|
||||
if (overflow) {
|
||||
if (error < 0) {
|
||||
offset -= 4 - overflow;
|
||||
}
|
||||
else {
|
||||
offset += overflow;
|
||||
}
|
||||
}
|
||||
phase &= 0x3FFFFFFF;
|
||||
|
||||
// Find the lowest value
|
||||
float lowest = INFINITY;
|
||||
int lowestId = -1;
|
||||
for (int i = 0; i < LINE_SIZE; i++) {
|
||||
float val = base_type::out.writeBuf[i];
|
||||
if (val < lowest) {
|
||||
lowest = val;
|
||||
lowestId = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Check the the line is in lock
|
||||
bool lineLocked = (lowestId < SYNC_R_END || lowestId >= SYNC_L_START);
|
||||
|
||||
// Update the lock status based on the line lock
|
||||
if (!lineLocked && locked) {
|
||||
locked--;
|
||||
}
|
||||
else if (lineLocked && locked < MAX_LOCK) {
|
||||
locked++;
|
||||
}
|
||||
|
||||
// If not locked, attempt to lock by forcing the sync to happen at the right spot
|
||||
// TODO: This triggers waaaay too easily at low SNR
|
||||
if (!locked && fastLock) {
|
||||
offset += lowestId - SYNC_R_START;
|
||||
locked = MAX_LOCK / 2;
|
||||
if (++counter >= 100) {
|
||||
counter = 0;
|
||||
//flog::warn("Left: {}, Right: {}, Error: {}, Freq: {}, Phase: {}", left, right, error, pcl.freq, pcl.phase);
|
||||
}
|
||||
|
||||
// Output line
|
||||
if (!base_type::out.swap(LINE_SIZE)) { break; }
|
||||
pixel = 0;
|
||||
if (!base_type::out.swap(outCount)) { break; }
|
||||
outCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the offset ready for the next buffer
|
||||
// Advance symbol offset and phase
|
||||
pcl.advance(error);
|
||||
float delta = floorf(pcl.phase);
|
||||
offset += delta;
|
||||
pcl.phase -= delta;
|
||||
}
|
||||
offset -= count;
|
||||
|
||||
// Update delay buffer
|
||||
@ -216,15 +155,16 @@ public:
|
||||
|
||||
// Swap if some data was generated
|
||||
base_type::_in->flush();
|
||||
return 0;
|
||||
return outCount;
|
||||
}
|
||||
|
||||
float syncBias = 0;
|
||||
bool locked = false;
|
||||
bool test2 = false;
|
||||
|
||||
uint32_t period = (0x800072F3 >> 1);//(1 << 31) + 1;
|
||||
float syncBias = 0.0f;
|
||||
bool forceLock = false;
|
||||
|
||||
int locked = 0;
|
||||
bool fastLock = true;
|
||||
int counter = 0;
|
||||
|
||||
protected:
|
||||
void generateInterpTaps() {
|
||||
@ -235,6 +175,7 @@ protected:
|
||||
}
|
||||
|
||||
dsp::multirate::PolyphaseBank<float> interpBank;
|
||||
dsp::loop::PhaseControlLoop<double, false> pcl;
|
||||
|
||||
double _omega;
|
||||
double _omegaGain;
|
||||
@ -242,14 +183,11 @@ protected:
|
||||
double _omegaRelLimit;
|
||||
int _interpPhaseCount;
|
||||
int _interpTapCount;
|
||||
float* buffer;
|
||||
float* bufStart;
|
||||
|
||||
uint32_t phase = 0;
|
||||
uint32_t maxPeriod;
|
||||
uint32_t minPeriod;
|
||||
float syncLevel = -0.03f;
|
||||
|
||||
int offset = 0;
|
||||
int pixel = 0;
|
||||
int outCount = 0;
|
||||
float* buffer;
|
||||
float* bufStart;
|
||||
|
||||
float syncLevel = -0.03f;
|
||||
};
|
@ -16,13 +16,9 @@
|
||||
#include <dsp/filter/fir.h>
|
||||
#include <dsp/taps/from_array.h>
|
||||
|
||||
#include "amplitude.h"
|
||||
#include <dsp/demod/am.h>
|
||||
#include <dsp/loop/fast_agc.h>
|
||||
#include "chrominance_filter.h"
|
||||
|
||||
#include "filters.h"
|
||||
#include <dsp/math/normalize_phase.h>
|
||||
#include <fstream>
|
||||
#include "chroma_pll.h"
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
@ -33,26 +29,24 @@ SDRPP_MOD_INFO{/* Name: */ "atv_decoder",
|
||||
/* Max instances */ -1
|
||||
};
|
||||
|
||||
#define SAMPLE_RATE (625.0f * (float)LINE_SIZE * 25.0f)
|
||||
#define SAMPLE_RATE (625.0f * 720.0f * 25.0f)
|
||||
|
||||
class ATVDecoderModule : public ModuleManager::Instance {
|
||||
public:
|
||||
ATVDecoderModule(std::string name) : img(768, 576) {
|
||||
ATVDecoderModule(std::string name) : img(720, 625) {
|
||||
this->name = name;
|
||||
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 7000000.0f, SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE, true);
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, 8000000.0f, SAMPLE_RATE, SAMPLE_RATE, SAMPLE_RATE, true);
|
||||
|
||||
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);
|
||||
sink.init(&sync.out, handler, this);
|
||||
|
||||
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));
|
||||
|
||||
file = std::ofstream("chromasub_diff.bin", std::ios::binary | std::ios::out);
|
||||
|
||||
agc.start();
|
||||
demod.start();
|
||||
sync.start();
|
||||
sink.start();
|
||||
@ -64,10 +58,7 @@ class ATVDecoderModule : public ModuleManager::Instance {
|
||||
if (vfo) {
|
||||
sigpath::vfoManager.deleteVFO(vfo);
|
||||
}
|
||||
agc.stop();
|
||||
demod.stop();
|
||||
sync.stop();
|
||||
sink.stop();
|
||||
gui::menu.removeEntry(name);
|
||||
}
|
||||
|
||||
@ -83,8 +74,6 @@ class ATVDecoderModule : public ModuleManager::Instance {
|
||||
|
||||
bool isEnabled() { return enabled; }
|
||||
|
||||
std::ofstream file;
|
||||
|
||||
private:
|
||||
static void menuHandler(void *ctx) {
|
||||
ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
|
||||
@ -93,215 +82,142 @@ class ATVDecoderModule : public ModuleManager::Instance {
|
||||
style::beginDisabled();
|
||||
}
|
||||
|
||||
// Ideal width for testing: 750pixels
|
||||
|
||||
ImGui::FillWidth();
|
||||
_this->img.draw();
|
||||
|
||||
ImGui::TextUnformatted("Horizontal Sync:");
|
||||
ImGui::SameLine();
|
||||
if (_this->sync.locked > 750) {
|
||||
ImGui::LeftLabel("Sync");
|
||||
ImGui::FillWidth();
|
||||
ImGui::SliderFloat("##syncLvl", &_this->sync_level, -2, 2);
|
||||
|
||||
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");
|
||||
}
|
||||
else {
|
||||
ImGui::TextUnformatted("Not locked");
|
||||
}
|
||||
|
||||
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);
|
||||
ImGui::Checkbox("Force Lock", &_this->sync.forceLock);
|
||||
|
||||
if (!_this->enabled) {
|
||||
style::endDisabled();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Close Debug")) {
|
||||
_this->file.close();
|
||||
}
|
||||
|
||||
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) {
|
||||
ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
|
||||
|
||||
// Correct the offset
|
||||
volk_32f_s32f_add_32f(data, data, _this->offset, count);
|
||||
// Convert line to complex
|
||||
_this->r2c.process(720, data, _this->r2c.out.writeBuf);
|
||||
|
||||
// Correct the gain
|
||||
volk_32f_s32f_multiply_32f(data, data, _this->gain, count);
|
||||
// Isolate the chroma subcarrier
|
||||
_this->fir.process(720, _this->r2c.out.writeBuf, _this->fir.out.writeBuf);
|
||||
|
||||
// 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
|
||||
// Run chroma carrier through the PLL
|
||||
_this->pll.process(720, _this->fir.out.writeBuf, _this->pll.out.writeBuf, ((_this->ypos%2)==1) ^ _this->evenFrame);
|
||||
|
||||
// Compute the blanking level
|
||||
float blankLevel = 0.0f;
|
||||
volk_32f_accumulator_s32f(&blankLevel, &data[HBLANK_START], HBLANK_LEN);
|
||||
blankLevel /= (float)HBLANK_LEN;
|
||||
// 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) ];
|
||||
|
||||
// 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);
|
||||
//uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[_this->ypos*720];
|
||||
|
||||
// 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;
|
||||
for (int i = 0; i < count; i++) {
|
||||
int imval = std::clamp<float>((data[i] - _this->minLvl) * 255.0 / _this->spanLvl, 0, 255);
|
||||
// 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);
|
||||
// currentLine[i] = 0xFF000000 | (im << 8) | re;
|
||||
currentLine[i] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Vertical scan logic
|
||||
_this->ypos++;
|
||||
bool rollover = _this->ypos >= 625;
|
||||
if (rollover) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
|
||||
_this->evenFrame = !_this->evenFrame;
|
||||
}
|
||||
else {
|
||||
for (int i = 155; i < (155+768); i++) {
|
||||
int imval = std::clamp<float>(data[i-155+COLORBURST_START] * 255.0f, 0, 255);
|
||||
currentLine[i-155] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute whether to rollover
|
||||
bool rollToOdd = (_this->ypos == 624);
|
||||
bool rollToEven = (_this->ypos == 623);
|
||||
|
||||
// Compute the field sync
|
||||
bool syncToOdd = (_this->syncHistory == 0b0101011010010101);
|
||||
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->line = 0;
|
||||
|
||||
// Swap the video buffer
|
||||
_this->img.swap();
|
||||
}
|
||||
else {
|
||||
_this->ypos += 2;
|
||||
_this->line++;
|
||||
|
||||
// Measure vsync levels
|
||||
float sync0 = 0.0f, sync1 = 0.0f;
|
||||
for (int i = 0; i < 306; i++) {
|
||||
sync0 += data[i];
|
||||
}
|
||||
for (int i = (720/2); i < ((720/2)+306); i++) {
|
||||
sync1 += data[i];
|
||||
}
|
||||
sync0 *= (1.0f/305.0f);
|
||||
sync1 *= (1.0f/305.0f);
|
||||
|
||||
// Save sync detection to history
|
||||
_this->syncHistory >>= 2;
|
||||
_this->syncHistory |= (((uint16_t)(sync1 < _this->sync_level)) << 9) | (((uint16_t)(sync0 < _this->sync_level)) << 8);
|
||||
|
||||
// Trigger vsync in case one is detected
|
||||
// TODO: Also sync with odd field
|
||||
if (!rollover && _this->syncHistory == 0b0000011111) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
|
||||
_this->evenFrame = !_this->evenFrame;
|
||||
}
|
||||
_this->ypos = 0;
|
||||
_this->img.swap();
|
||||
}
|
||||
}
|
||||
|
||||
// NEW SYNC:
|
||||
float offset = 0.0f;
|
||||
float gain = 1.0f;
|
||||
uint16_t syncHistory = 0;
|
||||
int line = 0;
|
||||
int ypos = 0;
|
||||
int vlock = 0;
|
||||
float subcarrierFreq = 0.0f;
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
|
||||
VFOManager::VFO *vfo = NULL;
|
||||
//dsp::demod::Quadrature demod;
|
||||
dsp::loop::FastAGC<dsp::complex_t> agc;
|
||||
dsp::demod::Amplitude demod;
|
||||
//dsp::demod::AM<float> demod;
|
||||
dsp::demod::Quadrature demod;
|
||||
LineSync sync;
|
||||
dsp::sink::Handler<float> sink;
|
||||
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 colorMode = false;
|
||||
bool evenFrame = 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;
|
||||
};
|
||||
|
@ -1,37 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(dab_decoder)
|
||||
|
||||
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
||||
|
||||
include(${SDRPP_MODULE_CMAKE})
|
||||
|
||||
target_include_directories(dab_decoder PRIVATE "src/")
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_include_directories(dab_decoder PRIVATE "C:/Program Files/codec2/include/")
|
||||
target_link_directories(dab_decoder PRIVATE "C:/Program Files/codec2/lib")
|
||||
|
||||
target_link_libraries(dab_decoder PRIVATE libcodec2)
|
||||
elseif (ANDROID)
|
||||
target_include_directories(dab_decoder PUBLIC
|
||||
/sdr-kit/${ANDROID_ABI}/include/codec2
|
||||
)
|
||||
|
||||
target_link_libraries(dab_decoder PUBLIC
|
||||
/sdr-kit/${ANDROID_ABI}/lib/libcodec2.so
|
||||
)
|
||||
else ()
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(LIBCODEC2 REQUIRED codec2)
|
||||
|
||||
target_include_directories(dab_decoder PRIVATE ${LIBCODEC2_INCLUDE_DIRS})
|
||||
target_link_directories(dab_decoder PRIVATE ${LIBCODEC2_LIBRARY_DIRS})
|
||||
target_link_libraries(dab_decoder PRIVATE ${LIBCODEC2_LIBRARIES})
|
||||
|
||||
# Include it because for some reason pkgconfig doesn't look here?
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
target_include_directories(dab_decoder PRIVATE "/usr/local/include")
|
||||
endif()
|
||||
endif ()
|
@ -1,280 +0,0 @@
|
||||
#pragma once
|
||||
#include <dsp/processor.h>
|
||||
#include <utils/flog.h>
|
||||
#include <fftw3.h>
|
||||
#include "dab_phase_sym.h"
|
||||
|
||||
namespace dab {
|
||||
class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||
public:
|
||||
CyclicSync() {}
|
||||
|
||||
// TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
|
||||
CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
|
||||
|
||||
void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
|
||||
// Computer the number of samples for the symbol and its cyclic prefix
|
||||
symbolSamps = round(samplerate * symbolLength);
|
||||
prefixSamps = round(samplerate * cyclicPrefixLength);
|
||||
|
||||
// Allocate and clear the delay buffer
|
||||
delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
|
||||
dsp::buffer::clear(delayBuf, symbolSamps);
|
||||
|
||||
// Allocate and clear the history buffer
|
||||
histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
|
||||
dsp::buffer::clear(histBuf, prefixSamps);
|
||||
|
||||
// Compute the delay input addresses
|
||||
delayBufInput = &delayBuf[symbolSamps];
|
||||
|
||||
// Compute the correlation AGC configuration
|
||||
this->agcRate = agcRate;
|
||||
agcRateInv = 1.0f - agcRate;
|
||||
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
base_type::tempStop();
|
||||
|
||||
base_type::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
// Copy the data into the normal delay buffer
|
||||
memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
|
||||
|
||||
// Flush the input stream
|
||||
base_type::_in->flush();
|
||||
|
||||
// Do cross-correlation
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Get the current history slot
|
||||
dsp::complex_t* slot = &histBuf[histId++];
|
||||
|
||||
// Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
|
||||
histId %= prefixSamps;
|
||||
|
||||
// Kick out last value from the correlation
|
||||
corr -= *slot;
|
||||
|
||||
// Save input value and compute the new prodct
|
||||
dsp::complex_t val = delayBuf[i];
|
||||
dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
|
||||
|
||||
// Add the new value to the correlation
|
||||
*slot = prod;
|
||||
|
||||
// Add the new value to the history buffer
|
||||
corr += prod;
|
||||
|
||||
// Compute sample amplitude
|
||||
float rcorr = corr.amplitude();
|
||||
|
||||
// If a high enough peak is reached, reset the symbol counter
|
||||
if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
|
||||
peakCorr = rcorr;
|
||||
peakLCorr = lastCorr;
|
||||
samplesSincePeak = 0;
|
||||
}
|
||||
|
||||
// If this is the sample right after the peak, save it
|
||||
if (samplesSincePeak == 1) {
|
||||
peakRCorr = rcorr;
|
||||
}
|
||||
|
||||
// Write the sample to the output
|
||||
out.writeBuf[samplesSincePeak++] = val;
|
||||
|
||||
// If the end of the symbol is reached, send it off
|
||||
if (samplesSincePeak >= symbolSamps) {
|
||||
if (!out.swap(symbolSamps)) {
|
||||
return -1;
|
||||
}
|
||||
samplesSincePeak = 0;
|
||||
peakCorr = 0;
|
||||
}
|
||||
|
||||
// Update the average correlation
|
||||
lastCorr = rcorr;
|
||||
|
||||
// Update the average correlation value
|
||||
avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
|
||||
}
|
||||
|
||||
// Move unused data
|
||||
memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
protected:
|
||||
int symbolSamps;
|
||||
int prefixSamps;
|
||||
|
||||
int histId = 0;
|
||||
dsp::complex_t* histBuf;
|
||||
|
||||
dsp::complex_t* delayBuf;
|
||||
dsp::complex_t* delayBufInput;
|
||||
|
||||
dsp::complex_t corr = { 0.0f, 0.0f };
|
||||
|
||||
int samplesSincePeak = 0;
|
||||
float lastCorr = 0.0f;
|
||||
float peakCorr = 0.0f;
|
||||
float peakLCorr = 0.0f;
|
||||
float peakRCorr = 0.0f;
|
||||
|
||||
// Note only required for DAB
|
||||
float avgCorr = 0.0f;
|
||||
float agcRate;
|
||||
float agcRateInv;
|
||||
};
|
||||
|
||||
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||
public:
|
||||
FrameFreqSync() {}
|
||||
|
||||
FrameFreqSync(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) { init(in, agcRate); }
|
||||
|
||||
void init(dsp::stream<dsp::complex_t>* in, float agcRate = 0.01f) {
|
||||
// Allocate buffers
|
||||
amps = dsp::buffer::alloc<float>(2048);
|
||||
conjRef = dsp::buffer::alloc<dsp::complex_t>(2048);
|
||||
corrIn = (dsp::complex_t*)fftwf_alloc_complex(2048);
|
||||
corrOut = (dsp::complex_t*)fftwf_alloc_complex(2048);
|
||||
|
||||
// Copy the phase reference
|
||||
memcpy(conjRef, DAB_PHASE_SYM_CONJ, 2048 * sizeof(dsp::complex_t));
|
||||
|
||||
// Plan the FFT computation
|
||||
plan = fftwf_plan_dft_1d(2048, (fftwf_complex*)corrIn, (fftwf_complex*)corrOut, FFTW_FORWARD, FFTW_ESTIMATE);
|
||||
|
||||
// Compute the correlation AGC configuration
|
||||
this->agcRate = agcRate;
|
||||
agcRateInv = 1.0f - agcRate;
|
||||
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
assert(base_type::_block_init);
|
||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||
base_type::tempStop();
|
||||
|
||||
base_type::tempStart();
|
||||
}
|
||||
|
||||
int run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
// Apply frequency shift
|
||||
lv_32fc_t phase = lv_cmake(1.0f, 0.0f);
|
||||
lv_32fc_t phaseDelta = lv_cmake(cos(offset), sin(offset));
|
||||
#if VOLK_VERSION >= 030100
|
||||
volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||
#else
|
||||
volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)_in->readBuf, (lv_32fc_t*)_in->readBuf, phaseDelta, &phase, count);
|
||||
#endif
|
||||
|
||||
// Compute the amplitude amplitude of all samples
|
||||
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)_in->readBuf, 2048);
|
||||
|
||||
// Compute the average signal level by adding up all values
|
||||
float level = 0.0f;
|
||||
volk_32f_accumulator_s32f(&level, amps, 2048);
|
||||
|
||||
// Detect a frame sync condition
|
||||
if (level < avgLvl * 0.5f) {
|
||||
// Reset symbol counter
|
||||
sym = 1;
|
||||
|
||||
// Update the average level
|
||||
avgLvl = agcRate*level + agcRateInv*avgLvl;
|
||||
|
||||
// Flush the input stream and return
|
||||
base_type::_in->flush();
|
||||
return count;
|
||||
}
|
||||
|
||||
// Update the average level
|
||||
avgLvl = agcRate*level + agcRateInv*avgLvl;
|
||||
|
||||
// Handle phase reference
|
||||
if (sym == 1) {
|
||||
// Output the symbols (DEBUG ONLY)
|
||||
memcpy(corrIn, _in->readBuf, 2048 * sizeof(dsp::complex_t));
|
||||
fftwf_execute(plan);
|
||||
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
|
||||
int outCount = 0;
|
||||
dsp::complex_t pi4 = { cos(3.1415926535*0.25), sin(3.1415926535*0.25) };
|
||||
for (int i = -767; i < 768; i++) {
|
||||
if (!i) { continue; }
|
||||
int cid0 = ((i-1) >= 0) ? (i-1) : 2048+(i-1);
|
||||
int cid1 = (i >= 0) ? i : 2048+i;;
|
||||
out.writeBuf[outCount++] = pi4 * (corrOut[cid1] * corrOut[cid0].conj()) * (1.0f/(amps[cid0]*amps[cid0]));
|
||||
}
|
||||
out.swap(outCount);
|
||||
|
||||
// Multiply the samples with the conjugated phase reference signal
|
||||
volk_32fc_x2_multiply_32fc((lv_32fc_t*)corrIn, (lv_32fc_t*)_in->readBuf, (lv_32fc_t*)conjRef, 2048);
|
||||
|
||||
// Compute the FFT of the product
|
||||
fftwf_execute(plan);
|
||||
|
||||
// Compute the amplitude of the bins
|
||||
volk_32fc_magnitude_32f(amps, (lv_32fc_t*)corrOut, 2048);
|
||||
|
||||
// Locate highest power bin
|
||||
uint32_t peakId;
|
||||
volk_32f_index_max_32u(&peakId, amps, 2048);
|
||||
|
||||
// Obtain the value of the bins next to the peak
|
||||
float peakL = amps[(peakId + 2047) % 2048];
|
||||
float peakR = amps[(peakId + 1) % 2048];
|
||||
|
||||
// Compute the integer frequency offset
|
||||
float offInt = (peakId < 1024) ? (float)peakId : ((float)peakId - 2048.0f);
|
||||
|
||||
// Compute the frequency offset in rad/samp
|
||||
float off = 3.1415926535f * (offInt + ((peakR - peakL) / (peakR + peakL))) * (1.0f / 1024.0f);
|
||||
|
||||
// Run control loop
|
||||
offset -= 0.1f*off;
|
||||
flog::debug("Offset: {} Hz, Error: {} Hz, Avg Level: {}", offset * (0.5f/3.1415926535f)*2.048e6, off * (0.5f/3.1415926535f)*2.048e6, avgLvl);
|
||||
}
|
||||
|
||||
// Increment the symbol counter
|
||||
sym++;
|
||||
|
||||
// Flush the input stream and return
|
||||
base_type::_in->flush();
|
||||
return count;
|
||||
}
|
||||
|
||||
protected:
|
||||
fftwf_plan plan;
|
||||
|
||||
float* amps;
|
||||
dsp::complex_t* conjRef;
|
||||
dsp::complex_t* corrIn;
|
||||
dsp::complex_t* corrOut;
|
||||
|
||||
int sym;
|
||||
float offset = 0.0f;
|
||||
|
||||
float avgLvl = 0.0f;
|
||||
float agcRate;
|
||||
float agcRateInv;
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,163 +0,0 @@
|
||||
#include <imgui.h>
|
||||
#include <config.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <module.h>
|
||||
#include <filesystem>
|
||||
#include <dsp/stream.h>
|
||||
#include <dsp/buffer/reshaper.h>
|
||||
#include <dsp/multirate/rational_resampler.h>
|
||||
#include <dsp/sink/handler_sink.h>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include "dab_dsp.h"
|
||||
#include <gui/widgets/constellation_diagram.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "dab_decoder",
|
||||
/* Description: */ "DAB/DAB+ Decoder for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ -1
|
||||
};
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
#define INPUT_SAMPLE_RATE 2.048e6
|
||||
#define VFO_BANDWIDTH 1.6e6
|
||||
|
||||
class M17DecoderModule : public ModuleManager::Instance {
|
||||
public:
|
||||
M17DecoderModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary);
|
||||
|
||||
// Load config
|
||||
config.acquire();
|
||||
|
||||
config.release(true);
|
||||
|
||||
// Initialize VFO
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
|
||||
vfo->setSnapInterval(250);
|
||||
|
||||
// Initialize DSP here
|
||||
csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE);
|
||||
ffsync.init(&csync.out);
|
||||
ns.init(&ffsync.out, handler, this);
|
||||
|
||||
// Start DSO Here
|
||||
csync.start();
|
||||
ffsync.start();
|
||||
ns.start();
|
||||
|
||||
gui::menu.registerEntry(name, menuHandler, this, this);
|
||||
}
|
||||
|
||||
~M17DecoderModule() {
|
||||
gui::menu.removeEntry(name);
|
||||
// Stop DSP Here
|
||||
if (enabled) {
|
||||
csync.stop();
|
||||
ffsync.stop();
|
||||
ns.stop();
|
||||
sigpath::vfoManager.deleteVFO(vfo);
|
||||
}
|
||||
|
||||
sigpath::sinkManager.unregisterStream(name);
|
||||
}
|
||||
|
||||
void postInit() {}
|
||||
|
||||
void enable() {
|
||||
double bw = gui::waterfall.getBandwidth();
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), VFO_BANDWIDTH, INPUT_SAMPLE_RATE, VFO_BANDWIDTH, VFO_BANDWIDTH, true);
|
||||
vfo->setSnapInterval(250);
|
||||
|
||||
// Set Input of demod here
|
||||
csync.setInput(vfo->output);
|
||||
|
||||
// Start DSP here
|
||||
csync.start();
|
||||
ffsync.start();
|
||||
ns.start();
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
// Stop DSP here
|
||||
csync.stop();
|
||||
ffsync.stop();
|
||||
ns.stop();
|
||||
|
||||
sigpath::vfoManager.deleteVFO(vfo);
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
static void menuHandler(void* ctx) {
|
||||
M17DecoderModule* _this = (M17DecoderModule*)ctx;
|
||||
|
||||
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||
|
||||
if (!_this->enabled) { style::beginDisabled(); }
|
||||
|
||||
_this->constDiagram.draw();
|
||||
|
||||
if (!_this->enabled) { style::endDisabled(); }
|
||||
}
|
||||
|
||||
std::ofstream file;
|
||||
|
||||
static void handler(dsp::complex_t* data, int count, void* ctx) {
|
||||
M17DecoderModule* _this = (M17DecoderModule*)ctx;
|
||||
//_this->file.write((char*)data, count * sizeof(dsp::complex_t));
|
||||
|
||||
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
|
||||
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
|
||||
_this->constDiagram.releaseBuffer();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
|
||||
dab::CyclicSync csync;
|
||||
dab::FrameFreqSync ffsync;
|
||||
dsp::sink::Handler<dsp::complex_t> ns;
|
||||
|
||||
ImGui::ConstellationDiagram constDiagram;
|
||||
|
||||
// DSP Chain
|
||||
VFOManager::VFO* vfo;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
// Create default recording directory
|
||||
json def = json({});
|
||||
config.setPath(core::args["root"].s() + "/dab_decoder_config.json");
|
||||
config.load(def);
|
||||
config.enableAutoSave();
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new M17DecoderModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
delete (M17DecoderModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
0123456789
|
||||
--- ---
|
||||
|
||||
0*4
|
||||
1*5
|
||||
2*6
|
||||
|
||||
1*5
|
||||
2*6 = L + 3*7 - 0*4
|
||||
3*7
|
||||
|
||||
2*6
|
||||
3*7 = L + 4*8 - 1*5
|
||||
4*8
|
||||
|
||||
3*7
|
||||
4*8 = L + 5*9 - 2*6
|
||||
5*9
|
||||
|
||||
|
||||
|
||||
0*5
|
||||
1*6
|
||||
2*7
|
||||
|
||||
1*6
|
||||
2*7
|
||||
3*8
|
||||
|
||||
2*7
|
||||
3*8
|
||||
4*9
|
||||
|
||||
=> Use same technique to cache the interpolation results
|
@ -8,15 +8,15 @@
|
||||
#include "dsp.h"
|
||||
#include "pocsag.h"
|
||||
|
||||
#define BAUDRATE 2400
|
||||
#define SAMPLERATE (BAUDRATE*10)
|
||||
|
||||
class POCSAGDecoder : public Decoder {
|
||||
public:
|
||||
POCSAGDecoder(const std::string& name, VFOManager::VFO* vfo) : diag(0.6, BAUDRATE) {
|
||||
POCSAGDecoder(const std::string& name, VFOManager::VFO* vfo) : diag(0.6, 2400) {
|
||||
this->name = name;
|
||||
this->vfo = vfo;
|
||||
|
||||
// Default baudrate (TODO: Load from config)
|
||||
baudrate = 2400;
|
||||
|
||||
// Define baudrate options
|
||||
baudrates.define(512, "512 Baud", 512);
|
||||
baudrates.define(1200, "1200 Baud", 1200);
|
||||
@ -24,9 +24,9 @@ public:
|
||||
|
||||
// Init DSP
|
||||
vfo->setBandwidthLimits(12500, 12500, true);
|
||||
vfo->setSampleRate(SAMPLERATE, 12500);
|
||||
dsp.init(vfo->output, SAMPLERATE, BAUDRATE);
|
||||
reshape.init(&dsp.soft, BAUDRATE, (BAUDRATE / 30.0) - BAUDRATE);
|
||||
vfo->setSampleRate(baudrate*10.0, 12500);
|
||||
dsp.init(vfo->output, baudrate*10.0, baudrate);
|
||||
reshape.init(&dsp.soft, baudrate, (baudrate / 30.0) - baudrate);
|
||||
dataHandler.init(&dsp.out, _dataHandler, this);
|
||||
diagHandler.init(&reshape.out, _diagHandler, this);
|
||||
|
||||
@ -42,7 +42,7 @@ public:
|
||||
ImGui::LeftLabel("Baudrate");
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::Combo(("##pager_decoder_pocsag_br_" + name).c_str(), &brId, baudrates.txt)) {
|
||||
// TODO
|
||||
setBaudrate(baudrates.value(brId));
|
||||
}
|
||||
|
||||
ImGui::FillWidth();
|
||||
@ -79,7 +79,8 @@ private:
|
||||
static void _diagHandler(float* data, int count, void* ctx) {
|
||||
POCSAGDecoder* _this = (POCSAGDecoder*)ctx;
|
||||
float* buf = _this->diag.acquireBuffer();
|
||||
memcpy(buf, data, count * sizeof(float));
|
||||
int maxCount = std::min<int>(count, _this->diag.getCount());
|
||||
memcpy(buf, data, maxCount * sizeof(float));
|
||||
_this->diag.releaseBuffer();
|
||||
}
|
||||
|
||||
@ -87,6 +88,15 @@ private:
|
||||
flog::debug("[{}]: '{}'", (uint32_t)addr, msg);
|
||||
}
|
||||
|
||||
void setBaudrate(double baudrate) {
|
||||
vfo->setSampleRate(baudrate*10.0, 12500);
|
||||
stop();
|
||||
reshape.setKeep(baudrate);
|
||||
reshape.setSkip((baudrate / 30.0) - baudrate);
|
||||
diag.setCount(baudrate);
|
||||
start();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
VFOManager::VFO* vfo;
|
||||
|
||||
@ -100,6 +110,7 @@ private:
|
||||
ImGui::SymbolDiagram diag;
|
||||
|
||||
int brId = 2;
|
||||
double baudrate = 2400;
|
||||
|
||||
OptionList<int, int> baudrates;
|
||||
};
|
@ -1,8 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(ryfi_decoder)
|
||||
|
||||
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
|
||||
|
||||
include(${SDRPP_MODULE_CMAKE})
|
||||
|
||||
target_include_directories(ryfi_decoder PRIVATE "src/")
|
@ -1,139 +0,0 @@
|
||||
#include <imgui.h>
|
||||
#include <config.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <module.h>
|
||||
#include <filesystem>
|
||||
#include <dsp/routing/splitter.h>
|
||||
#include <dsp/buffer/reshaper.h>
|
||||
#include <dsp/sink/handler_sink.h>
|
||||
#include <gui/widgets/folder_select.h>
|
||||
#include <gui/widgets/constellation_diagram.h>
|
||||
#include "ryfi/receiver.h"
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "ryfi_decoder",
|
||||
/* Description: */ "RyFi decoder for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ -1
|
||||
};
|
||||
|
||||
#define INPUT_BANDWIDTH 600e3
|
||||
#define INPUT_SAMPLE_RATE 1000e3
|
||||
#define INPUT_BAUDRATE 500e3
|
||||
|
||||
#define SYMBOL_DIAG_RATE 30
|
||||
#define SYMBOL_DIAG_COUNT 1024
|
||||
|
||||
class RyFiDecoderModule : public ModuleManager::Instance {
|
||||
public:
|
||||
RyFiDecoderModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, 0, INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
|
||||
rx.init(vfo->output, INPUT_BAUDRATE, INPUT_SAMPLE_RATE);
|
||||
reshape.init(rx.softOut, SYMBOL_DIAG_COUNT, (INPUT_BAUDRATE / SYMBOL_DIAG_RATE) - SYMBOL_DIAG_COUNT);
|
||||
symSink.init(&reshape.out, symSinkHandler, this);
|
||||
rx.onPacket.bind(&RyFiDecoderModule::packetHandler, this);
|
||||
|
||||
rx.start();
|
||||
reshape.start();
|
||||
symSink.start();
|
||||
|
||||
gui::menu.registerEntry(name, menuHandler, this, this);
|
||||
}
|
||||
|
||||
~RyFiDecoderModule() {
|
||||
rx.stop();
|
||||
reshape.stop();
|
||||
symSink.stop();
|
||||
sigpath::vfoManager.deleteVFO(vfo);
|
||||
gui::menu.removeEntry(name);
|
||||
}
|
||||
|
||||
void postInit() {}
|
||||
|
||||
void enable() {
|
||||
double bw = gui::waterfall.getBandwidth();
|
||||
vfo = sigpath::vfoManager.createVFO(name, ImGui::WaterfallVFO::REF_CENTER, std::clamp<double>(0, -bw / 2.0, bw / 2.0), INPUT_BANDWIDTH, INPUT_SAMPLE_RATE, INPUT_BANDWIDTH, INPUT_BANDWIDTH, true);
|
||||
|
||||
rx.setInput(vfo->output);
|
||||
|
||||
rx.start();
|
||||
reshape.start();
|
||||
symSink.start();
|
||||
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
rx.stop();
|
||||
reshape.stop();
|
||||
symSink.stop();
|
||||
|
||||
sigpath::vfoManager.deleteVFO(vfo);
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
void packetHandler(ryfi::Packet pkt) {
|
||||
flog::debug("Got a {} byte packet!", pkt.size());
|
||||
}
|
||||
|
||||
static void menuHandler(void* ctx) {
|
||||
RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx;
|
||||
|
||||
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||
|
||||
if (!_this->enabled) { style::beginDisabled(); }
|
||||
|
||||
ImGui::SetNextItemWidth(menuWidth);
|
||||
_this->constDiagram.draw();
|
||||
|
||||
if (!_this->enabled) { style::endDisabled(); }
|
||||
}
|
||||
|
||||
static void symSinkHandler(dsp::complex_t* data, int count, void* ctx) {
|
||||
RyFiDecoderModule* _this = (RyFiDecoderModule*)ctx;
|
||||
|
||||
dsp::complex_t* buf = _this->constDiagram.acquireBuffer();
|
||||
memcpy(buf, data, 1024 * sizeof(dsp::complex_t));
|
||||
_this->constDiagram.releaseBuffer();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
|
||||
// DSP Chain
|
||||
VFOManager::VFO* vfo;
|
||||
ryfi::Receiver rx;
|
||||
dsp::buffer::Reshaper<dsp::complex_t> reshape;
|
||||
dsp::sink::Handler<dsp::complex_t> symSink;
|
||||
|
||||
ImGui::ConstellationDiagram constDiagram;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new RyFiDecoderModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
delete (RyFiDecoderModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
#include "conv_codec.h"
|
||||
|
||||
namespace ryfi {
|
||||
ConvEncoder::ConvEncoder(dsp::stream<uint8_t>* in) {
|
||||
// Create the convolutional encoder instance
|
||||
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
|
||||
|
||||
// Init the base class
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
ConvEncoder::~ConvEncoder() {
|
||||
// Destroy the convolutional encoder instance
|
||||
correct_convolutional_destroy(conv);
|
||||
}
|
||||
|
||||
int ConvEncoder::encode(const uint8_t* in, uint8_t* out, int count) {
|
||||
// Run convolutional encoder on the data
|
||||
return correct_convolutional_encode(conv, in, count, out);
|
||||
}
|
||||
|
||||
int ConvEncoder::run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||
|
||||
base_type::_in->flush();
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
ConvDecoder::ConvDecoder(dsp::stream<dsp::complex_t>* in) {
|
||||
// Create the convolutional encoder instance
|
||||
conv = correct_convolutional_create(2, 7, correct_conv_r12_7_polynomial);
|
||||
|
||||
// Allocate the soft symbol buffer
|
||||
soft = dsp::buffer::alloc<uint8_t>(STREAM_BUFFER_SIZE);
|
||||
|
||||
// Init the base class
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
ConvDecoder::~ConvDecoder() {
|
||||
// Destroy the convolutional encoder instance
|
||||
correct_convolutional_destroy(conv);
|
||||
|
||||
// Free the soft symbol buffer
|
||||
dsp::buffer::free(soft);
|
||||
}
|
||||
|
||||
int ConvDecoder::decode(const dsp::complex_t* in, uint8_t* out, int count) {
|
||||
// Convert to uint8
|
||||
const float* _in = (const float*)in;
|
||||
count *= 2;
|
||||
for (int i = 0; i < count; i++) {
|
||||
soft[i] = std::clamp<int>((_in[i] * 127.0f) + 128.0f, 0, 255);
|
||||
}
|
||||
|
||||
// Run convolutional decoder on the data
|
||||
return correct_convolutional_decode_soft(conv, soft, count, out);
|
||||
}
|
||||
|
||||
int ConvDecoder::run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||
|
||||
base_type::_in->flush();
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "dsp/processor.h"
|
||||
|
||||
extern "C" {
|
||||
#include "correct.h"
|
||||
}
|
||||
|
||||
namespace ryfi {
|
||||
/**
|
||||
* RyFi Convolutional Encoder.
|
||||
*/
|
||||
class ConvEncoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||
public:
|
||||
/**
|
||||
* Create a convolutional encoder specifying an input stream.
|
||||
* @param in Input stream.
|
||||
*/
|
||||
ConvEncoder(dsp::stream<uint8_t>* in = NULL);
|
||||
|
||||
// Destructor
|
||||
~ConvEncoder();
|
||||
|
||||
/**
|
||||
* Encode data.
|
||||
* @param in Input bytes.
|
||||
* @param out Output bits.
|
||||
* @param count Number of input bytes.
|
||||
* @return Number of output bits.
|
||||
*/
|
||||
int encode(const uint8_t* in, uint8_t* out, int count);
|
||||
|
||||
private:
|
||||
int run();
|
||||
|
||||
correct_convolutional* conv;
|
||||
};
|
||||
|
||||
/**
|
||||
* RyFi Convolutional Decoder.
|
||||
*/
|
||||
class ConvDecoder : public dsp::Processor<dsp::complex_t, uint8_t> {
|
||||
using base_type = dsp::Processor<dsp::complex_t, uint8_t>;
|
||||
public:
|
||||
/**
|
||||
* Create a convolutional encoder specifying an input stream.
|
||||
* @param in Input stream.
|
||||
*/
|
||||
ConvDecoder(dsp::stream<dsp::complex_t>* in = NULL);
|
||||
|
||||
// Destructor
|
||||
~ConvDecoder();
|
||||
|
||||
/**
|
||||
* Decode soft symbols.
|
||||
* @param in Input soft symbols.
|
||||
* @param out Output bytes.
|
||||
* @param count Number of input bytes.
|
||||
* @return Number of output bits.
|
||||
*/
|
||||
int decode(const dsp::complex_t* in, uint8_t* out, int count);
|
||||
|
||||
private:
|
||||
int run();
|
||||
|
||||
correct_convolutional* conv;
|
||||
uint8_t* soft = NULL;
|
||||
};
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#include "frame.h"
|
||||
|
||||
namespace ryfi {
|
||||
int Frame::serialize(uint8_t* bytes) const {
|
||||
// Write the counter
|
||||
bytes[0] = (counter >> 8) & 0xFF;
|
||||
bytes[1] = counter & 0xFF;
|
||||
|
||||
// Write the first packet pointer
|
||||
bytes[2] = (firstPacket >> 8) & 0xFF;
|
||||
bytes[3] = firstPacket & 0xFF;
|
||||
|
||||
// Write the last packet pointer
|
||||
bytes[4] = (lastPacket >> 8) & 0xFF;
|
||||
bytes[5] = lastPacket & 0xFF;
|
||||
|
||||
// Write the data
|
||||
memcpy(&bytes[6], content, FRAME_DATA_SIZE);
|
||||
|
||||
// Return the length of a serialized frame
|
||||
return FRAME_SIZE;
|
||||
}
|
||||
|
||||
void Frame::deserialize(const uint8_t* bytes, Frame& frame) {
|
||||
// Read the counter
|
||||
frame.counter = (((uint16_t)bytes[0]) << 8) | ((uint16_t)bytes[1]);
|
||||
|
||||
// Read the first packet pointer
|
||||
frame.firstPacket = (((uint16_t)bytes[2]) << 8) | ((uint16_t)bytes[3]);
|
||||
|
||||
// Read the last packet pointer
|
||||
frame.lastPacket = (((uint16_t)bytes[4]) << 8) | ((uint16_t)bytes[5]);
|
||||
|
||||
// Read the data
|
||||
memcpy(frame.content, &bytes[6], FRAME_DATA_SIZE);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include "rs_codec.h"
|
||||
|
||||
namespace ryfi {
|
||||
enum PacketOffset {
|
||||
PKT_OFFS_NONE = 0xFFFF
|
||||
};
|
||||
|
||||
struct Frame {
|
||||
/**
|
||||
* Serialize the frame to bytes.
|
||||
* @param bytes Buffer to write the serialized frame to.
|
||||
*/
|
||||
int serialize(uint8_t* bytes) const;
|
||||
|
||||
/**
|
||||
* Deserialize a frame from bytes.
|
||||
* @param bytes Buffer to deserialize the frame from.
|
||||
* @param frame Object that will contain the deserialize frame.
|
||||
*/
|
||||
static void deserialize(const uint8_t* bytes, Frame& frame);
|
||||
|
||||
// Size of a serialized frame
|
||||
static inline const int FRAME_SIZE = RS_BLOCK_DEC_SIZE*RS_BLOCK_COUNT;
|
||||
|
||||
// Size of the data area of the frame
|
||||
static inline const int FRAME_DATA_SIZE = FRAME_SIZE - 6;
|
||||
|
||||
// Steadily increasing counter.
|
||||
uint16_t counter = 0;
|
||||
|
||||
// Byte offset of the first packet in the frame.
|
||||
uint16_t firstPacket = 0;
|
||||
|
||||
// Byte offset of the last packet in the frame.
|
||||
uint16_t lastPacket = 0;
|
||||
|
||||
// Data area of the frame.
|
||||
uint8_t content[FRAME_DATA_SIZE];
|
||||
};
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
#include "framing.h"
|
||||
|
||||
namespace ryfi {
|
||||
dsp::complex_t QPSK_SYMBOLS[4] = {
|
||||
{ -0.070710678118f, -0.070710678118f },
|
||||
{ -0.070710678118f, 0.070710678118f },
|
||||
{ 0.070710678118f, -0.070710678118f },
|
||||
{ 0.070710678118f, 0.070710678118f },
|
||||
};
|
||||
|
||||
Framer::Framer(dsp::stream<uint8_t>* in) {
|
||||
// Generate the sync symbols
|
||||
int k = 0;
|
||||
for (int i = 62; i >= 0; i -= 2) {
|
||||
syncSyms[k++] = QPSK_SYMBOLS[(SYNC_WORD >> i) & 0b11];
|
||||
}
|
||||
|
||||
|
||||
// Initialize base class
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
int Framer::encode(const uint8_t* in, dsp::complex_t* out, int count) {
|
||||
// Copy sync symbols
|
||||
memcpy(out, syncSyms, SYNC_SYMS*sizeof(dsp::complex_t));
|
||||
|
||||
// Modulate the rest of the bits
|
||||
dsp::complex_t* dataOut = &out[SYNC_SYMS];
|
||||
int dataSyms = count / 2;
|
||||
for (int i = 0; i < dataSyms; i++) {
|
||||
uint8_t bits = (in[i >> 2] >> (6 - 2*(i & 0b11))) & 0b11;
|
||||
dataOut[i] = QPSK_SYMBOLS[bits];
|
||||
}
|
||||
|
||||
// Compute and return the total number of symbols
|
||||
return SYNC_SYMS + dataSyms;
|
||||
}
|
||||
|
||||
int Framer::run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||
|
||||
base_type::_in->flush();
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
Deframer::Deframer(dsp::stream<dsp::complex_t> *in) {
|
||||
// Compute sync word rotations
|
||||
// 0: 00 01 11 10
|
||||
// 90: 10 00 01 11
|
||||
// 180: 11 10 00 01
|
||||
// 270: 01 11 10 00
|
||||
|
||||
// For 0 and 180 it's the sync and its complement
|
||||
syncRots[ROT_0_DEG] = SYNC_WORD;
|
||||
syncRots[ROT_180_DEG] = ~SYNC_WORD;
|
||||
|
||||
// For 90 and 270 its the quadrature and its complement
|
||||
uint64_t quad;
|
||||
for (int i = 62; i >= 0; i -= 2) {
|
||||
// Get the symbol
|
||||
uint8_t sym = (SYNC_WORD >> i) & 0b11;
|
||||
|
||||
// Rotate it 90 degrees
|
||||
uint8_t rsym;
|
||||
switch (sym) {
|
||||
case 0b00: rsym = 0b10; break;
|
||||
case 0b01: rsym = 0b00; break;
|
||||
case 0b11: rsym = 0b01; break;
|
||||
case 0b10: rsym = 0b11; break;
|
||||
}
|
||||
|
||||
// Push it into the quadrature
|
||||
quad = (quad << 2) | rsym;
|
||||
}
|
||||
syncRots[ROT_90_DEG] = quad;
|
||||
syncRots[ROT_270_DEG] = ~quad;
|
||||
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
int Deframer::run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
dsp::complex_t* in = base_type::_in->readBuf;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (recv) {
|
||||
// Copy the symbol to the output and rotate it approprieate
|
||||
base_type::out.writeBuf[outCount++] = in[i] * symRot;
|
||||
|
||||
// Check if we're done receiving the frame, send it out
|
||||
if (!(--recv)) {
|
||||
if (!base_type::out.swap(outCount)) {
|
||||
base_type::_in->flush();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Get the raw symbol
|
||||
dsp::complex_t fsym = in[i];
|
||||
|
||||
// Decode the symbol
|
||||
uint8_t sym = ((fsym.re > 0) ? 0b10 : 0b00) | ((fsym.im > 0) ? 0b01 : 0b00);
|
||||
|
||||
// Push it to the shift register
|
||||
shift = (shift << 2) | sym;
|
||||
|
||||
// Find the rotation starting with the last known one
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// Get the test rotation
|
||||
int testRot = (knownRot+i) & 0b11;
|
||||
|
||||
// Check if the hamming distance is close enough
|
||||
int dist;
|
||||
if (distance(shift, syncRots[testRot]) < 6) {
|
||||
// Save the new rotation
|
||||
knownRot = testRot;
|
||||
|
||||
// Start reading in symbols for the frame
|
||||
symRot = symRots[knownRot];
|
||||
recv = 8168; // TODO: Don't hardcode!
|
||||
outCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base_type::_in->flush();
|
||||
return count;
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
#pragma once
|
||||
#include "dsp/processor.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace ryfi {
|
||||
// Synchronization word.
|
||||
inline const uint64_t SYNC_WORD = 0x341CC540819D8963;
|
||||
|
||||
// Number of synchronization bits.
|
||||
inline const int SYNC_BITS = 64;
|
||||
|
||||
// Number of synchronization symbols.
|
||||
inline const int SYNC_SYMS = SYNC_BITS / 2;
|
||||
|
||||
// Possible constellation rotations
|
||||
enum {
|
||||
ROT_0_DEG = 0,
|
||||
ROT_90_DEG = 1,
|
||||
ROT_180_DEG = 2,
|
||||
ROT_270_DEG = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* RyFi Framer.
|
||||
*/
|
||||
class Framer : public dsp::Processor<uint8_t, dsp::complex_t> {
|
||||
using base_type = dsp::Processor<uint8_t, dsp::complex_t>;
|
||||
public:
|
||||
/**
|
||||
* Create a framer specifying an input stream.
|
||||
* @param in Input stream.
|
||||
*/
|
||||
Framer(dsp::stream<uint8_t>* in = NULL);
|
||||
|
||||
/**
|
||||
* Encode a frame to symbols adding a sync word.
|
||||
*/
|
||||
int encode(const uint8_t* in, dsp::complex_t* out, int count);
|
||||
|
||||
private:
|
||||
int run();
|
||||
|
||||
dsp::complex_t syncSyms[SYNC_SYMS];
|
||||
};
|
||||
|
||||
class Deframer : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||
public:
|
||||
/**
|
||||
* Create a deframer specifying an input stream.
|
||||
* @param in Input stream.
|
||||
*/
|
||||
Deframer(dsp::stream<dsp::complex_t> *in = NULL);
|
||||
|
||||
private:
|
||||
int run();
|
||||
|
||||
inline static constexpr int distance(uint64_t a, uint64_t b) {
|
||||
int dist = 0;
|
||||
for (int i = 0; i < 64; i++) {
|
||||
dist += ((a & 1) != (b & 1));
|
||||
a >>= 1;
|
||||
b >>= 1;
|
||||
}
|
||||
return dist;
|
||||
}
|
||||
|
||||
// Frame reading counters
|
||||
int recv = 0;
|
||||
int outCount = 0;
|
||||
|
||||
// Rotation handling
|
||||
int knownRot = 0;
|
||||
uint64_t syncRots[4];
|
||||
dsp::complex_t symRot;
|
||||
const dsp::complex_t symRots[4] = {
|
||||
{ 1.0f, 0.0f }, // 0 deg
|
||||
{ 0.0f, -1.0f }, // 90 deg
|
||||
{ -1.0f, 0.0f }, // 180 deg
|
||||
{ 0.0f, 1.0f }, // 270 deg
|
||||
};
|
||||
|
||||
// Shift register
|
||||
uint64_t shift;
|
||||
};
|
||||
}
|
@ -1,126 +0,0 @@
|
||||
#include "packet.h"
|
||||
#include "string.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ryfi {
|
||||
Packet::Packet() {}
|
||||
|
||||
Packet::Packet(uint8_t* content, int size) {
|
||||
// Check that the size isn't too large
|
||||
if (size > MAX_CONTENT_SIZE) {
|
||||
throw std::runtime_error("Content size is too large to fit in a packet");
|
||||
}
|
||||
|
||||
|
||||
// Allocate the buffer
|
||||
allocate(size);
|
||||
|
||||
// Copy over the content
|
||||
memcpy(_content, content, size);
|
||||
}
|
||||
|
||||
Packet::Packet(const Packet& b) {
|
||||
// Reallocate the buffer
|
||||
allocate(b._size);
|
||||
|
||||
// Copy over the content
|
||||
memcpy(_content, b._content, b._size);
|
||||
}
|
||||
|
||||
Packet::Packet(Packet&& b) {
|
||||
// Move members
|
||||
_content = b._content;
|
||||
_size = b._size;
|
||||
|
||||
// Destroy old object
|
||||
b._content = NULL;
|
||||
b._size = 0;
|
||||
}
|
||||
|
||||
Packet::~Packet() {
|
||||
// Delete the content
|
||||
if (_content) { delete[] _content; }
|
||||
}
|
||||
|
||||
Packet& Packet::operator=(const Packet& b) {
|
||||
// Reallocate the buffer
|
||||
allocate(b._size);
|
||||
|
||||
// Copy over the content
|
||||
memcpy(_content, b._content, b._size);
|
||||
|
||||
// Return self
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packet& Packet::operator=(Packet&& b) {
|
||||
// Move members
|
||||
_content = b._content;
|
||||
_size = b._size;
|
||||
|
||||
// Destroy old object
|
||||
b._content = NULL;
|
||||
b._size = 0;
|
||||
|
||||
// Return self
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packet::operator bool() const {
|
||||
return _size > 0;
|
||||
}
|
||||
|
||||
int Packet::size() const {
|
||||
// Return the size
|
||||
return _size;
|
||||
}
|
||||
|
||||
const uint8_t* Packet::data() const {
|
||||
// Return the size
|
||||
return _content;
|
||||
}
|
||||
|
||||
void Packet::setContent(uint8_t* content, int size) {
|
||||
// Check that the size isn't too large
|
||||
if (size > MAX_CONTENT_SIZE) {
|
||||
throw std::runtime_error("Content size is too large to fit in a packet");
|
||||
}
|
||||
|
||||
// Reallocate the buffer
|
||||
allocate(size);
|
||||
|
||||
// Copy over the content
|
||||
memcpy(_content, content, size);
|
||||
}
|
||||
|
||||
int Packet::serializedSize() const {
|
||||
// Two size bytes + Size of the content
|
||||
return _size + 2;
|
||||
}
|
||||
|
||||
int Packet::serialize(uint8_t* bytes) const {
|
||||
// Write the size in big-endian
|
||||
bytes[0] = (_size >> 8) & 0xFF;
|
||||
bytes[1] = _size & 0xFF;
|
||||
|
||||
// Copy the content of the packet
|
||||
memcpy(&bytes[2], _content, _size);
|
||||
|
||||
// Return the serialized size
|
||||
return serializedSize();
|
||||
}
|
||||
|
||||
void Packet::allocate(int newSize) {
|
||||
// If the size hasn't changed, do nothing
|
||||
if (newSize == _size) { return; }
|
||||
|
||||
// Free the old buffer
|
||||
if (_content) { delete[] _content; };
|
||||
|
||||
// Update the size
|
||||
_size = newSize;
|
||||
|
||||
// Allocate the buffer
|
||||
_content = new uint8_t[newSize];
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace ryfi {
|
||||
/**
|
||||
* RyFi Protocol Packet.
|
||||
*/
|
||||
class Packet {
|
||||
public:
|
||||
// Default constructor
|
||||
Packet();
|
||||
|
||||
/**
|
||||
* Create a packet from its content.
|
||||
* @param content Content of the packet.
|
||||
* @param size Number of bytes of content.
|
||||
*/
|
||||
Packet(uint8_t* content, int size);
|
||||
|
||||
// Copy constructor
|
||||
Packet(const Packet& b);
|
||||
|
||||
// Move constructor
|
||||
Packet(Packet&& b);
|
||||
|
||||
// Destructor
|
||||
~Packet();
|
||||
|
||||
// Copy assignment operator
|
||||
Packet& operator=(const Packet& b);
|
||||
|
||||
// Move assignment operator
|
||||
Packet& operator=(Packet&& b);
|
||||
|
||||
// Cast to bool operator
|
||||
operator bool() const;
|
||||
|
||||
/**
|
||||
* Get the size of the content of the packet.
|
||||
* @return Size of the content of the packet.
|
||||
*/
|
||||
int size() const;
|
||||
|
||||
/**
|
||||
* Get the content of the packet. The pointer is only valid until reallocation or deletion.
|
||||
* @return Content of the packet.
|
||||
*/
|
||||
const uint8_t* data() const;
|
||||
|
||||
/**
|
||||
* Set the content of the packet.
|
||||
* @param content Content of the packet.
|
||||
* @param size Number of bytes of content.
|
||||
*/
|
||||
void setContent(uint8_t* content, int size);
|
||||
|
||||
/**
|
||||
* Get the size of the serialized packet.
|
||||
* @return Size of the serialized packet.
|
||||
*/
|
||||
int serializedSize() const;
|
||||
|
||||
/**
|
||||
* Serialize the packet to bytes.
|
||||
* @param bytes Buffer to which to write the serialized packet.
|
||||
* @return Size of the serialized packet.
|
||||
*/
|
||||
int serialize(uint8_t* bytes) const;
|
||||
|
||||
/**
|
||||
* Deserialize a packet from bytes.
|
||||
* TODO
|
||||
*/
|
||||
static bool deserialize(uint8_t* bytes, int size, Packet& pkt);
|
||||
|
||||
// Maximum size of the content of the packet.
|
||||
static inline const int MAX_CONTENT_SIZE = 0xFFFF;
|
||||
|
||||
// Maximum size of the serialized packet.
|
||||
static inline const int MAX_SERIALIZED_SIZE = MAX_CONTENT_SIZE + 2;
|
||||
|
||||
private:
|
||||
void allocate(int newSize);
|
||||
|
||||
uint8_t* _content = NULL;
|
||||
int _size = 0;
|
||||
};
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
#include "receiver.h"
|
||||
|
||||
#include "utils/flog.h"
|
||||
|
||||
namespace ryfi {
|
||||
Receiver::Receiver() {}
|
||||
|
||||
Receiver::Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) {
|
||||
init(in, baudrate, samplerate);
|
||||
}
|
||||
|
||||
Receiver::~Receiver() {
|
||||
// Stop everything
|
||||
stop();
|
||||
}
|
||||
|
||||
void Receiver::init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate) {
|
||||
// Initialize the DSP
|
||||
demod.init(in, baudrate, samplerate, 31, 0.6, 0.1f, 0.005f, 1e-6, 0.01);
|
||||
doubler.init(&demod.out);
|
||||
softOut = &doubler.outA;
|
||||
deframer.setInput(&doubler.outB);
|
||||
conv.setInput(&deframer.out);
|
||||
rs.setInput(&conv.out);
|
||||
}
|
||||
|
||||
void Receiver::setInput(dsp::stream<dsp::complex_t>* in) {
|
||||
demod.setInput(in);
|
||||
}
|
||||
|
||||
void Receiver::start() {
|
||||
// Do nothing if already running
|
||||
if (running) { return; }
|
||||
|
||||
// Start the worker thread
|
||||
workerThread = std::thread(&Receiver::worker, this);
|
||||
|
||||
// Start the DSP
|
||||
demod.start();
|
||||
doubler.start();
|
||||
deframer.start();
|
||||
conv.start();
|
||||
rs.start();
|
||||
|
||||
// Update the running state
|
||||
running = true;
|
||||
}
|
||||
|
||||
void Receiver::stop() {
|
||||
// Do nothing if not running
|
||||
if (!running) { return; }
|
||||
|
||||
// Stop the worker thread
|
||||
rs.out.stopReader();
|
||||
if (workerThread.joinable()) { workerThread.join(); }
|
||||
rs.out.clearReadStop();
|
||||
|
||||
// Stop the DSP
|
||||
demod.stop();
|
||||
doubler.stop();
|
||||
deframer.stop();
|
||||
conv.stop();
|
||||
rs.stop();
|
||||
|
||||
// Update the running state
|
||||
running = false;
|
||||
}
|
||||
|
||||
void Receiver::worker() {
|
||||
Frame frame;
|
||||
uint16_t lastCounter = 0;
|
||||
uint8_t* pktBuffer = new uint8_t[Packet::MAX_CONTENT_SIZE];
|
||||
int pktExpected = 0;
|
||||
int pktRead = 0;
|
||||
int valid = 0;
|
||||
|
||||
while (true) {
|
||||
// Read a frame
|
||||
int count = rs.out.read();
|
||||
if (count <= 0) { break; }
|
||||
|
||||
// Deserialize the frame
|
||||
Frame::deserialize(rs.out.readBuf, frame);
|
||||
valid++;
|
||||
|
||||
// Flush the stream
|
||||
rs.out.flush();
|
||||
|
||||
//flog::info("Frame[{}]: FirstPacket={}, LastPacket={}", frame.counter, frame.firstPacket, frame.lastPacket);
|
||||
|
||||
// Compute the expected frame counter
|
||||
uint16_t expectedCounter = lastCounter + 1;
|
||||
lastCounter = frame.counter;
|
||||
|
||||
// If the frames aren't consecutive
|
||||
int frameRead = 0;
|
||||
if (frame.counter != expectedCounter) {
|
||||
flog::warn("Lost at least {} frames after {} valid frames", ((int)frame.counter - (int)expectedCounter + 0x10000) % 0x10000, valid);
|
||||
|
||||
// Cancel the partial packet if there was one
|
||||
pktExpected = 0;
|
||||
pktRead = 0;
|
||||
valid = 1;
|
||||
|
||||
// If this frame is not an idle frame or continuation frame
|
||||
if (frame.firstPacket != PKT_OFFS_NONE) {
|
||||
// If the offset of the first packet is not plausible
|
||||
if (frame.firstPacket > Frame::FRAME_DATA_SIZE-2) {
|
||||
flog::warn("Packet had non-plausible offset: {}", frameRead);
|
||||
|
||||
// Skip the frame
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip to the end of the packet
|
||||
frameRead = frame.firstPacket;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no partial packet and the frame doesn't contain a packet start, skip it
|
||||
if (!pktExpected && frame.firstPacket == PKT_OFFS_NONE) { continue; }
|
||||
|
||||
// Extract packets from the frame
|
||||
bool firstPacket = true;
|
||||
bool lastPacket = false;
|
||||
while (frameRead < Frame::FRAME_DATA_SIZE) {
|
||||
// If there is a partial packet read as much as possible from it
|
||||
if (pktExpected) {
|
||||
// Compute how many bytes of the packet are available in the frame
|
||||
int readable = std::min<int>(pktExpected - pktRead, Frame::FRAME_DATA_SIZE - frameRead);
|
||||
//flog::debug("Reading {} bytes", readable);
|
||||
|
||||
// Write them to the packet
|
||||
memcpy(&pktBuffer[pktRead], &frame.content[frameRead], readable);
|
||||
pktRead += readable;
|
||||
frameRead += readable;
|
||||
|
||||
// If the packet is read entirely
|
||||
if (pktRead >= pktExpected) {
|
||||
// Create the packet object
|
||||
Packet pkt(pktBuffer, pktExpected);
|
||||
|
||||
// Send off the packet
|
||||
onPacket(pkt);
|
||||
|
||||
// Prepare for the next packet
|
||||
pktRead = 0;
|
||||
pktExpected = 0;
|
||||
|
||||
// If this was the last packet of the frame
|
||||
if (lastPacket || frame.firstPacket == PKT_OFFS_NONE) {
|
||||
// Skip the rest of the frame
|
||||
frameRead = Frame::FRAME_DATA_SIZE;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Go to next packet
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the packet offset is not plausible
|
||||
if (Frame::FRAME_DATA_SIZE - frameRead < 2) {
|
||||
flog::warn("Packet had non-plausible offset: {}", frameRead);
|
||||
|
||||
// Skip the rest of the frame and the packet
|
||||
frameRead = Frame::FRAME_DATA_SIZE;
|
||||
pktExpected = 0;
|
||||
pktRead = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is the first packet, use the frame info to skip possible left over data
|
||||
if (firstPacket) {
|
||||
frameRead = frame.firstPacket;
|
||||
firstPacket = false;
|
||||
}
|
||||
|
||||
// Check if this is the last packet
|
||||
lastPacket = (frameRead == frame.lastPacket);
|
||||
|
||||
// Parse the packet size
|
||||
pktExpected = ((uint16_t)frame.content[frameRead]) << 8;
|
||||
pktExpected |= (uint16_t)frame.content[frameRead+1];
|
||||
//flog::debug("Starting to read a {} byte packet at offset {}", pktExpected, frameRead);
|
||||
|
||||
// Skip to the packet content
|
||||
frameRead += 2;
|
||||
}
|
||||
}
|
||||
|
||||
delete[] pktBuffer;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
#pragma once
|
||||
#include "utils/new_event.h"
|
||||
#include "dsp/demod/psk.h"
|
||||
#include "dsp/routing/doubler.h"
|
||||
#include "packet.h"
|
||||
#include "frame.h"
|
||||
#include "rs_codec.h"
|
||||
#include "conv_codec.h"
|
||||
#include "framing.h"
|
||||
#include <mutex>
|
||||
|
||||
namespace ryfi {
|
||||
class Receiver {
|
||||
public:
|
||||
Receiver();
|
||||
|
||||
/**
|
||||
* Create a transmitter.
|
||||
* @param in Baseband input.
|
||||
* @param baudrate Baudrate to use over the air.
|
||||
* @param samplerate Samplerate of the baseband.
|
||||
*/
|
||||
Receiver(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate);
|
||||
|
||||
/**
|
||||
* Create a transmitter.
|
||||
* @param in Baseband input.
|
||||
* @param baudrate Baudrate to use over the air.
|
||||
* @param samplerate Samplerate of the baseband.
|
||||
*/
|
||||
void init(dsp::stream<dsp::complex_t>* in, double baudrate, double samplerate);
|
||||
|
||||
/**
|
||||
* Set the input stream.
|
||||
* @param in Baseband input.
|
||||
*/
|
||||
void setInput(dsp::stream<dsp::complex_t>* in);
|
||||
|
||||
// Destructor
|
||||
~Receiver();
|
||||
|
||||
/**
|
||||
* Start the transmitter's DSP.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop the transmitter's DSP.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
dsp::stream<dsp::complex_t>* softOut;
|
||||
|
||||
NewEvent<Packet> onPacket;
|
||||
|
||||
private:
|
||||
void worker();
|
||||
|
||||
// DSP
|
||||
dsp::demod::PSK<4> demod;
|
||||
dsp::routing::Doubler<dsp::complex_t> doubler;
|
||||
Deframer deframer;
|
||||
ConvDecoder conv;
|
||||
RSDecoder rs;
|
||||
|
||||
bool running = false;
|
||||
std::thread workerThread;
|
||||
};
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
#include "rs_codec.h"
|
||||
|
||||
namespace ryfi {
|
||||
RSEncoder::RSEncoder(dsp::stream<uint8_t>* in) {
|
||||
// Create the convolutional encoder instance
|
||||
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32);
|
||||
|
||||
// Init the base class
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
RSEncoder::~RSEncoder() {
|
||||
// Destroy the convolutional encoder instance
|
||||
correct_reed_solomon_destroy(rs);
|
||||
}
|
||||
|
||||
int RSEncoder::encode(const uint8_t* in, uint8_t* out, int count) {
|
||||
// Check the size
|
||||
assert(count == RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE);
|
||||
|
||||
// Go through each block
|
||||
uint8_t block[RS_BLOCK_ENC_SIZE];
|
||||
for (int i = 0; i < RS_BLOCK_COUNT; i++) {
|
||||
// Encode block
|
||||
correct_reed_solomon_encode(rs, &in[i*RS_BLOCK_DEC_SIZE], RS_BLOCK_DEC_SIZE, block);
|
||||
|
||||
// Interleave into the frame
|
||||
int k = 0;
|
||||
for (int j = i; j < RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT; j += RS_BLOCK_COUNT) {
|
||||
out[j] = block[k++];
|
||||
}
|
||||
}
|
||||
|
||||
// Scramble
|
||||
for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) {
|
||||
out[i] ^= RS_SCRAMBLER_SEQ[i];
|
||||
}
|
||||
|
||||
return RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE;
|
||||
}
|
||||
|
||||
int RSEncoder::run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
count = encode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||
|
||||
base_type::_in->flush();
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
RSDecoder::RSDecoder(dsp::stream<uint8_t>* in) {
|
||||
// Create the convolutional encoder instance
|
||||
rs = correct_reed_solomon_create(correct_rs_primitive_polynomial_ccsds, 1, 1, 32);
|
||||
|
||||
// Init the base class
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
RSDecoder::~RSDecoder() {
|
||||
// Destroy the convolutional encoder instance
|
||||
correct_reed_solomon_destroy(rs);
|
||||
}
|
||||
|
||||
int RSDecoder::decode(uint8_t* in, uint8_t* out, int count) {
|
||||
// Check the size
|
||||
assert(count == RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE);
|
||||
|
||||
// Descramble (TODO: Don't do it in-place)
|
||||
for (int i = 0; i < RS_BLOCK_COUNT*RS_BLOCK_ENC_SIZE; i++) {
|
||||
in[i] ^= RS_SCRAMBLER_SEQ[i];
|
||||
}
|
||||
|
||||
// Go through each block
|
||||
uint8_t block[RS_BLOCK_ENC_SIZE];
|
||||
for (int i = 0; i < RS_BLOCK_COUNT; i++) {
|
||||
// Deinterleave out of the frame
|
||||
int k = 0;
|
||||
for (int j = i; j < count; j += RS_BLOCK_COUNT) {
|
||||
block[k++] = in[j];
|
||||
}
|
||||
|
||||
// Decode block and return if decoding fails
|
||||
int res = correct_reed_solomon_decode(rs, block, RS_BLOCK_ENC_SIZE, &out[i*RS_BLOCK_DEC_SIZE]);
|
||||
if (res < 0) { return 0; }
|
||||
}
|
||||
|
||||
return RS_BLOCK_COUNT*RS_BLOCK_DEC_SIZE;
|
||||
}
|
||||
|
||||
int RSDecoder::run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
count = decode(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||
|
||||
base_type::_in->flush();
|
||||
if (count && !out.swap(count)) { return -1; }
|
||||
return count;
|
||||
}
|
||||
|
||||
const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT] = {
|
||||
0x75, 0x05, 0x7C, 0xCE, 0xF1, 0xD0, 0x6C, 0xF6, 0xFA, 0x65, 0xF6, 0xFC, 0xE0, 0x0A, 0x82, 0x17,
|
||||
0x6C, 0xBE, 0x76, 0xA0, 0xD6, 0x46, 0x12, 0x2E, 0xDE, 0xB5, 0xF7, 0xAD, 0xCB, 0x51, 0x63, 0x47,
|
||||
0x27, 0x30, 0x7E, 0x43, 0xD1, 0xA1, 0xCB, 0x10, 0x08, 0x49, 0xDF, 0x86, 0xD4, 0xC4, 0xD7, 0x3C,
|
||||
0x6D, 0x03, 0x07, 0x37, 0x5B, 0xB3, 0xCD, 0x79, 0x6F, 0x1E, 0xBA, 0xC5, 0x6E, 0xC3, 0x8C, 0x7A,
|
||||
0x25, 0x99, 0x61, 0x54, 0x5A, 0x96, 0x57, 0x9B, 0xE0, 0x60, 0x5B, 0x09, 0x6D, 0x8B, 0x2D, 0x9D,
|
||||
0x15, 0x9D, 0x0E, 0xBF, 0x57, 0xFB, 0x9C, 0x49, 0x82, 0x2C, 0x48, 0x59, 0x92, 0x47, 0x79, 0x17,
|
||||
0x16, 0x74, 0xEA, 0xEA, 0xBB, 0xC5, 0x72, 0x32, 0x17, 0xD1, 0xB3, 0xDE, 0xEB, 0x15, 0xC7, 0x55,
|
||||
0x8A, 0xF2, 0x88, 0xC2, 0x33, 0xA6, 0x17, 0x8B, 0xD4, 0x77, 0x22, 0x00, 0x63, 0x47, 0x45, 0x5F,
|
||||
0x36, 0x35, 0x58, 0x8B, 0x88, 0xEC, 0xCA, 0xC4, 0x60, 0x53, 0x9E, 0xBD, 0xB2, 0xF5, 0x51, 0x46,
|
||||
0x34, 0x9A, 0x07, 0x25, 0x3F, 0xF5, 0x65, 0x63, 0x77, 0x3C, 0x5A, 0xFA, 0x4E, 0x0C, 0xF7, 0x1B,
|
||||
0x82, 0xAB, 0x73, 0x06, 0x7F, 0xB7, 0xC6, 0x6B, 0xBF, 0xB1, 0x46, 0xF3, 0x01, 0x91, 0xB1, 0xFF,
|
||||
0x5C, 0x6F, 0xF9, 0x43, 0x0E, 0x6A, 0x70, 0x89, 0x0B, 0xEA, 0x8C, 0xD4, 0x1B, 0x51, 0x01, 0x31,
|
||||
0x71, 0x2E, 0xDF, 0x24, 0xC1, 0xD5, 0xDB, 0x0E, 0xF5, 0xEB, 0x78, 0x79, 0x39, 0x5B, 0xAD, 0xC3,
|
||||
0xA9, 0xA6, 0x60, 0x30, 0xA2, 0x9A, 0x7B, 0xA0, 0xF4, 0xAA, 0xC5, 0x57, 0xB3, 0x16, 0xF9, 0xB5,
|
||||
0x79, 0x20, 0xC1, 0x88, 0x9A, 0x00, 0x43, 0xB2, 0xC6, 0x84, 0x8D, 0x03, 0xF2, 0xD8, 0x90, 0x7A,
|
||||
0x21, 0x37, 0x7E, 0xF7, 0x75, 0xE5, 0xFB, 0xC9, 0xDC, 0xAB, 0x4B, 0xBC, 0x35, 0x38, 0xB9, 0x3A,
|
||||
0x53, 0x89, 0x7E, 0xD5, 0x94, 0x12, 0x2D, 0x9B, 0x91, 0x90, 0x1D, 0x4D, 0x0E, 0xE0, 0x93, 0xF3,
|
||||
0xC1, 0xA1, 0x9B, 0x73, 0x27, 0x22, 0x41, 0x27, 0xEE, 0x2A, 0xD7, 0x45, 0xBC, 0x8F, 0x9B, 0xA2,
|
||||
0x36, 0x11, 0x16, 0x37, 0x1A, 0xF1, 0x2E, 0x71, 0xCF, 0x86, 0x89, 0x83, 0x5A, 0xF1, 0x24, 0x6C,
|
||||
0x56, 0x71, 0x53, 0xE4, 0xD2, 0xCB, 0xCA, 0x86, 0x1E, 0xA0, 0xD5, 0x83, 0x3B, 0xEF, 0x09, 0x09,
|
||||
0xC2, 0x07, 0x53, 0x86, 0xE6, 0x8A, 0xC6, 0x70, 0xFB, 0x91, 0x43, 0xCB, 0x91, 0x6E, 0xA9, 0xBC,
|
||||
0x31, 0x42, 0x61, 0x0C, 0x88, 0xB8, 0x2C, 0xED, 0xD8, 0xE6, 0xA3, 0xEC, 0xAC, 0xB9, 0x45, 0x5E,
|
||||
0x2C, 0x73, 0x3F, 0x2E, 0x06, 0xE0, 0xBF, 0x73, 0xDD, 0x2E, 0x45, 0x50, 0x6C, 0x53, 0x55, 0xF0,
|
||||
0x7F, 0x6E, 0x61, 0xFA, 0xA0, 0x7A, 0x1C, 0xF0, 0xBD, 0xAC, 0x48, 0x61, 0x03, 0x6B, 0xED, 0x54,
|
||||
0x2A, 0x27, 0x94, 0xF6, 0xF9, 0x6A, 0x04, 0x08, 0x0B, 0x3C, 0xC3, 0x30, 0x66, 0x01, 0xFB, 0xDC,
|
||||
0xC9, 0x65, 0x03, 0x83, 0x7D, 0x0A, 0xDF, 0xA5, 0x04, 0x14, 0xE4, 0xF2, 0x4C, 0x01, 0xDF, 0x04,
|
||||
0xD2, 0x80, 0xB9, 0x9B, 0xD9, 0x5E, 0xF8, 0x2A, 0x93, 0x8D, 0x8C, 0x09, 0x9B, 0x38, 0xEC, 0x3B,
|
||||
0xC4, 0x29, 0x90, 0x7C, 0x65, 0x3A, 0xF2, 0x4B, 0x69, 0xD3, 0x63, 0x9B, 0x40, 0x95, 0xC3, 0xFB,
|
||||
0x67, 0x54, 0x40, 0x9B, 0x26, 0x9F, 0x52, 0xFE, 0xD8, 0xD0, 0x24, 0x9C, 0x5C, 0xD4, 0xEF, 0xDE,
|
||||
0x28, 0x66, 0x75, 0x04, 0xCB, 0xA4, 0xC0, 0xB9, 0x4B, 0xC9, 0x20, 0x4B, 0x56, 0xC7, 0x86, 0xC5,
|
||||
0x39, 0x45, 0x18, 0xA7, 0x48, 0x14, 0x1A, 0x51, 0xCA, 0xD0, 0xC0, 0x15, 0xDD, 0xC1, 0x28, 0x4A,
|
||||
0x7A, 0xD2, 0x10, 0xEA, 0x83, 0xD3, 0x3A, 0xEF, 0x48, 0x29, 0x41, 0xA4, 0xD4, 0x57, 0xA6, 0x1D,
|
||||
0x76, 0x24, 0x93, 0x58, 0x7E, 0xB7, 0xDD, 0x0B, 0xF2, 0xCE, 0x71, 0x55, 0xF5, 0xAB, 0x8C, 0xC8,
|
||||
0x70, 0x59, 0x73, 0x69, 0x9D, 0x29, 0x5E, 0x59, 0xF4, 0xB2, 0xC4, 0x97, 0x75, 0xF0, 0x65, 0x1B,
|
||||
0x66, 0x5F, 0xA4, 0x33, 0x5C, 0xC7, 0xBF, 0x45, 0xE6, 0x20, 0xC0, 0xBD, 0xAD, 0xAE, 0x9F, 0x97,
|
||||
0x05, 0xD8, 0x04, 0x2B, 0x0A, 0x46, 0xE8, 0xB8, 0xCB, 0x00, 0xE2, 0x7C, 0x70, 0x1B, 0x49, 0xDE,
|
||||
0x81, 0xEB, 0x24, 0xAC, 0x1B, 0x3E, 0x09, 0xFB, 0xAC, 0xB7, 0xF2, 0xD1, 0xB2, 0x78, 0xF3, 0xAC,
|
||||
0xC7, 0x6A, 0xA2, 0x07, 0x4C, 0xED, 0x61, 0xAD, 0x04, 0x7F, 0x45, 0x83, 0x59, 0x31, 0x27, 0xF0,
|
||||
0x16, 0x6B, 0x0C, 0xAA, 0xD4, 0xD1, 0xCB, 0x1C, 0x51, 0x41, 0x0D, 0x2F, 0x8F, 0xF9, 0xF9, 0x7F,
|
||||
0x22, 0x89, 0x46, 0xF4, 0xB8, 0x93, 0x98, 0x9E, 0x3E, 0x23, 0xF1, 0x6E, 0x64, 0x08, 0xB6, 0xC9,
|
||||
0x6E, 0x53, 0x53, 0xED, 0xAD, 0x21, 0xCD, 0x1A, 0xF0, 0x45, 0xFC, 0x14, 0x00, 0xEA, 0xF7, 0x42,
|
||||
0xEE, 0xDA, 0x58, 0x0D, 0x85, 0xBC, 0x74, 0xFB, 0x73, 0x78, 0xB5, 0x5E, 0x5E, 0x6F, 0x6F, 0x7E,
|
||||
0x39, 0xC2, 0x05, 0x50, 0xDB, 0x3D, 0xB8, 0xF3, 0x8F, 0x80, 0xEC, 0x46, 0x29, 0x39, 0x89, 0xF3,
|
||||
0x55, 0x9C, 0x6A, 0x5F, 0x7C, 0xD9, 0x7C, 0x13, 0xE4, 0x56, 0x5E, 0xE9, 0x60, 0x19, 0xE2, 0x7D,
|
||||
0xC4, 0x41, 0x92, 0x8D, 0xDA, 0x21, 0x58, 0x20, 0xE9, 0xA8, 0x4C, 0x16, 0x34, 0x99, 0xAC, 0xB7,
|
||||
0x30, 0xBD, 0x39, 0x19, 0xAC, 0x9B, 0x4B, 0x27, 0xFA, 0x32, 0xC1, 0x48, 0xA1, 0x80, 0x34, 0x36,
|
||||
0x1E, 0xFB, 0x92, 0x43, 0x35, 0x72, 0x2D, 0xEF, 0xD2, 0xF2, 0xFC, 0xC2, 0x85, 0xAB, 0x59, 0x40,
|
||||
0x8D, 0x9D, 0x1A, 0x1F, 0xE2, 0x92, 0x87, 0xA2, 0xF9, 0x2C, 0x78, 0xE4, 0xC3, 0x26, 0x56, 0x07,
|
||||
0xB3, 0x78, 0xAF, 0x79, 0x3D, 0x88, 0xF4, 0xAD, 0x66, 0x7C, 0x07, 0x58, 0x98, 0x82, 0x1A, 0x26,
|
||||
0xF7, 0xFD, 0xCE, 0xFF, 0x75, 0xED, 0xAB, 0xBD, 0xAE, 0x6D, 0x5C, 0x28, 0x91, 0xF3, 0xB7, 0x5C,
|
||||
0x27, 0x05, 0xEC, 0x3B, 0xE3, 0xDD, 0x93, 0x24, 0x7F, 0xAD, 0x14, 0xAA, 0x49, 0x61, 0x8F, 0x96,
|
||||
0x1F, 0xAA, 0xB2, 0xEE, 0xA8, 0x24, 0x41, 0x7C, 0xDC, 0xF1, 0x28, 0x26, 0xE6, 0x7F, 0x98, 0x20,
|
||||
0x50, 0x5F, 0x90, 0x21, 0x8A, 0x09, 0x26, 0x59, 0xD0, 0x07, 0x2F, 0xE1, 0x35, 0x4D, 0x0B, 0x20,
|
||||
0xB2, 0xD5, 0xDD, 0xB5, 0xAC, 0x1B, 0xFE, 0xD9, 0xE3, 0x35, 0xF1, 0xB8, 0x3F, 0x3D, 0xFC, 0x0B,
|
||||
0x5A, 0x57, 0xA9, 0x92, 0x2B, 0xC8, 0x3E, 0xC2, 0xAA, 0xEF, 0xB9, 0x98, 0x2C, 0xA8, 0xAB, 0xF6,
|
||||
0xA1, 0xBF, 0xBC, 0x8D, 0x97, 0xA2, 0x74, 0xD9, 0xE5, 0x99, 0x85, 0x81, 0x15, 0xB0, 0xE7, 0x8B,
|
||||
0x48, 0x86, 0xF4, 0x94, 0x9C, 0x62, 0x82, 0xD1, 0x2C, 0x24, 0x4B, 0xAC, 0x7A, 0xB8, 0x4E, 0x4A,
|
||||
0xD2, 0xF6, 0xAA, 0xED, 0xE0, 0x9C, 0x98, 0xD2, 0xDF, 0xC1, 0xBC, 0xBF, 0x55, 0x7D, 0x40, 0xB5,
|
||||
0xDE, 0xD4, 0x25, 0xBB, 0x81, 0xF4, 0x07, 0x1D, 0xE7, 0x3C, 0xB4, 0x62, 0xC9, 0x55, 0x0A, 0x3A,
|
||||
0xD5, 0xCE, 0x97, 0xED, 0x30, 0x76, 0x76, 0x51, 0xBC, 0x8C, 0xE4, 0x54, 0xBE, 0xB7, 0xB5, 0xCD,
|
||||
0xF8, 0x76, 0x37, 0x53, 0x2C, 0x9F, 0xE4, 0xC7, 0xEB, 0xF5, 0x8D, 0x23, 0x8A, 0xDA, 0xD1, 0xA9,
|
||||
0xD8, 0x4C, 0x53, 0xF3, 0x49, 0xA7, 0x1A, 0x5D, 0xE5, 0x03, 0x49, 0x52, 0xD3, 0xE2, 0x1F, 0xA5,
|
||||
0x35, 0x9C, 0xBB, 0x0B, 0xC7, 0x0D, 0xA4, 0x65, 0x54, 0x8B, 0x39, 0xF1, 0x3B, 0x67, 0x21, 0x71,
|
||||
0x10, 0xE7, 0x76, 0xC4, 0xA8, 0xC2, 0x9D, 0x93, 0xC6, 0x51, 0xBA, 0x23
|
||||
};
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "dsp/processor.h"
|
||||
|
||||
extern "C" {
|
||||
#include "correct.h"
|
||||
}
|
||||
|
||||
namespace ryfi {
|
||||
// Size of an encoded reed-solomon block.
|
||||
inline const int RS_BLOCK_ENC_SIZE = 255;
|
||||
|
||||
// Size of a decoded reed-solomon block.
|
||||
inline const int RS_BLOCK_DEC_SIZE = 223;
|
||||
|
||||
// Number of reed-solomon blocks.
|
||||
inline const int RS_BLOCK_COUNT = 4;
|
||||
|
||||
// Scrambler sequence
|
||||
extern const uint8_t RS_SCRAMBLER_SEQ[RS_BLOCK_ENC_SIZE*RS_BLOCK_COUNT];
|
||||
|
||||
/**
|
||||
* RyFi Reed-Solomon Encoder.
|
||||
*/
|
||||
class RSEncoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||
public:
|
||||
/**
|
||||
* Create a reed-solomon encoder specifying an input stream.
|
||||
* @param in Input stream
|
||||
*/
|
||||
RSEncoder(dsp::stream<uint8_t>* in = NULL);
|
||||
|
||||
// Destructor
|
||||
~RSEncoder();
|
||||
|
||||
/**
|
||||
* Encode data.
|
||||
* @param in Input bytes.
|
||||
* @param out Output bytes.
|
||||
* @param count Number of input bytes.
|
||||
* @return Number of output bytes.
|
||||
*/
|
||||
int encode(const uint8_t* in, uint8_t* out, int count);
|
||||
|
||||
private:
|
||||
int run();
|
||||
|
||||
correct_reed_solomon* rs;
|
||||
};
|
||||
|
||||
/**
|
||||
* RyFi Reed-Solomon Decoder.
|
||||
*/
|
||||
class RSDecoder : public dsp::Processor<uint8_t, uint8_t> {
|
||||
using base_type = dsp::Processor<uint8_t, uint8_t>;
|
||||
public:
|
||||
/**
|
||||
* Create a reed-solomon decoder specifying an input stream.
|
||||
* @param in Input stream
|
||||
*/
|
||||
RSDecoder(dsp::stream<uint8_t>* in = NULL);
|
||||
|
||||
// Destructor
|
||||
~RSDecoder();
|
||||
|
||||
/**
|
||||
* Decode data.
|
||||
* @param in Input bytes.
|
||||
* @param out Output bytes.
|
||||
* @param count Number of input bytes.
|
||||
* @return Number of output bytes.
|
||||
*/
|
||||
int decode(uint8_t* in, uint8_t* out, int count);
|
||||
|
||||
private:
|
||||
int run();
|
||||
|
||||
correct_reed_solomon* rs;
|
||||
};
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
#include "transmitter.h"
|
||||
|
||||
namespace ryfi {
|
||||
Transmitter::Transmitter(double baudrate, double samplerate) {
|
||||
// Initialize the DSP
|
||||
rs.setInput(&in);
|
||||
conv.setInput(&rs.out);
|
||||
framer.setInput(&conv.out);
|
||||
resamp.init(&framer.out, baudrate, samplerate);
|
||||
|
||||
rrcTaps = dsp::taps::rootRaisedCosine<float>(511, 0.6, baudrate, samplerate);
|
||||
// Normalize the taps
|
||||
float tot = 0.0f;
|
||||
for (int i = 0; i < rrcTaps.size; i++) {
|
||||
tot += rrcTaps.taps[i];
|
||||
}
|
||||
for (int i = 0; i < rrcTaps.size; i++) {
|
||||
rrcTaps.taps[i] /= tot;
|
||||
}
|
||||
|
||||
rrc.init(&resamp.out, rrcTaps);
|
||||
out = &rrc.out;
|
||||
}
|
||||
|
||||
Transmitter::~Transmitter() {
|
||||
// Stop everything
|
||||
stop();
|
||||
}
|
||||
|
||||
void Transmitter::start() {
|
||||
// Do nothing if already running
|
||||
if (running) { return; }
|
||||
|
||||
// Start the worker thread
|
||||
workerThread = std::thread(&Transmitter::worker, this);
|
||||
|
||||
// Start the DSP
|
||||
rs.start();
|
||||
conv.start();
|
||||
framer.start();
|
||||
resamp.start();
|
||||
rrc.start();
|
||||
|
||||
// Update the running state
|
||||
running = true;
|
||||
}
|
||||
|
||||
void Transmitter::stop() {
|
||||
// Do nothing if not running
|
||||
if (!running) { return; }
|
||||
|
||||
// Stop the worker thread
|
||||
in.stopWriter();
|
||||
if (workerThread.joinable()) { workerThread.join(); }
|
||||
in.clearWriteStop();
|
||||
|
||||
// Stop the DSP
|
||||
rs.stop();
|
||||
conv.stop();
|
||||
framer.stop();
|
||||
resamp.stop();
|
||||
rrc.stop();
|
||||
|
||||
// Update the running state
|
||||
running = false;
|
||||
}
|
||||
|
||||
bool Transmitter::send(const Packet& pkt) {
|
||||
// Acquire the packet queue
|
||||
std::lock_guard<std::mutex> lck(packetsMtx);
|
||||
|
||||
// If there are too many packets queued up, drop the packet
|
||||
if (packets.size() >= MAX_QUEUE_SIZE) { return false; }
|
||||
|
||||
// Push the packet onto the queue
|
||||
packets.push(pkt);
|
||||
}
|
||||
|
||||
bool Transmitter::txFrame(const Frame& frame) {
|
||||
// Serialize the frame
|
||||
int count = frame.serialize(in.writeBuf);
|
||||
|
||||
// Send it off
|
||||
return in.swap(count);
|
||||
}
|
||||
|
||||
Packet Transmitter::popPacket() {
|
||||
// Acquire the packet queue
|
||||
std::unique_lock<std::mutex> lck(packetsMtx);
|
||||
|
||||
// If no packets are available, return empty packet
|
||||
if (!packets.size()) { return Packet(); }
|
||||
|
||||
// Pop the front packet and return it
|
||||
Packet pkt = packets.front();
|
||||
packets.pop();
|
||||
return pkt;
|
||||
}
|
||||
|
||||
void Transmitter::worker() {
|
||||
Frame frame;
|
||||
Packet pkt;
|
||||
uint16_t counter = 0;
|
||||
int pktToWrite = 0;
|
||||
int pktWritten = 0;
|
||||
uint8_t* pktBuffer = new uint8_t[Packet::MAX_SERIALIZED_SIZE];
|
||||
|
||||
while (true) {
|
||||
// Initialize the frame
|
||||
frame.counter = counter++;
|
||||
frame.firstPacket = PKT_OFFS_NONE;
|
||||
frame.lastPacket = PKT_OFFS_NONE;
|
||||
int frameOffset = 0;
|
||||
|
||||
// Fill the frame with as much packet data as possible
|
||||
while (frameOffset < sizeof(Frame::content)) {
|
||||
// If there is no packet in the process of being sent
|
||||
if (!pktWritten) {
|
||||
// If there is not enough space for the size of the packet
|
||||
if ((sizeof(Frame::content) - frameOffset) < 2) {
|
||||
// Fill the rest of the frame with noise and send it
|
||||
for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); }
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the next packet
|
||||
pkt = popPacket();
|
||||
|
||||
// If there was an available packet
|
||||
if (pkt) {
|
||||
// Serialize the packet
|
||||
pktToWrite = pkt.serializedSize();
|
||||
pkt.serialize(pktBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
// If none was available
|
||||
if (!pkt) {
|
||||
// Fill the rest of the frame with noise and send it
|
||||
for (int i = frameOffset; i < sizeof(Frame::content); i++) { frame.content[i] = rand(); }
|
||||
break;
|
||||
}
|
||||
|
||||
// If this is the beginning of the packet
|
||||
if (!pktWritten) {
|
||||
//flog::debug("Starting to write a {} byte packet at offset {}", pktToWrite-2, frameOffset);
|
||||
|
||||
// If this is the first packet of the frame, update its offset
|
||||
if (frame.firstPacket == PKT_OFFS_NONE) { frame.firstPacket = frameOffset; }
|
||||
|
||||
// Update the last packet pointer
|
||||
frame.lastPacket = frameOffset;
|
||||
}
|
||||
|
||||
// Compute the amount of data writeable to the frame
|
||||
int writeable = std::min<int>(pktToWrite - pktWritten, sizeof(Frame::content) - frameOffset);
|
||||
|
||||
// Copy the data to the frame
|
||||
memcpy(&frame.content[frameOffset], &pktBuffer[pktWritten], writeable);
|
||||
pktWritten += writeable;
|
||||
frameOffset += writeable;
|
||||
|
||||
// If the packet is done being sent
|
||||
if (pktWritten >= pktToWrite) {
|
||||
// Prepare for a new packet
|
||||
pktToWrite = 0;
|
||||
pktWritten = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Send the frame
|
||||
if (!txFrame(frame)) { break; }
|
||||
}
|
||||
|
||||
delete[] pktBuffer;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
#pragma once
|
||||
#include "dsp/multirate/rational_resampler.h"
|
||||
#include "dsp/taps/root_raised_cosine.h"
|
||||
#include "dsp/filter/fir.h"
|
||||
#include "packet.h"
|
||||
#include "frame.h"
|
||||
#include "rs_codec.h"
|
||||
#include "conv_codec.h"
|
||||
#include "framing.h"
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
|
||||
namespace ryfi {
|
||||
class Transmitter {
|
||||
public:
|
||||
/**
|
||||
* Create a transmitter.
|
||||
* @param baudrate Baudrate to use over the air.
|
||||
* @param samplerate Samplerate of the baseband.
|
||||
*/
|
||||
Transmitter(double baudrate, double samplerate);
|
||||
|
||||
// Destructor
|
||||
~Transmitter();
|
||||
|
||||
/**
|
||||
* Start the transmitter's DSP.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop the transmitter's DSP.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Send a packet.
|
||||
* @param pkg Packet to send.
|
||||
* @return True if the packet was send, false if it was dropped.
|
||||
*/
|
||||
bool send(const Packet& pkt);
|
||||
|
||||
// Baseband output
|
||||
dsp::stream<dsp::complex_t>* out;
|
||||
|
||||
static inline const int MAX_QUEUE_SIZE = 32;
|
||||
|
||||
private:
|
||||
bool txFrame(const Frame& frame);
|
||||
Packet popPacket();
|
||||
void worker();
|
||||
|
||||
// Packet queue
|
||||
std::mutex packetsMtx;
|
||||
std::queue<Packet> packets;
|
||||
|
||||
// DSP
|
||||
dsp::stream<uint8_t> in;
|
||||
RSEncoder rs;
|
||||
ConvEncoder conv;
|
||||
Framer framer;
|
||||
dsp::multirate::RationalResampler<dsp::complex_t> resamp;
|
||||
dsp::tap<float> rrcTaps;
|
||||
dsp::filter::FIR<dsp::complex_t, float> rrc;
|
||||
|
||||
bool running = false;
|
||||
std::thread workerThread;
|
||||
};
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
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/")
|
@ -1,128 +0,0 @@
|
||||
#include <imgui.h>
|
||||
#include <config.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <module.h>
|
||||
#include <filesystem>
|
||||
#include <dsp/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();
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
#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;
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,106 +0,0 @@
|
||||
#include <dsp/sink.h>
|
||||
#include <dsp/types.h>
|
||||
#include <dsp/demod/am.h>
|
||||
#include <dsp/demod/quadrature.h>
|
||||
#include <dsp/convert/real_to_complex.h>
|
||||
#include <dsp/channel/frequency_xlator.h>
|
||||
#include <dsp/filter/fir.h>
|
||||
#include <dsp/math/delay.h>
|
||||
#include <dsp/math/conjugate.h>
|
||||
#include <dsp/channel/rx_vfo.h>
|
||||
#include "vor_fm_filter.h"
|
||||
#include <utils/wav.h>
|
||||
|
||||
#define VOR_IN_SR 25e3
|
||||
|
||||
namespace vor {
|
||||
class Receiver : public dsp::Processor<dsp::complex_t, float> {
|
||||
using base_type = dsp::Processor<dsp::complex_t, float>;
|
||||
public:
|
||||
Receiver() {}
|
||||
|
||||
Receiver(dsp::stream<dsp::complex_t>* in) { init(in); }
|
||||
|
||||
~Receiver() {
|
||||
if (!base_type::_block_init) { return; }
|
||||
base_type::stop();
|
||||
dsp::taps::free(fmfTaps);
|
||||
}
|
||||
|
||||
void init(dsp::stream<dsp::complex_t>* in) {
|
||||
amd.init(NULL, dsp::demod::AM<float>::CARRIER, VOR_IN_SR, 50.0f / VOR_IN_SR, 5.0f / VOR_IN_SR, 100.0f / VOR_IN_SR, VOR_IN_SR);
|
||||
amr2c.init(NULL);
|
||||
fmr2c.init(NULL);
|
||||
fmx.init(NULL, -9960, VOR_IN_SR);
|
||||
fmfTaps = dsp::taps::fromArray(FM_TAPS_COUNT, fm_taps);
|
||||
fmf.init(NULL, fmfTaps);
|
||||
fmd.init(NULL, 600, VOR_IN_SR);
|
||||
amde.init(NULL, FM_TAPS_COUNT / 2);
|
||||
amv.init(NULL, VOR_IN_SR, 1000, 30, 30);
|
||||
fmv.init(NULL, VOR_IN_SR, 1000, 30, 30);
|
||||
|
||||
base_type::init(in);
|
||||
}
|
||||
|
||||
int process(dsp::complex_t* in, float* out, int count) {
|
||||
// Demodulate the AM outer modulation
|
||||
volk_32fc_magnitude_32f(amd.out.writeBuf, (lv_32fc_t*)in, count);
|
||||
amr2c.process(count, amd.out.writeBuf, amr2c.out.writeBuf);
|
||||
|
||||
// Isolate the FM subcarrier
|
||||
fmx.process(count, amr2c.out.writeBuf, fmx.out.writeBuf);
|
||||
fmf.process(count, fmx.out.writeBuf, fmx.out.writeBuf);
|
||||
|
||||
// Demodulate the FM subcarrier
|
||||
fmd.process(count, fmx.out.writeBuf, fmd.out.writeBuf);
|
||||
fmr2c.process(count, fmd.out.writeBuf, fmr2c.out.writeBuf);
|
||||
|
||||
// Delay the AM signal by the same amount as the FM one
|
||||
amde.process(count, amr2c.out.writeBuf, amr2c.out.writeBuf);
|
||||
|
||||
// Isolate the 30Hz component on both the AM and FM channels
|
||||
int rcount = amv.process(count, amr2c.out.writeBuf, amv.out.writeBuf);
|
||||
fmv.process(count, fmr2c.out.writeBuf, fmv.out.writeBuf);
|
||||
|
||||
// If no data was returned, we're done for this round
|
||||
if (!rcount) { return 0; }
|
||||
|
||||
// Conjugate FM reference
|
||||
volk_32fc_conjugate_32fc((lv_32fc_t*)fmv.out.writeBuf, (lv_32fc_t*)fmv.out.writeBuf, rcount);
|
||||
|
||||
// Multiply both together
|
||||
volk_32fc_x2_multiply_32fc((lv_32fc_t*)amv.out.writeBuf, (lv_32fc_t*)amv.out.writeBuf, (lv_32fc_t*)fmv.out.writeBuf, rcount);
|
||||
|
||||
// Compute angle
|
||||
volk_32fc_s32f_atan2_32f(out, (lv_32fc_t*)amv.out.writeBuf, 1.0f, rcount);
|
||||
|
||||
return rcount;
|
||||
}
|
||||
|
||||
int run() {
|
||||
int count = base_type::_in->read();
|
||||
if (count < 0) { return -1; }
|
||||
|
||||
int outCount = process(base_type::_in->readBuf, base_type::out.writeBuf, count);
|
||||
|
||||
// Swap if some data was generated
|
||||
base_type::_in->flush();
|
||||
if (outCount) {
|
||||
if (!base_type::out.swap(outCount)) { return -1; }
|
||||
}
|
||||
return outCount;
|
||||
}
|
||||
|
||||
private:
|
||||
dsp::demod::AM<float> amd;
|
||||
dsp::convert::RealToComplex amr2c;
|
||||
dsp::convert::RealToComplex fmr2c;
|
||||
dsp::channel::FrequencyXlator fmx;
|
||||
dsp::tap<float> fmfTaps;
|
||||
dsp::filter::FIR<dsp::complex_t, float> fmf;
|
||||
dsp::demod::Quadrature fmd;
|
||||
dsp::math::Delay<dsp::complex_t> amde;
|
||||
dsp::channel::RxVFO amv;
|
||||
dsp::channel::RxVFO fmv;
|
||||
};
|
||||
}
|
@ -6,14 +6,13 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
@ -26,30 +25,10 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -6,14 +6,13 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
@ -26,30 +25,10 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -6,14 +6,13 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
@ -26,30 +25,10 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -6,14 +6,13 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
@ -26,30 +25,10 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
|
@ -12,14 +12,13 @@ apt update
|
||||
# Install dependencies and tools
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev libzstd-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev libudev-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev libudev-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install a more recent libusb version
|
||||
@ -52,26 +51,6 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Fix missing .pc file for codec2
|
||||
echo 'prefix=/usr/' >> /usr/share/pkgconfig/codec2.pc
|
||||
echo 'libdir=/usr/include/x86_64-linux-gnu/' >> /usr/share/pkgconfig/codec2.pc
|
||||
@ -87,9 +66,9 @@ echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
# Generate package
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk1-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk1-bin, librtaudio6, libzstd1'
|
@ -6,14 +6,13 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
@ -26,31 +25,11 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk2-bin, librtaudio6, libzstd1'
|
@ -6,14 +6,13 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
@ -26,31 +25,11 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk2-bin, librtaudio6, libzstd1'
|
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:oracular
|
||||
FROM ubuntu:mantic
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
COPY do_build.sh /root
|
||||
RUN chmod +x /root/do_build.sh
|
35
docker_builds/ubuntu_mantic/do_build.sh
Normal file
35
docker_builds/ubuntu_mantic/do_build.sh
Normal file
@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cd /root
|
||||
|
||||
# Install dependencies and tools
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk-bin, librtaudio6, libzstd1'
|
@ -6,14 +6,13 @@ cd /root
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
libcodec2-dev autoconf libtool xxd
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.1
|
||||
cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
@ -26,31 +25,11 @@ make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'
|
||||
sh make_debian_package.sh ./build 'libfftw3-bin, libglfw3, libvolk-bin, librtaudio6, libzstd1'
|
@ -1,56 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
cd /root
|
||||
|
||||
# Install dependencies and tools
|
||||
apt update
|
||||
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \
|
||||
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
|
||||
libcodec2-dev autoconf libtool xxd libspdlog-dev
|
||||
|
||||
# Install SDRPlay libraries
|
||||
SDRPLAY_ARCH=$(dpkg --print-architecture)
|
||||
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2.run
|
||||
7z x ./SDRplay_RSP_API-Linux-3.15.2
|
||||
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
|
||||
cp inc/* /usr/include/
|
||||
|
||||
# Install libperseus
|
||||
git clone https://github.com/Microtelecom/libperseus-sdr
|
||||
cd libperseus-sdr
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
ldconfig
|
||||
cd ..
|
||||
|
||||
# Install librfnm
|
||||
git clone https://github.com/AlexandreRouma/librfnm
|
||||
cd librfnm
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
# Install libfobos
|
||||
git clone https://github.com/AlexandreRouma/libfobos
|
||||
cd libfobos
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
|
||||
make -j2
|
||||
make install
|
||||
cd ../../
|
||||
|
||||
cd SDRPlusPlus
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
|
||||
make VERBOSE=1 -j2
|
||||
|
||||
cd ..
|
||||
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'
|
@ -28,8 +28,6 @@ bundle_is_not_to_be_installed() {
|
||||
if [ "$1" = "Security" ]; 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.15" ]; then echo 1; fi
|
||||
if [ "$1" = "libxml2.2.dylib" ]; then echo 1; fi
|
||||
}
|
||||
|
||||
# ========================= FOR INTERNAL USE ONLY =========================
|
||||
@ -228,4 +226,4 @@ bundle_sign() {
|
||||
fi
|
||||
|
||||
codesign --force --deep -s - $1
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ mkdir sdrpp_debian_amd64/DEBIAN
|
||||
# Create package info
|
||||
echo Create package info
|
||||
echo Package: sdrpp >> sdrpp_debian_amd64/DEBIAN/control
|
||||
echo Version: 1.2.1$BUILD_NO >> sdrpp_debian_amd64/DEBIAN/control
|
||||
echo Version: 1.2.0$BUILD_NO >> sdrpp_debian_amd64/DEBIAN/control
|
||||
echo Maintainer: Ryzerth >> 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
|
||||
|
@ -22,7 +22,7 @@ cp -R root/res/* $BUNDLE/Contents/Resources/
|
||||
bundle_create_icns root/res/icons/sdrpp.macos.png $BUNDLE/Contents/Resources/sdrpp
|
||||
|
||||
# Create the property list
|
||||
bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.2.1 sdrp sdrpp sdrpp $BUNDLE/Contents/Info.plist
|
||||
bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.2.0 sdrp sdrpp sdrpp $BUNDLE/Contents/Info.plist
|
||||
|
||||
# ========================= Install binaries =========================
|
||||
|
||||
@ -35,14 +35,11 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/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/hermes_source/hermes_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/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/plutosdr_source/plutosdr_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfspace_source/rfspace_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_sdr_source/rtl_sdr_source.dylib
|
||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rtl_tcp_source/rtl_tcp_source.dylib
|
||||
|
@ -7,95 +7,86 @@ mkdir sdrpp_windows_x64
|
||||
cp -Recurse $root_dir/* sdrpp_windows_x64/
|
||||
|
||||
# Copy core
|
||||
cp $build_dir/Release/* sdrpp_windows_x64/
|
||||
cp $build_dir/Debug/* sdrpp_windows_x64/
|
||||
cp $build_dir/core/Debug/sdrpp_core.pdb sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/PothosSDR/bin/volk.dll' sdrpp_windows_x64/
|
||||
|
||||
# Copy source modules
|
||||
cp $build_dir/source_modules/airspy_source/Release/airspy_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/airspy_source/Debug/airspy_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/airspy.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/airspyhf_source/Release/airspyhf_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/airspyhf_source/Debug/airspyhf_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/airspyhf.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/audio_source/Release/audio_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/audio_source/Debug/audio_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/bladerf_source/Release/bladerf_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/bladerf_source/Debug/bladerf_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/bladeRF.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/file_source/Release/file_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/file_source/Debug/file_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/fobossdr_source/Release/fobossdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/RigExpert/Fobos/bin/fobos.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/hackrf_source/Release/hackrf_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/hackrf_source/Debug/hackrf_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/hermes_source/Debug/hermes_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/limesdr_source/Debug/limesdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/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/Debug/perseus_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/plutosdr_source/Release/plutosdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/plutosdr_source/Debug/plutosdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/libiio.dll' sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/PothosSDR/bin/libad9361.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/rfnm_source/Release/rfnm_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/RFNM/bin/rfnm.dll' sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/RFNM/bin/spdlog.dll' sdrpp_windows_x64/
|
||||
cp 'C:/Program Files/RFNM/bin/fmt.dll' sdrpp_windows_x64/
|
||||
cp $build_dir/source_modules/rfspace_source/Debug/rfspace_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/rfspace_source/Release/rfspace_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/rtl_sdr_source/Release/rtl_sdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/rtl_sdr_source/Debug/rtl_sdr_source.dll sdrpp_windows_x64/modules/
|
||||
cp 'C:/Program Files/PothosSDR/bin/rtlsdr.dll' sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/source_modules/rtl_tcp_source/Release/rtl_tcp_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/rtl_tcp_source/Debug/rtl_tcp_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/sdrplay_source/Release/sdrplay_source.dll sdrpp_windows_x64/modules/ -ErrorAction SilentlyContinue
|
||||
cp $build_dir/source_modules/sdrplay_source/Debug/sdrplay_source.dll sdrpp_windows_x64/modules/ -ErrorAction SilentlyContinue
|
||||
cp 'C:/Program Files/SDRplay/API/x64/sdrplay_api.dll' sdrpp_windows_x64/ -ErrorAction SilentlyContinue
|
||||
|
||||
cp $build_dir/source_modules/sdrpp_server_source/Release/sdrpp_server_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/sdrpp_server_source/Debug/sdrpp_server_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/source_modules/spyserver_source/Release/spyserver_source.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/source_modules/spyserver_source/Debug/spyserver_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
# cp $build_dir/source_modules/usrp_source/Release/usrp_source.dll sdrpp_windows_x64/modules/
|
||||
# cp $build_dir/source_modules/usrp_source/Debug/usrp_source.dll sdrpp_windows_x64/modules/
|
||||
|
||||
|
||||
# Copy sink modules
|
||||
cp $build_dir/sink_modules/audio_sink/Release/audio_sink.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/sink_modules/audio_sink/Debug/audio_sink.dll sdrpp_windows_x64/modules/
|
||||
cp "C:/Program Files (x86)/RtAudio/bin/rtaudio.dll" sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/sink_modules/network_sink/Release/network_sink.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/sink_modules/network_sink/Debug/network_sink.dll sdrpp_windows_x64/modules/
|
||||
|
||||
|
||||
# Copy decoder modules
|
||||
cp $build_dir/decoder_modules/m17_decoder/Release/m17_decoder.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/decoder_modules/m17_decoder/Debug/m17_decoder.dll sdrpp_windows_x64/modules/
|
||||
cp "C:/Program Files/codec2/lib/libcodec2.dll" sdrpp_windows_x64/
|
||||
|
||||
cp $build_dir/decoder_modules/meteor_demodulator/Release/meteor_demodulator.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/decoder_modules/meteor_demodulator/Debug/meteor_demodulator.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/decoder_modules/radio/Release/radio.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/decoder_modules/radio/Debug/radio.dll sdrpp_windows_x64/modules/
|
||||
|
||||
|
||||
# Copy misc modules
|
||||
cp $build_dir/misc_modules/discord_integration/Release/discord_integration.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/discord_integration/Debug/discord_integration.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/frequency_manager/Release/frequency_manager.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/frequency_manager/Debug/frequency_manager.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/iq_exporter/Release/iq_exporter.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/iq_exporter/Debug/iq_exporter.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/recorder/Release/recorder.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/recorder/Debug/recorder.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/rigctl_client/Release/rigctl_client.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/rigctl_client/Debug/rigctl_client.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/rigctl_server/Release/rigctl_server.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/rigctl_server/Debug/rigctl_server.dll sdrpp_windows_x64/modules/
|
||||
|
||||
cp $build_dir/misc_modules/scanner/Release/scanner.dll sdrpp_windows_x64/modules/
|
||||
cp $build_dir/misc_modules/scanner/Debug/scanner.dll sdrpp_windows_x64/modules/
|
||||
|
||||
|
||||
# Copy supporting libs
|
||||
|
12
min_broken/main.cpp
Normal file
12
min_broken/main.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
#include <mutex>
|
||||
|
||||
std::recursive_mutex mtx;
|
||||
|
||||
int main() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
printf("Works just fine!\n");
|
||||
|
||||
return 0;
|
||||
}
|
@ -168,9 +168,10 @@ public:
|
||||
writer.setSamplerate(samplerate);
|
||||
|
||||
// Open file
|
||||
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
||||
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
|
||||
std::string extension = ".wav";
|
||||
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, recMode, vfoName) + extension);
|
||||
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
|
||||
if (!writer.open(expandedPath)) {
|
||||
flog::error("Failed to open file for recording: {0}", expandedPath);
|
||||
return;
|
||||
@ -248,6 +249,7 @@ private:
|
||||
}
|
||||
ImGui::Columns(1, CONCAT("EndRecorderModeColumns##_", _this->name), false);
|
||||
ImGui::EndGroup();
|
||||
if (_this->recording) { style::endDisabled(); }
|
||||
|
||||
// Recording path
|
||||
if (_this->folderSelect.render("##_recorder_fold_" + _this->name)) {
|
||||
@ -282,11 +284,8 @@ private:
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
if (_this->recording) { style::endDisabled(); }
|
||||
|
||||
// Show additional audio options
|
||||
if (_this->recMode == RECORDER_MODE_AUDIO) {
|
||||
if (_this->recording) { style::beginDisabled(); }
|
||||
ImGui::LeftLabel("Stream");
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::Combo(CONCAT("##_recorder_stream_", _this->name), &_this->streamId, _this->audioStreams.txt)) {
|
||||
@ -295,7 +294,6 @@ private:
|
||||
config.conf[_this->name]["audioStream"] = _this->audioStreams.key(_this->streamId);
|
||||
config.release(true);
|
||||
}
|
||||
if (_this->recording) { style::endDisabled(); }
|
||||
|
||||
_this->updateAudioMeter(_this->audioLvl);
|
||||
ImGui::FillWidth();
|
||||
@ -451,7 +449,7 @@ private:
|
||||
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
||||
};
|
||||
|
||||
std::string genFileName(std::string templ, int mode, std::string name) {
|
||||
std::string genFileName(std::string templ, std::string type, std::string name) {
|
||||
// Get data
|
||||
time_t now = time(0);
|
||||
tm* ltm = localtime(&now);
|
||||
@ -461,9 +459,6 @@ private:
|
||||
freq += gui::waterfall.vfos[name]->generalOffset;
|
||||
}
|
||||
|
||||
// Select the recording type string
|
||||
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
||||
|
||||
// Format to string
|
||||
char freqStr[128];
|
||||
char hourStr[128];
|
||||
@ -472,7 +467,7 @@ private:
|
||||
char dayStr[128];
|
||||
char monStr[128];
|
||||
char yearStr[128];
|
||||
const char* modeStr = (recMode == RECORDER_MODE_AUDIO) ? "Unknown" : "IQ";
|
||||
const char* modeStr = "Unknown";
|
||||
sprintf(freqStr, "%.0lfHz", freq);
|
||||
sprintf(hourStr, "%02d", ltm->tm_hour);
|
||||
sprintf(minStr, "%02d", ltm->tm_min);
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include <gui/gui.h>
|
||||
#include <gui/style.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <chrono>
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "scanner",
|
||||
|
33
readme.md
33
readme.md
@ -41,13 +41,14 @@ To create a desktop shortcut, rightclick the exe and select `Send to -> Desktop
|
||||
|
||||
Download the latest release from [the Releases page](https://github.com/AlexandreRouma/SDRPlusPlus/releases) and extract to the directory of your choice.
|
||||
|
||||
Then, use apt to install it:
|
||||
Then, run:
|
||||
|
||||
```sh
|
||||
sudo apt install path/to/the/sdrpp_debian_amd64.deb
|
||||
sudo apt install libfftw3-dev libglfw3-dev libvolk2-dev libzstd-dev libairspyhf-dev libiio-dev libad9361-dev librtaudio-dev libhackrf-dev
|
||||
sudo dpkg -i sdrpp_debian_amd64.deb
|
||||
```
|
||||
|
||||
**IMPORTANT: You must install the drivers for your SDR. Follow instructions from your manufacturer as to how to do this on your particular distro.**
|
||||
If `libvolk2-dev` is not available, use `libvolk1-dev`.
|
||||
|
||||
### Arch-based
|
||||
|
||||
@ -324,16 +325,13 @@ Modules in beta are still included in releases for the most part but not enabled
|
||||
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
|
||||
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
|
||||
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
||||
| fobossdr_source | Working | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
|
||||
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
|
||||
| harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ✅ |
|
||||
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
|
||||
| kcsdr_source | Unfinished | libkcsdr | OPT_BUILD_KCSDR_SOURCE | ⛔ | ⛔ | ⛔ |
|
||||
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
|
||||
| network_source | Beta | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | ✅ |
|
||||
| network_source | Unfinished | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | ⛔ |
|
||||
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
|
||||
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
|
||||
| rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | ✅ | ✅ |
|
||||
| rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | ⛔ | ⛔ |
|
||||
| rfspace_source | Working | - | OPT_BUILD_RFSPACE_SOURCE | ✅ | ✅ | ✅ |
|
||||
| rtl_sdr_source | Working | librtlsdr | OPT_BUILD_RTL_SDR_SOURCE | ✅ | ✅ | ✅ |
|
||||
| rtl_tcp_source | Working | - | OPT_BUILD_RTL_TCP_SOURCE | ✅ | ✅ | ✅ |
|
||||
@ -341,9 +339,9 @@ Modules in beta are still included in releases for the most part but not enabled
|
||||
| sdrpp_server_source | Working | - | OPT_BUILD_SDRPP_SERVER_SOURCE | ✅ | ✅ | ✅ |
|
||||
| soapy_source | Deprecated | soapysdr | OPT_BUILD_SOAPY_SOURCE | ⛔ | ⛔ | ⛔ |
|
||||
| spectran_source | Unfinished | RTSA Suite | OPT_BUILD_SPECTRAN_SOURCE | ⛔ | ⛔ | ⛔ |
|
||||
| spectran_http_source | Beta | - | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅ | ✅ | ✅ |
|
||||
| spectran_http_source | Beta | - | OPT_BUILD_SPECTRAN_HTTP_SOURCE | ✅ | ✅ | ⛔ |
|
||||
| spyserver_source | Working | - | OPT_BUILD_SPYSERVER_SOURCE | ✅ | ✅ | ✅ |
|
||||
| usrp_source | Beta | libuhd | OPT_BUILD_USRP_SOURCE | ⛔ | ⛔ | ✅ |
|
||||
| usrp_source | Beta | libuhd | OPT_BUILD_USRP_SOURCE | ⛔ | ⛔ | ⛔ |
|
||||
|
||||
## Sinks
|
||||
|
||||
@ -352,22 +350,20 @@ 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) |
|
||||
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
|
||||
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
|
||||
| new_portaudio_sink | Working | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
| portaudio_sink | Working | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
| new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
| portaudio_sink | Beta | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||
|
||||
## Decoders
|
||||
|
||||
| Name | Stage | Dependencies | Option | Built by default| Built in Release | Enabled in SDR++ by default |
|
||||
|---------------------|------------|--------------|-------------------------------|:---------------:|:----------------:|:---------------------------:|
|
||||
| atv_decoder | Unfinished | - | OPT_BUILD_ATV_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
| dab_decoder | Unfinished | - | OPT_BUILD_DAB_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
| falcon9_decoder | Unfinished | ffplay | OPT_BUILD_FALCON9_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
| kgsstv_decoder | Unfinished | - | OPT_BUILD_KGSSTV_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
| m17_decoder | Working | - | OPT_BUILD_M17_DECODER | ⛔ | ✅ | ⛔ |
|
||||
| meteor_demodulator | Working | - | OPT_BUILD_METEOR_DEMODULATOR | ✅ | ✅ | ⛔ |
|
||||
| pager_decoder | Unfinished | - | OPT_BUILD_PAGER_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
| radio | Working | - | OPT_BUILD_RADIO | ✅ | ✅ | ✅ |
|
||||
| radio | Unfinished | - | OPT_BUILD_VOR_RECEIVER | ⛔ | ⛔ | ⛔ |
|
||||
| weather_sat_decoder | Unfinished | - | OPT_BUILD_WEATHER_SAT_DECODER | ⛔ | ⛔ | ⛔ |
|
||||
|
||||
## Misc
|
||||
@ -420,8 +416,8 @@ If you still have an issue, please open an issue about it or ask on the discord.
|
||||
|
||||
# Contributing
|
||||
|
||||
Feel free to submit band plans via the GitHub issue tracker.
|
||||
For code changes, please create a feature request instead.
|
||||
Feel free to submit pull requests and report bugs via the GitHub issue tracker.
|
||||
I will soon publish a contributing.md listing the code style to use.
|
||||
|
||||
# Credits
|
||||
|
||||
@ -441,18 +437,15 @@ For code changes, please create a feature request instead.
|
||||
* Flinger Films
|
||||
* [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ)
|
||||
* gringogrigio
|
||||
* Jandro
|
||||
* Jeff Moe
|
||||
* Joe Cupano
|
||||
* KD1SQ
|
||||
* Kezza
|
||||
* Krys Kamieniecki
|
||||
* Lee Donaghy
|
||||
* Lee (KD1SQ)
|
||||
* Lee KD1SQ
|
||||
* .lozenge. (Hank Hill)
|
||||
* Martin Herren (HB9FXX)
|
||||
* NeoVilsonWong
|
||||
* Nitin (VU2JEK)
|
||||
* ON4MU
|
||||
* [Passion-Radio.com](https://passion-radio.com/)
|
||||
* Paul Maine
|
||||
|
@ -2,8 +2,8 @@
|
||||
"name": "France",
|
||||
"country_name": "France",
|
||||
"country_code": "FR",
|
||||
"author_name": "Fred F4EED, Armand31",
|
||||
"author_url": "http://f4eed.wordpress.com, https://github.com/Armand31",
|
||||
"author_name": "Fred F4EED",
|
||||
"author_url": "http://f4eed.wordpress.com",
|
||||
"bands": [
|
||||
{
|
||||
"name": "137KHz - Radioamateur",
|
||||
@ -355,7 +355,7 @@
|
||||
"end": 54000000
|
||||
},
|
||||
{
|
||||
"name": "Radiodiffusion - Bande FM",
|
||||
"name": "Bande FM - Radiodif.",
|
||||
"type": "broadcast",
|
||||
"start": 80000000,
|
||||
"end": 108000000
|
||||
@ -396,12 +396,6 @@
|
||||
"start": 162362500,
|
||||
"end": 162587500
|
||||
},
|
||||
{
|
||||
"name": "Radiodiffusion - Bande DAB",
|
||||
"type": "broadcast",
|
||||
"start": 174000000,
|
||||
"end": 223000000
|
||||
},
|
||||
{
|
||||
"name": "Military Aviation",
|
||||
"type": "military",
|
||||
@ -414,12 +408,6 @@
|
||||
"start": 240000000,
|
||||
"end": 270000000
|
||||
},
|
||||
{
|
||||
"name": "Police (TETRAPOL)",
|
||||
"type": "military",
|
||||
"start": 380000000,
|
||||
"end": 400000000
|
||||
},
|
||||
{
|
||||
"name": "70cm - Radioamateur",
|
||||
"type": "amateur",
|
||||
@ -432,24 +420,12 @@
|
||||
"start": 446000000,
|
||||
"end": 446200000
|
||||
},
|
||||
{
|
||||
"name": "TNT (DVB-T)",
|
||||
"type": "broadcast",
|
||||
"start": 470000000,
|
||||
"end": 694000000
|
||||
},
|
||||
{
|
||||
"name": "23cm - Radioamateur",
|
||||
"type": "amateur",
|
||||
"start": 1240000000,
|
||||
"end": 1300000000
|
||||
},
|
||||
{
|
||||
"name": "Radiodiffusion - Bande DAB",
|
||||
"type": "broadcast",
|
||||
"start": 1452000000,
|
||||
"end": 1492000000
|
||||
},
|
||||
{
|
||||
"name": "13cm - Radioamateur",
|
||||
"type": "amateur",
|
||||
|
@ -1,231 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
@ -1,549 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
@ -1,285 +0,0 @@
|
||||
{
|
||||
"name": "Turkey",
|
||||
"country_name": "Turkey",
|
||||
"country_code": "TR",
|
||||
"author_name": "Yunus TA2PEA",
|
||||
"author_url": "https://github.com/ycanerol",
|
||||
"bands": [
|
||||
{
|
||||
"name": "LW",
|
||||
"type": "amateur",
|
||||
"start": 135700,
|
||||
"end": 137800
|
||||
},
|
||||
{
|
||||
"name": "630m",
|
||||
"type": "amateur",
|
||||
"start": 472000,
|
||||
"end": 479000
|
||||
},
|
||||
{
|
||||
"name": "160m",
|
||||
"type": "amateur",
|
||||
"start": 1810000,
|
||||
"end": 1850000
|
||||
},
|
||||
{
|
||||
"name": "80m",
|
||||
"type": "amateur",
|
||||
"start": 3500000,
|
||||
"end": 3800000
|
||||
},
|
||||
{
|
||||
"name": "60m",
|
||||
"type": "amateur",
|
||||
"start": 5351500,
|
||||
"end": 5366500
|
||||
},
|
||||
{
|
||||
"name": "40m",
|
||||
"type": "amateur",
|
||||
"start": 7000000,
|
||||
"end": 7200000
|
||||
},
|
||||
{
|
||||
"name": "30m",
|
||||
"type": "amateur",
|
||||
"start": 10100000,
|
||||
"end": 10150000
|
||||
},
|
||||
{
|
||||
"name": "20m",
|
||||
"type": "amateur",
|
||||
"start": 14000000,
|
||||
"end": 14350000
|
||||
},
|
||||
{
|
||||
"name": "17m",
|
||||
"type": "amateur",
|
||||
"start": 18068000,
|
||||
"end": 18168000
|
||||
},
|
||||
{
|
||||
"name": "15m",
|
||||
"type": "amateur",
|
||||
"start": 21000000,
|
||||
"end": 21450000
|
||||
},
|
||||
{
|
||||
"name": "12m",
|
||||
"type": "amateur",
|
||||
"start": 24890000,
|
||||
"end": 24990000
|
||||
},
|
||||
{
|
||||
"name": "CB",
|
||||
"type": "other",
|
||||
"start": 26565000,
|
||||
"end": 27405000
|
||||
},
|
||||
{
|
||||
"name": "Pagers",
|
||||
"type": "amateur",
|
||||
"start": 27750000,
|
||||
"end": 28000000
|
||||
},
|
||||
{
|
||||
"name": "10m",
|
||||
"type": "amateur",
|
||||
"start": 28000000,
|
||||
"end": 29700000
|
||||
},
|
||||
{
|
||||
"name": "6m",
|
||||
"type": "amateur",
|
||||
"start": 50030000,
|
||||
"end": 51000000
|
||||
},
|
||||
{
|
||||
"name": "FM",
|
||||
"type": "broadcast",
|
||||
"start": 87500000,
|
||||
"end": 108000000
|
||||
},
|
||||
{
|
||||
"name": "Airband VOR/ILS",
|
||||
"type": "aviation",
|
||||
"start": 108000000,
|
||||
"end": 117975000
|
||||
},
|
||||
{
|
||||
"name": "Airband Voice",
|
||||
"type": "aviation",
|
||||
"start": 117975000,
|
||||
"end": 137000000
|
||||
},
|
||||
{
|
||||
"name": "2m",
|
||||
"type": "amateur",
|
||||
"start": 144000000,
|
||||
"end": 146000000
|
||||
},
|
||||
{
|
||||
"name": "Sayac Okuma",
|
||||
"type": "other",
|
||||
"start": 169400000,
|
||||
"end": 169475000
|
||||
},
|
||||
{
|
||||
"name": "Pagers",
|
||||
"type": "other",
|
||||
"start": 167000000,
|
||||
"end": 167100000
|
||||
},
|
||||
{
|
||||
"name": "Public announcement systems",
|
||||
"type": "other",
|
||||
"start": 173882500,
|
||||
"end": 174000000
|
||||
},
|
||||
{
|
||||
"name": "DVB-T",
|
||||
"type": "broadcast",
|
||||
"start": 174000000,
|
||||
"end": 216000000
|
||||
},
|
||||
{
|
||||
"name": "T-DAB",
|
||||
"type": "broadcast",
|
||||
"start": 216000000,
|
||||
"end": 233000000
|
||||
},
|
||||
{
|
||||
"name": "ILS-Glide Path",
|
||||
"type": "aviation",
|
||||
"start": 328600000,
|
||||
"end": 335400000
|
||||
},
|
||||
{
|
||||
"name": "Public Safety/Emergency",
|
||||
"type": "other",
|
||||
"start": 380000000,
|
||||
"end": 385000000
|
||||
},
|
||||
{
|
||||
"name": "Public Safety/Emergency",
|
||||
"type": "other",
|
||||
"start": 390000000,
|
||||
"end": 395000000
|
||||
},
|
||||
{
|
||||
"name": "70cm",
|
||||
"type": "amateur",
|
||||
"start": 430200000,
|
||||
"end": 430700000
|
||||
},
|
||||
{
|
||||
"name": "70cm-RepeaterRX",
|
||||
"type": "amateur",
|
||||
"start": 431550000,
|
||||
"end": 431825000
|
||||
},
|
||||
{
|
||||
"name": "70cm",
|
||||
"type": "amateur",
|
||||
"start": 432000000,
|
||||
"end": 432975000
|
||||
},
|
||||
{
|
||||
"name": "70cm",
|
||||
"type": "amateur",
|
||||
"start": 433400000,
|
||||
"end": 434000000
|
||||
},
|
||||
{
|
||||
"name": "70cm",
|
||||
"type": "amateur",
|
||||
"start": 435000000,
|
||||
"end": 438000000
|
||||
},
|
||||
{
|
||||
"name": "70cm-RepeaterTX",
|
||||
"type": "amateur",
|
||||
"start": 439150000,
|
||||
"end": 439425000
|
||||
},
|
||||
{
|
||||
"name": "Public announcement systems",
|
||||
"type": "other",
|
||||
"start": 445250000,
|
||||
"end": 445462500
|
||||
},
|
||||
{
|
||||
"name": "PMR446",
|
||||
"type": "other",
|
||||
"start": 446006250,
|
||||
"end": 446196875
|
||||
},
|
||||
{
|
||||
"name": "RFID",
|
||||
"type": "other",
|
||||
"start": 865000000,
|
||||
"end": 868000000
|
||||
},
|
||||
{
|
||||
"name": "RFID",
|
||||
"type": "other",
|
||||
"start": 916100000,
|
||||
"end": 918900000
|
||||
},
|
||||
{
|
||||
"name": "23cm",
|
||||
"type": "amateur",
|
||||
"start": 1240000000,
|
||||
"end": 1300000000
|
||||
},
|
||||
{
|
||||
"name": "DECT",
|
||||
"type": "other",
|
||||
"start": 1880000000,
|
||||
"end": 1900000000
|
||||
},
|
||||
{
|
||||
"name": "5GHz",
|
||||
"type": "amateur",
|
||||
"start": 5650000000,
|
||||
"end": 5670000000
|
||||
},
|
||||
{
|
||||
"name": "5GHz",
|
||||
"type": "amateur",
|
||||
"start": 5820000000,
|
||||
"end": 5850000000
|
||||
},
|
||||
{
|
||||
"name": "3cm",
|
||||
"type": "amateur",
|
||||
"start": 104500000000,
|
||||
"end": 104520000000
|
||||
},
|
||||
{
|
||||
"name": "24GHz",
|
||||
"type": "amateur",
|
||||
"start": 24000000000,
|
||||
"end": 24050000000
|
||||
},
|
||||
{
|
||||
"name": "47GHz",
|
||||
"type": "amateur",
|
||||
"start": 47000000000,
|
||||
"end": 47200000000
|
||||
},
|
||||
{
|
||||
"name": "75GHz",
|
||||
"type": "amateur",
|
||||
"start": 75500000000,
|
||||
"end": 7600000000
|
||||
},
|
||||
{
|
||||
"name": "134GHz",
|
||||
"type": "amateur",
|
||||
"start": 134000000000,
|
||||
"end": 142000000000
|
||||
}
|
||||
]
|
||||
}
|
@ -217,19 +217,14 @@ private:
|
||||
}
|
||||
|
||||
void startServer() {
|
||||
try {
|
||||
if (modeId == SINK_MODE_TCP) {
|
||||
listener = net::listen(hostname, port);
|
||||
if (listener) {
|
||||
listener->acceptAsync(clientHandler, this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
|
||||
if (modeId == SINK_MODE_TCP) {
|
||||
listener = net::listen(hostname, port);
|
||||
if (listener) {
|
||||
listener->acceptAsync(clientHandler, this);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
flog::error("Failed to open socket: {}", e.what());
|
||||
else {
|
||||
conn = net::openUDP("0.0.0.0", port, hostname, port, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,9 +183,6 @@ private:
|
||||
static void start(void* ctx) {
|
||||
AudioSourceModule* _this = (AudioSourceModule*)ctx;
|
||||
if (_this->running) { return; }
|
||||
|
||||
// If no device is selected, give up
|
||||
if (_this->selectedDevice.empty()) { return; }
|
||||
|
||||
// Stream options
|
||||
RtAudio::StreamParameters parameters;
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <libbladeRF.h>
|
||||
#include <gui/smgui.h>
|
||||
#include <algorithm>
|
||||
#include <utils/optionlist.h>
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
@ -38,10 +37,6 @@ public:
|
||||
BladeRFSourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
// Define clocks
|
||||
clocks.define("onboard", "On-Board", CLOCK_SELECT_ONBOARD);
|
||||
clocks.define("external", "External", CLOCK_SELECT_EXTERNAL);
|
||||
|
||||
sampleRate = 1000000.0;
|
||||
|
||||
handler.ctx = this;
|
||||
@ -272,15 +267,6 @@ public:
|
||||
}
|
||||
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
|
||||
if (config.conf["devices"][selectedSerial].contains("gainMode")) {
|
||||
std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
|
||||
@ -378,7 +364,6 @@ private:
|
||||
if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
|
||||
|
||||
// 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_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);
|
||||
@ -501,19 +486,6 @@ 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
|
||||
SmGui::LeftLabel("Gain control mode");
|
||||
SmGui::FillWidth();
|
||||
@ -565,15 +537,6 @@ 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() {
|
||||
int16_t* buffer = new int16_t[bufferSize * 2];
|
||||
bladerf_metadata meta;
|
||||
@ -602,7 +565,6 @@ private:
|
||||
int devId = 0;
|
||||
int srId = 0;
|
||||
int bwId = 0;
|
||||
int clkId = 0;
|
||||
int chanId = 0;
|
||||
int gainMode = 0;
|
||||
bool streamingEnabled = false;
|
||||
@ -618,8 +580,8 @@ private:
|
||||
std::string sampleRatesTxt;
|
||||
std::vector<uint64_t> bandwidths;
|
||||
std::string bandwidthsTxt;
|
||||
|
||||
std::string channelNamesTxt;
|
||||
OptionList<std::string, bladerf_clock_select> clocks;
|
||||
|
||||
int bufferSize;
|
||||
struct bladerf_stream* rxStream;
|
||||
|
@ -1,21 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(fobossdr_source)
|
||||
|
||||
file(GLOB SRC "src/*.cpp")
|
||||
|
||||
include(${SDRPP_MODULE_CMAKE})
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_link_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/lib/")
|
||||
target_include_directories(fobossdr_source PRIVATE "C:/Program Files/RigExpert/Fobos/include/")
|
||||
target_link_libraries(fobossdr_source PRIVATE fobos)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(LIBFOBOS REQUIRED libfobos)
|
||||
|
||||
target_include_directories(fobossdr_source PRIVATE ${LIBFOBOS_INCLUDE_DIRS})
|
||||
target_link_directories(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARY_DIRS})
|
||||
target_link_libraries(fobossdr_source PRIVATE ${LIBFOBOS_LIBRARIES})
|
||||
endif ()
|
@ -1,560 +0,0 @@
|
||||
#include <imgui.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/smgui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <utils/optionlist.h>
|
||||
#include <atomic>
|
||||
#include <fobos.h>
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "fobossdr_source",
|
||||
/* Description: */ "FobosSDR Source Module",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ -1
|
||||
};
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
// Work around for the fobos API not including
|
||||
#define FOBOS_LNA_GAIN_MIN 1
|
||||
#define FOBOS_LNA_GAIN_MAX 3
|
||||
#define FOBOS_VGA_GAIN_MIN 0
|
||||
#define FOBOS_VGA_GAIN_MAX 31
|
||||
|
||||
class FobosSDRSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
FobosSDRSourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
sampleRate = 50000000.0;
|
||||
|
||||
// Initialize the DDC
|
||||
ddc.init(&ddcIn, 50e6, 50e6, 50e6, 0.0);
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
handler.deselectHandler = menuDeselected;
|
||||
handler.menuHandler = menuHandler;
|
||||
handler.startHandler = start;
|
||||
handler.stopHandler = stop;
|
||||
handler.tuneHandler = tune;
|
||||
handler.stream = &ddc.out;
|
||||
|
||||
// Refresh devices
|
||||
refresh();
|
||||
|
||||
// Select device from config
|
||||
config.acquire();
|
||||
std::string devSerial = config.conf["device"];
|
||||
config.release();
|
||||
select(devSerial);
|
||||
|
||||
sigpath::sourceManager.registerSource("FobosSDR", &handler);
|
||||
}
|
||||
|
||||
~FobosSDRSourceModule() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
void postInit() {}
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
enum Port {
|
||||
PORT_RF,
|
||||
PORT_HF1,
|
||||
PORT_HF2
|
||||
};
|
||||
|
||||
private:
|
||||
std::string getBandwdithScaled(double bw) {
|
||||
char buf[1024];
|
||||
if (bw >= 1000000.0) {
|
||||
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
|
||||
}
|
||||
else if (bw >= 1000.0) {
|
||||
sprintf(buf, "%.1lfKHz", bw / 1000.0);
|
||||
}
|
||||
else {
|
||||
sprintf(buf, "%.1lfHz", bw);
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
devices.clear();
|
||||
|
||||
// Get device list
|
||||
char serials[1024];
|
||||
memset(serials, 0, sizeof(serials));
|
||||
int devCount = fobos_rx_list_devices(serials);
|
||||
if (devCount < 0) {
|
||||
flog::error("Failed to get device list: {}", devCount);
|
||||
return;
|
||||
}
|
||||
|
||||
// If no device, give up
|
||||
if (!devCount) { return; }
|
||||
|
||||
// Generate device entries
|
||||
const char* _serials = serials;
|
||||
int index = 0;
|
||||
while (*_serials) {
|
||||
// Read serial until space
|
||||
std::string serial = "";
|
||||
while (*_serials) {
|
||||
// Get a character
|
||||
char c = *(_serials++);
|
||||
|
||||
// If it's a space, we're done
|
||||
if (c == ' ') { break; }
|
||||
|
||||
// Otherwise, add it to the string
|
||||
serial += c;
|
||||
}
|
||||
|
||||
// Create entry
|
||||
devices.define(serial, serial, index++);
|
||||
}
|
||||
}
|
||||
|
||||
void select(const std::string& serial) {
|
||||
// If there are no devices, give up
|
||||
if (devices.empty()) {
|
||||
selectedSerial.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the serial was not found, select the first available serial
|
||||
if (!devices.keyExists(serial)) {
|
||||
select(devices.key(0));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the ID in the list
|
||||
int id = devices.keyId(serial);
|
||||
selectedDevId = devices[id];
|
||||
|
||||
// Open the device
|
||||
fobos_dev_t* dev;
|
||||
int err = fobos_rx_open(&dev, selectedDevId);
|
||||
if (err) {
|
||||
flog::error("Failed to open device: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get a list of supported samplerates
|
||||
double srList[128];
|
||||
unsigned int srCount;
|
||||
err = fobos_rx_get_samplerates(dev, srList, &srCount);
|
||||
if (err) {
|
||||
flog::error("Failed to get samplerate list: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate samplerate list
|
||||
samplerates.clear();
|
||||
for (int i = 0; i < srCount; i++) {
|
||||
std::string str = getBandwdithScaled(srList[i]);
|
||||
samplerates.define(srList[i], str, srList[i]);
|
||||
}
|
||||
|
||||
// Add some custom samplerates
|
||||
samplerates.define(5e6, "5.0MHz", 5e6);
|
||||
samplerates.define(2.5e6, "2.5MHz", 2.5e6);
|
||||
samplerates.define(1.25e6, "1.25MHz", 1.25e6);
|
||||
|
||||
// Define the ports
|
||||
ports.clear();
|
||||
ports.define("rf", "RF", PORT_RF);
|
||||
ports.define("hf1", "HF1", PORT_HF1);
|
||||
ports.define("hf2", "HF2", PORT_HF2);
|
||||
|
||||
// Define clock sources
|
||||
clockSources.clear();
|
||||
clockSources.define("internal", "Internal", 0);
|
||||
clockSources.define("external", "External", 1);
|
||||
|
||||
// Close the device
|
||||
fobos_rx_close(dev);
|
||||
|
||||
// Save serial number
|
||||
selectedSerial = serial;
|
||||
devId = id;
|
||||
|
||||
// Load default options
|
||||
sampleRate = 50e6;
|
||||
srId = samplerates.valueId(sampleRate);
|
||||
port = PORT_RF;
|
||||
portId = ports.valueId(port);
|
||||
clkSrcId = clockSources.nameId("Internal");
|
||||
lnaGain = 0;
|
||||
vgaGain = 0;
|
||||
|
||||
// Load config
|
||||
config.acquire();
|
||||
if (config.conf["devices"][selectedSerial].contains("samplerate")) {
|
||||
int desiredSr = config.conf["devices"][selectedSerial]["samplerate"];
|
||||
if (samplerates.keyExists(desiredSr)) {
|
||||
srId = samplerates.keyId(desiredSr);
|
||||
sampleRate = samplerates[srId];
|
||||
}
|
||||
}
|
||||
if (config.conf["devices"][selectedSerial].contains("port")) {
|
||||
std::string desiredPort = config.conf["devices"][selectedSerial]["port"];
|
||||
if (ports.keyExists(desiredPort)) {
|
||||
portId = ports.keyId(desiredPort);
|
||||
port = ports[portId];
|
||||
}
|
||||
}
|
||||
if (config.conf["devices"][selectedSerial].contains("clkSrc")) {
|
||||
std::string desiredClkSrc = config.conf["devices"][selectedSerial]["clkSrc"];
|
||||
if (clockSources.keyExists(desiredClkSrc)) {
|
||||
clkSrcId = clockSources.keyId(desiredClkSrc);
|
||||
}
|
||||
}
|
||||
if (config.conf["devices"][selectedSerial].contains("lnaGain")) {
|
||||
lnaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["lnaGain"], FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX);
|
||||
}
|
||||
if (config.conf["devices"][selectedSerial].contains("vgaGain")) {
|
||||
vgaGain = std::clamp<int>(config.conf["devices"][selectedSerial]["vgaGain"], FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX);
|
||||
}
|
||||
config.release();
|
||||
|
||||
// Update the samplerate
|
||||
core::setInputSampleRate(sampleRate);
|
||||
}
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
flog::info("FobosSDRSourceModule '{0}': Menu Select!", _this->name);
|
||||
}
|
||||
|
||||
static void menuDeselected(void* ctx) {
|
||||
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||
flog::info("FobosSDRSourceModule '{0}': Menu Deselect!", _this->name);
|
||||
}
|
||||
|
||||
static void start(void* ctx) {
|
||||
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||
if (_this->running) { return; }
|
||||
|
||||
// Open the device
|
||||
int err = fobos_rx_open(&_this->openDev, _this->selectedDevId);
|
||||
if (err) {
|
||||
flog::error("Failed to open device: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the selected port
|
||||
_this->port = _this->ports[_this->portId];
|
||||
|
||||
// Configure the device
|
||||
double actualSr, actualFreq;
|
||||
fobos_rx_set_samplerate(_this->openDev, (_this->sampleRate >= 50e6) ? _this->sampleRate : 50e6, &actualSr);
|
||||
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
|
||||
fobos_rx_set_direct_sampling(_this->openDev, _this->port != PORT_RF);
|
||||
fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
|
||||
fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||
|
||||
// Configure the DDC
|
||||
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
|
||||
// Set the frequency
|
||||
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
|
||||
}
|
||||
else if (_this->port == PORT_RF) {
|
||||
// Set the frequency
|
||||
fobos_rx_set_frequency(_this->openDev, _this->freq, &actualFreq);
|
||||
|
||||
// Configure and start the DDC for decimation only
|
||||
_this->ddc.setInSamplerate(actualSr);
|
||||
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
|
||||
_this->ddc.setOffset(0.0);
|
||||
_this->ddc.start();
|
||||
}
|
||||
else {
|
||||
// Configure and start the DDC
|
||||
_this->ddc.setInSamplerate(actualSr);
|
||||
_this->ddc.setOutSamplerate(_this->sampleRate, _this->sampleRate);
|
||||
_this->ddc.setOffset(_this->freq);
|
||||
_this->ddc.start();
|
||||
}
|
||||
|
||||
// Compute buffer size (Lower than usual, but it's a workaround for their API having broken streaming)
|
||||
_this->bufferSize = _this->sampleRate / 400.0;
|
||||
|
||||
// Start streaming
|
||||
err = fobos_rx_start_sync(_this->openDev, _this->bufferSize);
|
||||
if (err) {
|
||||
flog::error("Failed to start stream: {}", err);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start worker
|
||||
_this->run = true;
|
||||
_this->workerThread = std::thread(&FobosSDRSourceModule::worker, _this);
|
||||
|
||||
_this->running = true;
|
||||
flog::info("FobosSDRSourceModule '{0}': Start!", _this->name);
|
||||
}
|
||||
|
||||
static void stop(void* ctx) {
|
||||
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||
if (!_this->running) { return; }
|
||||
_this->running = false;
|
||||
|
||||
// Stop worker
|
||||
_this->run = false;
|
||||
if (_this->port == PORT_RF && _this->sampleRate >= 50e6) {
|
||||
_this->ddc.out.stopWriter();
|
||||
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||
_this->ddc.out.clearWriteStop();
|
||||
}
|
||||
else {
|
||||
_this->ddcIn.stopWriter();
|
||||
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||
_this->ddcIn.clearWriteStop();
|
||||
}
|
||||
|
||||
// Stop streaming
|
||||
fobos_rx_stop_sync(_this->openDev);
|
||||
|
||||
// Stop the DDC
|
||||
_this->ddc.stop();
|
||||
|
||||
// Close the device
|
||||
fobos_rx_close(_this->openDev);
|
||||
|
||||
flog::info("FobosSDRSourceModule '{0}': Stop!", _this->name);
|
||||
}
|
||||
|
||||
static void tune(double freq, void* ctx) {
|
||||
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
if (_this->port == PORT_RF) {
|
||||
double actual; // Dummy, don't care
|
||||
fobos_rx_set_frequency(_this->openDev, freq, &actual);
|
||||
}
|
||||
else {
|
||||
_this->ddc.setOffset(freq);
|
||||
}
|
||||
}
|
||||
_this->freq = freq;
|
||||
flog::info("FobosSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||
}
|
||||
|
||||
static void menuHandler(void* ctx) {
|
||||
FobosSDRSourceModule* _this = (FobosSDRSourceModule*)ctx;
|
||||
|
||||
if (_this->running) { SmGui::BeginDisabled(); }
|
||||
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Combo(CONCAT("##_fobossdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
||||
_this->select(_this->devices.key(_this->devId));
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
config.acquire();
|
||||
config.conf["device"] = _this->selectedSerial;
|
||||
config.release(true);
|
||||
}
|
||||
|
||||
if (SmGui::Combo(CONCAT("##_fobossdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
|
||||
_this->sampleRate = _this->samplerates.value(_this->srId);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["samplerate"] = _this->samplerates.key(_this->srId);
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
SmGui::SameLine();
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Button(CONCAT("Refresh##_fobossdr_refr_", _this->name))) {
|
||||
_this->refresh();
|
||||
_this->select(_this->selectedSerial);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
SmGui::LeftLabel("Antenna Port");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_fobossdr_port_", _this->name), &_this->portId, _this->ports.txt)) {
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["port"] = _this->ports.key(_this->portId);
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (_this->running) { SmGui::EndDisabled(); }
|
||||
|
||||
SmGui::LeftLabel("Clock Source");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_fobossdr_clk_", _this->name), &_this->clkSrcId, _this->clockSources.txt)) {
|
||||
if (_this->running) {
|
||||
fobos_rx_set_clk_source(_this->openDev, _this->clockSources[_this->clkSrcId]);
|
||||
}
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["clkSrc"] = _this->clockSources.key(_this->clkSrcId);
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (_this->port == PORT_RF) {
|
||||
SmGui::LeftLabel("LNA Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_fobossdr_lna_gain_", _this->name), &_this->lnaGain, FOBOS_LNA_GAIN_MIN, FOBOS_LNA_GAIN_MAX)) {
|
||||
if (_this->running) {
|
||||
fobos_rx_set_lna_gain(_this->openDev, _this->lnaGain);
|
||||
}
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["lnaGain"] = _this->lnaGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
|
||||
SmGui::LeftLabel("VGA Gain");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_fobossdr_vga_gain_", _this->name), &_this->vgaGain, FOBOS_VGA_GAIN_MIN, FOBOS_VGA_GAIN_MAX)) {
|
||||
if (_this->running) {
|
||||
fobos_rx_set_vga_gain(_this->openDev, _this->vgaGain);
|
||||
}
|
||||
if (!_this->selectedSerial.empty()) {
|
||||
config.acquire();
|
||||
config.conf["devices"][_this->selectedSerial]["vgaGain"] = _this->vgaGain;
|
||||
config.release(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void worker() {
|
||||
// Select different processing depending on the mode
|
||||
if (port == PORT_RF && sampleRate >= 50e6) {
|
||||
while (run) {
|
||||
// Read samples
|
||||
unsigned int sampCount = 0;
|
||||
int err = fobos_rx_read_sync(openDev, (float*)ddc.out.writeBuf, &sampCount);
|
||||
if (err) { break; }
|
||||
|
||||
// Send out samples to the core
|
||||
if (!ddc.out.swap(sampCount)) { break; }
|
||||
}
|
||||
}
|
||||
else if (port == PORT_RF) {
|
||||
while (run) {
|
||||
// Read samples
|
||||
unsigned int sampCount = 0;
|
||||
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
|
||||
if (err) { break; }
|
||||
|
||||
// Send samples to the DDC
|
||||
if (!ddcIn.swap(sampCount)) { break; }
|
||||
}
|
||||
}
|
||||
else if (port == PORT_HF1) {
|
||||
while (run) {
|
||||
// Read samples
|
||||
unsigned int sampCount = 0;
|
||||
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
|
||||
if (err) { break; }
|
||||
|
||||
// Null out the HF2 samples
|
||||
for (int i = 0; i < sampCount; i++) {
|
||||
ddcIn.writeBuf[i].im = 0.0f;
|
||||
}
|
||||
|
||||
// Send samples to the DDC
|
||||
if (!ddcIn.swap(sampCount)) { break; }
|
||||
}
|
||||
}
|
||||
else if (port == PORT_HF2) {
|
||||
while (run) {
|
||||
// Read samples
|
||||
unsigned int sampCount = 0;
|
||||
int err = fobos_rx_read_sync(openDev, (float*)ddcIn.writeBuf, &sampCount);
|
||||
if (err) { break; }
|
||||
|
||||
// Null out the HF2 samples
|
||||
for (int i = 0; i < sampCount; i++) {
|
||||
ddcIn.writeBuf[i].re = 0.0f;
|
||||
}
|
||||
|
||||
// Send samples to the DDC
|
||||
if (!ddcIn.swap(sampCount)) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
double sampleRate;
|
||||
SourceManager::SourceHandler handler;
|
||||
bool running = false;
|
||||
double freq;
|
||||
|
||||
OptionList<std::string, int> devices;
|
||||
OptionList<int, double> samplerates;
|
||||
OptionList<std::string, Port> ports;
|
||||
OptionList<std::string, int> clockSources;
|
||||
int devId = 0;
|
||||
int srId = 0;
|
||||
int portId = 0;
|
||||
int clkSrcId = 0;
|
||||
Port port;
|
||||
int lnaGain = 0;
|
||||
int vgaGain = 0;
|
||||
std::string selectedSerial;
|
||||
int selectedDevId;
|
||||
|
||||
fobos_dev_t* openDev;
|
||||
|
||||
int bufferSize;
|
||||
std::thread workerThread;
|
||||
std::atomic<bool> run = false;
|
||||
|
||||
dsp::stream<dsp::complex_t> ddcIn;
|
||||
dsp::channel::RxVFO ddc;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
json def = json({});
|
||||
def["devices"] = json({});
|
||||
def["device"] = "";
|
||||
config.setPath(core::args["root"].s() + "/fobossdr_config.json");
|
||||
config.load(def);
|
||||
config.enableAutoSave();
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new FobosSDRSourceModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
delete (FobosSDRSourceModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(harogic_source)
|
||||
|
||||
file(GLOB SRC "src/*.cpp")
|
||||
|
||||
include(${SDRPP_MODULE_CMAKE})
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_link_directories(harogic_source PRIVATE "C:/radio/HTRA_API/x64/htra_api/")
|
||||
target_include_directories(harogic_source PRIVATE "C:/radio/HTRA_API/x64/htra_api/")
|
||||
target_link_libraries(harogic_source PRIVATE htra_api)
|
||||
else (MSVC)
|
||||
target_link_directories(harogic_source PRIVATE "/opt/htraapi/lib/${CMAKE_SYSTEM_PROCESSOR}/")
|
||||
target_include_directories(harogic_source PRIVATE "/opt/htraapi/inc/")
|
||||
target_link_libraries(harogic_source PRIVATE htraapi)
|
||||
endif ()
|
@ -1,510 +0,0 @@
|
||||
#include <imgui.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/smgui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <utils/optionlist.h>
|
||||
#include <htra_api.h>
|
||||
#include <atomic>
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "harogic_source",
|
||||
/* Description: */ "harogic Source Module",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ -1
|
||||
};
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
class HarogicSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
HarogicSourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
sampleRate = 61440000.0;
|
||||
|
||||
handler.ctx = this;
|
||||
handler.selectHandler = menuSelected;
|
||||
handler.deselectHandler = menuDeselected;
|
||||
handler.menuHandler = menuHandler;
|
||||
handler.startHandler = start;
|
||||
handler.stopHandler = stop;
|
||||
handler.tuneHandler = tune;
|
||||
handler.stream = &stream;
|
||||
|
||||
// Refresh devices
|
||||
refresh();
|
||||
|
||||
// Select first (TODO: Select from config)
|
||||
select("");
|
||||
|
||||
sigpath::sourceManager.registerSource("Harogic", &handler);
|
||||
}
|
||||
|
||||
~HarogicSourceModule() {
|
||||
|
||||
}
|
||||
|
||||
void postInit() {}
|
||||
|
||||
void enable() {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
void disable() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
bool isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
void refresh() {
|
||||
devices.clear();
|
||||
|
||||
// Set up the device parameters
|
||||
BootProfile_TypeDef profile = {};
|
||||
profile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
|
||||
profile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortOnly;
|
||||
|
||||
// Working variables
|
||||
void* dev;
|
||||
BootInfo_TypeDef binfo;
|
||||
|
||||
for (int i = 0; i < 128; i++) {
|
||||
// Attempt to open the device with the given ID
|
||||
int ret = Device_Open(&dev, i, &profile, &binfo);
|
||||
if (ret < 0) { break; }
|
||||
|
||||
// Create serial string
|
||||
char serial[64];
|
||||
sprintf(serial, "%" PRIX64, binfo.DeviceInfo.DeviceUID);
|
||||
|
||||
// Add the device to the list
|
||||
devices.define(serial, serial, i);
|
||||
|
||||
// Close the device
|
||||
Device_Close(&dev);
|
||||
}
|
||||
}
|
||||
|
||||
void select(const std::string& serial) {
|
||||
// If there are no devices, give up
|
||||
if (devices.empty()) {
|
||||
selectedSerial.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the serial was not found, select the first available serial
|
||||
if (!devices.keyExists(serial)) {
|
||||
select(devices.key(0));
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the menu ID
|
||||
devId = devices.keyId(serial);
|
||||
selectedDevIndex = devices.value(devId);
|
||||
|
||||
// Set up the device parameters
|
||||
BootProfile_TypeDef bprofile = {};
|
||||
bprofile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
|
||||
bprofile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortOnly;
|
||||
|
||||
// Working variables
|
||||
BootInfo_TypeDef binfo;
|
||||
|
||||
// Attempt to open the device by ID
|
||||
void* dev;
|
||||
int ret = Device_Open(&dev, selectedDevIndex, &bprofile, &binfo);
|
||||
if (ret < 0) {
|
||||
flog::error("Could not open device: {}", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the default streaming parameters to query some info
|
||||
IQS_Profile_TypeDef profile;
|
||||
IQS_ProfileDeInit(&dev, &profile);
|
||||
|
||||
// Compute all available samplerates
|
||||
samplerates.clear();
|
||||
for (int i = 0; i < 8; i++) {
|
||||
double sr = profile.NativeIQSampleRate_SPS / (double)(1 << i);
|
||||
char buf[128];
|
||||
sprintf(buf, "%.02fMHz", sr / 1e6);
|
||||
samplerates.define(1 << i, buf, sr);
|
||||
}
|
||||
|
||||
// Define RX ports
|
||||
rxPorts.clear();
|
||||
rxPorts.define("external", "External", ExternalPort);
|
||||
rxPorts.define("internal", "Internal", InternalPort);
|
||||
rxPorts.define("ant", "ANT", ANT_Port);
|
||||
rxPorts.define("tr", "T/R", TR_Port);
|
||||
rxPorts.define("swr", "SWR", SWR_Port);
|
||||
rxPorts.define("int", "INT", INT_Port);
|
||||
|
||||
// Define gain strategies
|
||||
gainStategies.clear();
|
||||
gainStategies.define("lowNoise", "Low Noise", LowNoisePreferred);
|
||||
gainStategies.define("highLinearity", "High Linearity", HighLinearityPreferred);
|
||||
|
||||
// Define preamplifier modes
|
||||
preampModes.clear();
|
||||
preampModes.define("auto", "Auto", AutoOn);
|
||||
preampModes.define("off", "Off", ForcedOff);
|
||||
|
||||
// Define LO modes
|
||||
loModes.clear();
|
||||
loModes.define("auto", "Auto", LOOpt_Auto);
|
||||
loModes.define("speed", "Speed", LOOpt_Speed);
|
||||
loModes.define("spurs", "Spurs", LOOpt_Spur);
|
||||
loModes.define("phaseNoise", "Phase Noise", LOOpt_PhaseNoise);
|
||||
|
||||
// Close the device
|
||||
Device_Close(&dev);
|
||||
|
||||
// TODO: Load configuration
|
||||
sampleRate = samplerates.value(0);
|
||||
refLvl = 0;
|
||||
portId = rxPorts.valueId(ExternalPort);
|
||||
gainStratId = gainStategies.valueId(LowNoisePreferred);
|
||||
preampModeId = preampModes.valueId(AutoOn);
|
||||
ifAgc = false;
|
||||
loModeId = loModes.valueId(LOOpt_Auto);
|
||||
|
||||
// Update the samplerate
|
||||
core::setInputSampleRate(sampleRate);
|
||||
|
||||
// Save serial number
|
||||
selectedSerial = serial;
|
||||
}
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
flog::info("HarogicSourceModule '{0}': Menu Select!", _this->name);
|
||||
}
|
||||
|
||||
static void menuDeselected(void* ctx) {
|
||||
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||
flog::info("HarogicSourceModule '{0}': Menu Deselect!", _this->name);
|
||||
}
|
||||
|
||||
static void start(void* ctx) {
|
||||
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||
if (_this->running) { return; }
|
||||
|
||||
// Set up the device parameters
|
||||
BootProfile_TypeDef bprofile = {};
|
||||
bprofile.PhysicalInterface = PhysicalInterface_TypeDef::USB;
|
||||
bprofile.DevicePowerSupply = DevicePowerSupply_TypeDef::USBPortAndPowerPort;
|
||||
|
||||
// Working variables
|
||||
BootInfo_TypeDef binfo;
|
||||
|
||||
// Attempt to open the device by ID
|
||||
int ret = Device_Open(&_this->openDev, _this->selectedDevIndex, &bprofile, &binfo);
|
||||
if (ret < 0) {
|
||||
flog::error("Could not open device: {}", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the decimation amount
|
||||
int dec = _this->samplerates.key(_this->samplerates.valueId(_this->sampleRate));
|
||||
flog::debug("Using decimation factor: {}", dec);
|
||||
|
||||
// Decide to use either 8 or 16bit samples
|
||||
_this->sampsInt8 = (_this->sampleRate > 64e6);
|
||||
|
||||
// Get the default configuration
|
||||
IQS_ProfileDeInit(&_this->openDev, &_this->profile);
|
||||
|
||||
// Automatic configuration
|
||||
_this->profile.Atten = -1;
|
||||
_this->profile.BusTimeout_ms = 100;
|
||||
_this->profile.TriggerSource = Bus;
|
||||
_this->profile.TriggerMode = Adaptive;
|
||||
_this->profile.DataFormat = _this->sampsInt8 ? Complex8bit : Complex16bit;
|
||||
|
||||
// User selectable config
|
||||
_this->profile.CenterFreq_Hz = _this->freq;
|
||||
_this->profile.RefLevel_dBm = _this->refLvl;
|
||||
_this->profile.DecimateFactor = dec;
|
||||
_this->profile.RxPort = _this->rxPorts.value(_this->portId);
|
||||
_this->profile.GainStrategy = _this->gainStategies.value(_this->gainStratId);
|
||||
_this->profile.Preamplifier = _this->preampModes.value(_this->preampModeId);
|
||||
_this->profile.EnableIFAGC = _this->ifAgc;
|
||||
_this->profile.LOOptimization = _this->loModes.value(_this->loModeId);
|
||||
|
||||
// Apply the configuration
|
||||
IQS_StreamInfo_TypeDef info;
|
||||
ret = IQS_Configuration(&_this->openDev, &_this->profile, &_this->profile, &info);
|
||||
if (ret < 0) {
|
||||
flog::error("Could not configure device: {}", ret);
|
||||
Device_Close(&_this->openDev);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the stream configuration
|
||||
_this->bufferSize = info.PacketSamples;
|
||||
flog::debug("Got buffer size: {}", _this->bufferSize);
|
||||
|
||||
// Start the stream
|
||||
ret = IQS_BusTriggerStart(&_this->openDev);
|
||||
if (ret < 0) {
|
||||
flog::error("Could not start stream: {}", ret);
|
||||
Device_Close(&_this->openDev);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start worker
|
||||
_this->run = true;
|
||||
_this->workerThread = std::thread(&HarogicSourceModule::worker, _this);
|
||||
|
||||
_this->running = true;
|
||||
flog::info("HarogicSourceModule '{0}': Start!", _this->name);
|
||||
}
|
||||
|
||||
static void stop(void* ctx) {
|
||||
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||
if (!_this->running) { return; }
|
||||
_this->running = false;
|
||||
|
||||
// Stop worker
|
||||
_this->run = false;
|
||||
_this->stream.stopWriter();
|
||||
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||
_this->stream.clearWriteStop();
|
||||
|
||||
// Stop the stream
|
||||
IQS_BusTriggerStop(&_this->openDev);
|
||||
|
||||
// Close the device
|
||||
Device_Close(&_this->openDev);
|
||||
|
||||
flog::info("HarogicSourceModule '{0}': Stop!", _this->name);
|
||||
}
|
||||
|
||||
static void tune(double freq, void* ctx) {
|
||||
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
// Update the frequency in the configuration
|
||||
_this->profile.CenterFreq_Hz = freq;
|
||||
_this->applyProfile();
|
||||
}
|
||||
_this->freq = freq;
|
||||
flog::info("HarogicSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
||||
}
|
||||
|
||||
static void menuHandler(void* ctx) {
|
||||
HarogicSourceModule* _this = (HarogicSourceModule*)ctx;
|
||||
|
||||
if (_this->running) { SmGui::BeginDisabled(); }
|
||||
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Combo(CONCAT("##_harogic_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
||||
_this->select(_this->devices.key(_this->devId));
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
if (SmGui::Combo(CONCAT("##_harogic_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
|
||||
_this->sampleRate = _this->samplerates.value(_this->srId);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
SmGui::SameLine();
|
||||
SmGui::FillWidth();
|
||||
SmGui::ForceSync();
|
||||
if (SmGui::Button(CONCAT("Refresh##_harogic_refr_", _this->name))) {
|
||||
_this->refresh();
|
||||
_this->select(_this->selectedSerial);
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
if (_this->running) { SmGui::EndDisabled(); }
|
||||
|
||||
SmGui::LeftLabel("RX Port");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_harogic_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
|
||||
if (_this->running) {
|
||||
_this->profile.RxPort = _this->rxPorts.value(_this->portId);
|
||||
_this->applyProfile();
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
SmGui::LeftLabel("LO Mode");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_lo_mode_", _this->name), &_this->loModeId, _this->loModes.txt)) {
|
||||
if (_this->running) {
|
||||
_this->profile.LOOptimization = _this->loModes.value(_this->loModeId);
|
||||
_this->applyProfile();
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
SmGui::LeftLabel("Gain Mode");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_harogic_gain_mode_", _this->name), &_this->gainStratId, _this->gainStategies.txt)) {
|
||||
if (_this->running) {
|
||||
_this->profile.GainStrategy = _this->gainStategies.value(_this->gainStratId);
|
||||
_this->applyProfile();
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
SmGui::LeftLabel("Preamp Mode");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_harogic_preamp_mode_", _this->name), &_this->preampModeId, _this->preampModes.txt)) {
|
||||
if (_this->running) {
|
||||
_this->profile.Preamplifier = _this->preampModes.value(_this->preampModeId);
|
||||
_this->applyProfile();
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
SmGui::LeftLabel("Reference");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_harogic_ref_", _this->name), &_this->refLvl, _this->minRef, _this->maxRef)) {
|
||||
if (_this->running) {
|
||||
_this->profile.RefLevel_dBm = _this->refLvl;
|
||||
_this->applyProfile();
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
if (SmGui::Checkbox(CONCAT("IF AGC##_harogic_if_agc_", _this->name), &_this->ifAgc)) {
|
||||
if (_this->running) {
|
||||
_this->profile.EnableIFAGC = _this->ifAgc;
|
||||
_this->applyProfile();
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
}
|
||||
|
||||
void applyProfile() {
|
||||
// Acquire device
|
||||
std::lock_guard<std::mutex> lck(devMtx);
|
||||
|
||||
// Configure the device
|
||||
IQS_StreamInfo_TypeDef info;
|
||||
int ret = IQS_Configuration(&openDev, &profile, &profile, &info);
|
||||
if (ret < 0) {
|
||||
flog::error("Failed to apply tuning config: {}", ret);
|
||||
}
|
||||
|
||||
// Re-trigger the stream
|
||||
ret = IQS_BusTriggerStart(&openDev);
|
||||
if (ret < 0) {
|
||||
flog::error("Could not start stream: {}", ret);
|
||||
}
|
||||
}
|
||||
|
||||
void worker() {
|
||||
// Allocate sample buffer
|
||||
int realSamps = bufferSize*2;
|
||||
IQStream_TypeDef iqs;
|
||||
|
||||
// Define number of buffers per swap to maintain 200 fps
|
||||
int maxBufCount = STREAM_BUFFER_SIZE / bufferSize;
|
||||
int bufCount = (sampleRate / bufferSize) / 200;
|
||||
if (bufCount <= 0) { bufCount = 1; }
|
||||
if (bufCount > maxBufCount) { bufCount = maxBufCount; }
|
||||
int count = 0;
|
||||
|
||||
flog::debug("Swapping will be done {} buffers at a time", bufCount);
|
||||
|
||||
// Worker loop
|
||||
while (run) {
|
||||
// Read samples
|
||||
devMtx.lock();
|
||||
int ret = IQS_GetIQStream_PM1(&openDev, &iqs);
|
||||
devMtx.unlock();
|
||||
if (ret < 0) {
|
||||
if (ret == APIRETVAL_WARNING_BusTimeOut) {
|
||||
flog::warn("Stream timed out");
|
||||
continue;
|
||||
}
|
||||
else if (ret <= APIRETVAL_WARNING_IFOverflow && ret >= APIRETVAL_WARNING_ADCConfigError) {
|
||||
// Just warnings, do nothing
|
||||
}
|
||||
else {
|
||||
flog::error("Streaming error: {}", ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert them to floating point
|
||||
if (sampsInt8) {
|
||||
volk_8i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*bufferSize], (int8_t*)iqs.AlternIQStream, 128.0f, realSamps);
|
||||
}
|
||||
else {
|
||||
volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*bufferSize], (int16_t*)iqs.AlternIQStream, 32768.0f, realSamps);
|
||||
}
|
||||
|
||||
// Send them off if we have enough
|
||||
if (count >= bufCount) {
|
||||
count = 0;
|
||||
if (!stream.swap(bufferSize*bufCount)) { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string name;
|
||||
bool enabled = true;
|
||||
dsp::stream<dsp::complex_t> stream;
|
||||
double sampleRate;
|
||||
SourceManager::SourceHandler handler;
|
||||
bool running = false;
|
||||
double freq;
|
||||
|
||||
OptionList<std::string, int> devices;
|
||||
OptionList<int, double> samplerates;
|
||||
OptionList<std::string, RxPort_TypeDef> rxPorts;
|
||||
OptionList<std::string, GainStrategy_TypeDef> gainStategies;
|
||||
OptionList<std::string, PreamplifierState_TypeDef> preampModes;
|
||||
OptionList<std::string, LOOptimization_TypeDef> loModes;
|
||||
int devId = 0;
|
||||
int srId = 0;
|
||||
int refLvl = -30;
|
||||
int minRef = -100;
|
||||
int maxRef = 7;
|
||||
int portId = 0;
|
||||
int gainStratId = 0;
|
||||
int preampModeId = 0;
|
||||
int loModeId = 0;
|
||||
bool ifAgc = false;
|
||||
std::string selectedSerial;
|
||||
int selectedDevIndex;
|
||||
|
||||
void* openDev;
|
||||
IQS_Profile_TypeDef profile;
|
||||
|
||||
int bufferSize;
|
||||
std::thread workerThread;
|
||||
std::atomic<bool> run = false;
|
||||
std::mutex devMtx;
|
||||
bool sampsInt8;
|
||||
};
|
||||
|
||||
MOD_EXPORT void _INIT_() {
|
||||
// Nothing here
|
||||
}
|
||||
|
||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
||||
return new HarogicSourceModule(name);
|
||||
}
|
||||
|
||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
||||
delete (HarogicSourceModule*)instance;
|
||||
}
|
||||
|
||||
MOD_EXPORT void _END_() {
|
||||
// Nothing here
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
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/")
|
||||
|
||||
target_include_directories(hydrasdr_source PUBLIC "C:/Program Files/HydraSDR/include/")
|
||||
|
||||
target_link_libraries(hydrasdr_source PRIVATE hydrasdr)
|
||||
elseif (ANDROID)
|
||||
# TODO
|
||||
else (MSVC)
|
||||
# TODO
|
||||
endif ()
|
@ -1,635 +0,0 @@
|
||||
#include <imgui.h>
|
||||
#include <utils/flog.h>
|
||||
#include <module.h>
|
||||
#include <gui/gui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <core.h>
|
||||
#include <gui/style.h>
|
||||
#include <config.h>
|
||||
#include <gui/smgui.h>
|
||||
#include <hydrasdr.h>
|
||||
#include <utils/optionlist.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android_backend.h>
|
||||
#endif
|
||||
|
||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "hydrasdr_source",
|
||||
/* Description: */ "HydraSDR source module for SDR++",
|
||||
/* Author: */ "Ryzerth",
|
||||
/* Version: */ 0, 1, 0,
|
||||
/* Max instances */ 1
|
||||
};
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
class HydraSDRSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
HydraSDRSourceModule(std::string name) {
|
||||
this->name = name;
|
||||
|
||||
// Define the ports
|
||||
ports.define("rx0", "RX0", RF_PORT_RX0);
|
||||
ports.define("rx1", "RX1", RF_PORT_RX1);
|
||||
ports.define("rx2", "RX2", RF_PORT_RX2);
|
||||
|
||||
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::AIRSPY_VIDPIDS);
|
||||
if (devFd < 0) { return; }
|
||||
|
||||
// Get device info
|
||||
std::string fakeName = "HydraSDR USB";
|
||||
devList.push_back(0xDEADBEEF);
|
||||
devListTxt += fakeName;
|
||||
devListTxt += '\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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
1
source_modules/kcsdr_source/.gitignore
vendored
@ -1 +0,0 @@
|
||||
vendor/*
|
@ -1,10 +0,0 @@
|
||||
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)
|
@ -1,209 +0,0 @@
|
||||
#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;
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
#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
|
@ -1,324 +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 "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[] {
|
||||
2*sizeof(int8_t),
|
||||
2*sizeof(int16_t),
|
||||
2*sizeof(int32_t),
|
||||
2*sizeof(float),
|
||||
sizeof(int8_t)*2,
|
||||
sizeof(int16_t)*2,
|
||||
sizeof(int32_t)*2,
|
||||
sizeof(float)*2,
|
||||
};
|
||||
|
||||
class NetworkSourceModule : public ModuleManager::Instance {
|
||||
@ -58,6 +58,20 @@ public:
|
||||
handler.tuneHandler = tune;
|
||||
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
|
||||
// protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
|
||||
protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
|
||||
@ -72,8 +86,8 @@ public:
|
||||
// Load config
|
||||
config.acquire();
|
||||
if (config.conf[name].contains("samplerate")) {
|
||||
samplerate = config.conf[name]["samplerate"];
|
||||
tempSamplerate = samplerate;
|
||||
int sr = config.conf[name]["samplerate"];
|
||||
if (samplerates.keyExists(sr)) { samplerate = samplerates.value(samplerates.keyId(sr)); }
|
||||
}
|
||||
if (config.conf[name].contains("protocol")) {
|
||||
std::string protoStr = config.conf[name]["protocol"];
|
||||
@ -94,6 +108,7 @@ public:
|
||||
config.release();
|
||||
|
||||
// Set menu IDs
|
||||
srId = samplerates.valueId(samplerate);
|
||||
protoId = protocols.valueId(proto);
|
||||
sampTypeId = sampleTypes.valueId(sampType);
|
||||
|
||||
@ -213,24 +228,35 @@ private:
|
||||
if (_this->running) { SmGui::BeginDisabled(); }
|
||||
|
||||
// Hostname and port field
|
||||
if (SmGui::InputText(("##network_source_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
|
||||
if (ImGui::InputText(("##iq_exporter_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
|
||||
config.acquire();
|
||||
config.conf[_this->name]["host"] = _this->hostname;
|
||||
config.release(true);
|
||||
}
|
||||
SmGui::SameLine();
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::InputInt(("##network_source_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
|
||||
ImGui::SameLine();
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::InputInt(("##iq_exporter_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
|
||||
_this->port = std::clamp<int>(_this->port, 1, 65535);
|
||||
config.acquire();
|
||||
config.conf[_this->name]["port"] = _this->port;
|
||||
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
|
||||
SmGui::LeftLabel("Protocol");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(("##network_source_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
|
||||
ImGui::LeftLabel("Protocol");
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::Combo(("##iq_exporter_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
|
||||
_this->proto = _this->protocols.value(_this->protoId);
|
||||
config.acquire();
|
||||
config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
|
||||
@ -238,38 +264,15 @@ private:
|
||||
}
|
||||
|
||||
// Sample type selector
|
||||
SmGui::LeftLabel("Sample type");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(("##network_source_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
|
||||
ImGui::LeftLabel("Sample type");
|
||||
ImGui::FillWidth();
|
||||
if (ImGui::Combo(("##iq_exporter_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
|
||||
_this->sampType = _this->sampleTypes.value(_this->sampTypeId);
|
||||
config.acquire();
|
||||
config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
|
||||
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(); }
|
||||
}
|
||||
|
||||
@ -277,17 +280,14 @@ private:
|
||||
// Compute sizes
|
||||
int blockSize = samplerate / 200;
|
||||
int sampleSize = SAMPLE_TYPE_SIZE[sampType];
|
||||
|
||||
// Chose amount of bytes to attempt to read
|
||||
bool forceSize = (proto != PROTOCOL_UDP);
|
||||
int frameSize = sampleSize * (forceSize ? blockSize : STREAM_BUFFER_SIZE);
|
||||
int frameSize = blockSize*sampleSize;
|
||||
|
||||
// Allocate receive buffer
|
||||
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
|
||||
|
||||
while (true) {
|
||||
// Read samples from socket
|
||||
int bytes = sock->recv(buffer, frameSize, forceSize);
|
||||
int bytes = sock->recv(buffer, frameSize, true);
|
||||
if (bytes <= 0) { break; }
|
||||
|
||||
// Convert to CF32 (note: problem if partial sample)
|
||||
@ -325,7 +325,7 @@ private:
|
||||
double freq;
|
||||
|
||||
int samplerate = 1000000;
|
||||
int tempSamplerate = 1000000;
|
||||
int srId;
|
||||
Protocol proto = PROTOCOL_UDP;
|
||||
int protoId;
|
||||
SampleType sampType = SAMPLE_TYPE_INT16;
|
||||
@ -333,6 +333,7 @@ private:
|
||||
char hostname[1024] = "localhost";
|
||||
int port = 1234;
|
||||
|
||||
OptionList<int, int> samplerates;
|
||||
OptionList<std::string, Protocol> protocols;
|
||||
OptionList<std::string, SampleType> sampleTypes;
|
||||
|
||||
|
@ -23,12 +23,6 @@ SDRPP_MOD_INFO{
|
||||
|
||||
ConfigManager config;
|
||||
|
||||
const std::vector<const char*> deviceWhiteList = {
|
||||
"PlutoSDR",
|
||||
"ANTSDR",
|
||||
"LibreSDR"
|
||||
};
|
||||
|
||||
class PlutoSDRSourceModule : public ModuleManager::Instance {
|
||||
public:
|
||||
PlutoSDRSourceModule(std::string name) {
|
||||
@ -136,14 +130,7 @@ private:
|
||||
std::string duri = iio_context_info_get_uri(info);
|
||||
|
||||
// If the device is not a plutosdr, don't include it
|
||||
bool isPluto = false;
|
||||
for (const auto type : deviceWhiteList) {
|
||||
if (desc.find(type) != std::string::npos) {
|
||||
isPluto = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isPluto) {
|
||||
if (desc.find("PlutoSDR") == std::string::npos) {
|
||||
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
|
||||
continue;
|
||||
}
|
||||
@ -176,9 +163,6 @@ private:
|
||||
// Construct the device name
|
||||
std::string devName = '(' + backend + ") " + model + " [" + serial + ']';
|
||||
|
||||
// Skip duplicate devices
|
||||
if (devices.keyExists(desc) || devices.nameExists(devName) || devices.valueExists(duri)) { continue; }
|
||||
|
||||
// Save device
|
||||
devices.define(desc, devName, duri);
|
||||
}
|
||||
|
@ -5,17 +5,6 @@ file(GLOB SRC "src/*.cpp")
|
||||
|
||||
include(${SDRPP_MODULE_CMAKE})
|
||||
|
||||
if (MSVC)
|
||||
# Lib path
|
||||
target_link_directories(rfnm_source PRIVATE "C:/Program Files/RFNM/lib/")
|
||||
target_include_directories(rfnm_source PUBLIC "C:/Program Files/RFNM/include/")
|
||||
target_link_libraries(rfnm_source PRIVATE rfnm)
|
||||
else (MSVC)
|
||||
find_package(PkgConfig)
|
||||
|
||||
pkg_check_modules(LIBRFNM REQUIRED librfnm)
|
||||
|
||||
target_include_directories(rfnm_source PRIVATE ${LIBRFNM_INCLUDE_DIRS})
|
||||
target_link_directories(rfnm_source PRIVATE ${LIBRFNM_LIBRARY_DIRS})
|
||||
target_link_libraries(rfnm_source PRIVATE ${LIBRFNM_LIBRARIES})
|
||||
endif ()
|
||||
target_include_directories(rfnm_source PRIVATE "C:/Users/ryzerth/Documents/Code/librfnm/include/librfnm")
|
||||
target_link_directories(rfnm_source PRIVATE "C:/Users/ryzerth/Documents/Code/librfnm/build/Release")
|
||||
target_link_libraries(rfnm_source PRIVATE librfnm)
|
@ -3,10 +3,9 @@
|
||||
#include <gui/gui.h>
|
||||
#include <gui/smgui.h>
|
||||
#include <signal_path/signal_path.h>
|
||||
#include <librfnm/librfnm.h>
|
||||
#include <librfnm.h>
|
||||
#include <core.h>
|
||||
#include <utils/optionlist.h>
|
||||
#include <atomic>
|
||||
|
||||
SDRPP_MOD_INFO{
|
||||
/* Name: */ "rfnm_source",
|
||||
@ -62,7 +61,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void refresh() {
|
||||
void refresh() {
|
||||
#ifndef __ANDROID__
|
||||
devices.clear();
|
||||
auto list = librfnm::find(librfnm_transport::LIBRFNM_TRANSPORT_USB);
|
||||
for (const auto& info : list) {
|
||||
@ -76,6 +76,16 @@ private:
|
||||
// Save device
|
||||
devices.define((char*)info.motherboard.serial_number, devName, (char*)info.motherboard.serial_number);
|
||||
}
|
||||
#else
|
||||
// Check for device presence
|
||||
int vid, pid;
|
||||
devFd = backend::getDeviceFD(vid, pid, backend::RFNM_VIDPIDS);
|
||||
if (devFd < 0) { return; }
|
||||
|
||||
// Get device info
|
||||
std::string fakeName = "RFNM USB";
|
||||
devices.define(fakeName, fakeName, fakeName);
|
||||
#endif
|
||||
}
|
||||
|
||||
void select(const std::string& serial) {
|
||||
@ -91,131 +101,8 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the device
|
||||
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
|
||||
|
||||
// Define samplerates
|
||||
samplerates.clear();
|
||||
samplerates.define(61440000, "61.44 MHz", 2);
|
||||
samplerates.define(122880000, "122.88 MHz", 1);
|
||||
|
||||
// Define daughterboards
|
||||
daughterboards.clear();
|
||||
for (int i = 0; i < 2; i++) {
|
||||
// If not present, skip
|
||||
if (!dev->s->hwinfo.daughterboard[i].board_id) { continue; }
|
||||
|
||||
// Format the daughterboard name
|
||||
std::string name = (i ? "[SEC] " : "[PRI] ") + std::string(dev->s->hwinfo.daughterboard[i].user_readable_name);
|
||||
|
||||
// Add the daughterboard to the list
|
||||
daughterboards.define(name, name, i);
|
||||
}
|
||||
|
||||
// Load options (TODO)
|
||||
srId = samplerates.keyId(61440000);
|
||||
dgbId = 0;
|
||||
|
||||
// Select the daughterboard
|
||||
selectDaughterboard(dev, 0);
|
||||
|
||||
// Update samplerate
|
||||
sampleRate = samplerates.key(srId);
|
||||
|
||||
// Close device
|
||||
delete dev;
|
||||
|
||||
// Save serial number
|
||||
selectedSerial = serial;
|
||||
}
|
||||
|
||||
struct PathConfig {
|
||||
rfnm_rf_path path;
|
||||
int chId;
|
||||
uint16_t appliesCh;
|
||||
|
||||
bool operator==(const PathConfig& b) const {
|
||||
return b.path == path;
|
||||
}
|
||||
};
|
||||
|
||||
void selectDaughterboard(librfnm* dev, int id) {
|
||||
// If no daugherboard is populated, give up
|
||||
if (!dev->s->hwinfo.daughterboard[0].board_id && !dev->s->hwinfo.daughterboard[1].board_id) {
|
||||
flog::error("The selected device has no daughterboards");
|
||||
return;
|
||||
}
|
||||
|
||||
// If the ID is not populated, select the other one
|
||||
if (id >= 2 || !dev->s->hwinfo.daughterboard[id].board_id) {
|
||||
selectDaughterboard(dev, 1 - id);
|
||||
}
|
||||
|
||||
// Compute the channel offset
|
||||
int offset = 0;
|
||||
for (int i = 0; i < id; i++) {
|
||||
offset += dev->s->hwinfo.daughterboard[i].rx_ch_cnt;
|
||||
}
|
||||
|
||||
// Define antenna paths by going through all channels
|
||||
paths.clear();
|
||||
int count = dev->s->hwinfo.daughterboard[id].rx_ch_cnt;
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Go through each possible path
|
||||
for (int j = 0; j < 10; j++) {
|
||||
// If it's the null path, stop searching
|
||||
rfnm_rf_path path = dev->s->rx.ch[offset + i].path_possible[j];
|
||||
if (path == RFNM_PATH_NULL) { continue; }
|
||||
|
||||
// Get the path
|
||||
PathConfig pc = { path, offset + i, (uint16_t)(1 << (offset + i + 8))};
|
||||
|
||||
// If it's not in the list, add it
|
||||
if (!paths.valueExists(pc)) {
|
||||
std::string name = librfnm::rf_path_to_string(pc.path);
|
||||
std::string capName = name;
|
||||
if (std::islower(capName[0])) { capName[0] = std::toupper(capName[0]); }
|
||||
paths.define(name, capName, pc);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the preferred path
|
||||
PathConfig preferred_pc = { dev->s->rx.ch[offset + i].path_preferred, 0, 0 };
|
||||
|
||||
// Make sure the path is accessible or give up
|
||||
if (!paths.valueExists(preferred_pc)) { continue; }
|
||||
|
||||
// Set this channel as the channel of its prefered path (cursed af but lazy)
|
||||
const PathConfig& pc = paths.value(paths.valueId(preferred_pc));
|
||||
((PathConfig*)&pc)->chId = offset + i;
|
||||
((PathConfig*)&pc)->appliesCh = (uint16_t)(1 << (offset + i + 8));
|
||||
}
|
||||
|
||||
// Dump antenna paths
|
||||
for (int i = 0; i < paths.size(); i++) {
|
||||
flog::debug("PATH[{}]: Name={}, Ch={}, Path={}", i, paths.name(i), paths.value(i).chId, (int)paths.value(i).path);
|
||||
}
|
||||
|
||||
// Load configuration (TODO)
|
||||
selectedPath = paths.key(0);
|
||||
|
||||
// Select antenna path
|
||||
selectPath(dev, id, selectedPath);
|
||||
|
||||
// Save selected daughterboard
|
||||
dgbId = id;
|
||||
}
|
||||
|
||||
void selectPath(librfnm* dev, int dgbId, const std::string& path) {
|
||||
// If the path doesn't exist, select the first path
|
||||
if (!paths.keyExists(path)) {
|
||||
selectPath(dev, dgbId, paths.key(0));
|
||||
}
|
||||
|
||||
// Save selected path
|
||||
selectedPath = path;
|
||||
pathId = paths.keyId(path);
|
||||
currentPath = paths.value(pathId);
|
||||
// // Open the device
|
||||
// librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, serial);
|
||||
|
||||
// Define bandwidths
|
||||
bandwidths.clear();
|
||||
@ -227,8 +114,27 @@ private:
|
||||
}
|
||||
|
||||
// Get gain range
|
||||
gainMin = dev->s->rx.ch[currentPath.chId].gain_range.min;
|
||||
gainMax = dev->s->rx.ch[currentPath.chId].gain_range.max;
|
||||
gainMin = -30;//dev->librfnm_s->rx.ch[0].gain_range.min;
|
||||
gainMax = 60;//dev->librfnm_s->rx.ch[0].gain_range.max;
|
||||
|
||||
// // Close device
|
||||
// delete dev;
|
||||
|
||||
// Define samplerates
|
||||
samplerates.clear();
|
||||
samplerates.define(61440000, "61.44 MHz", 2);
|
||||
samplerates.define(122880000, "122.88 MHz", 1);
|
||||
|
||||
// TODO: Load options
|
||||
srId = samplerates.keyId(61440000);
|
||||
bwId = bandwidths.nameId("Auto");
|
||||
gain = 0;
|
||||
|
||||
// Update samplerate
|
||||
sampleRate = samplerates.key(srId);
|
||||
|
||||
// Save serial number
|
||||
selectedSerial = serial;
|
||||
}
|
||||
|
||||
static void menuSelected(void* ctx) {
|
||||
@ -247,7 +153,24 @@ private:
|
||||
if (_this->running) { return; }
|
||||
|
||||
// Open the device
|
||||
#ifndef __ANDROID__
|
||||
_this->openDev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
|
||||
#else
|
||||
_this->openDev = new librfnm(_this->devFd);
|
||||
#endif
|
||||
|
||||
// Configure the device
|
||||
_this->openDev->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
|
||||
_this->openDev->librfnm_s->rx.ch[0].samp_freq_div_n = _this->samplerates[_this->srId];
|
||||
_this->openDev->librfnm_s->rx.ch[0].freq = _this->freq;
|
||||
_this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
|
||||
_this->openDev->librfnm_s->rx.ch[0].rfic_lpf_bw = 100;
|
||||
_this->openDev->librfnm_s->rx.ch[0].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
||||
_this->openDev->librfnm_s->rx.ch[0].path = _this->openDev->librfnm_s->rx.ch[0].path_preferred;
|
||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
||||
if (fail != rfnm_api_failcode::RFNM_API_OK) {
|
||||
flog::error("Failed to configure device: {}", (int)fail);
|
||||
}
|
||||
|
||||
// Configure the stream
|
||||
_this->bufferSize = -1;
|
||||
@ -263,24 +186,7 @@ private:
|
||||
_this->openDev->rx_qbuf(&_this->rxBuf[i]);
|
||||
}
|
||||
|
||||
// Flush buffers
|
||||
_this->openDev->rx_flush();
|
||||
|
||||
// Configure the device
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_ON;
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].samp_freq_div_n = _this->samplerates[_this->srId];
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].freq = _this->freq;
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain;
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].rfic_lpf_bw = 100;
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].path = _this->currentPath.path;
|
||||
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||
if (fail != rfnm_api_failcode::RFNM_API_OK) {
|
||||
flog::error("Failed to configure device: {}", (int)fail);
|
||||
}
|
||||
|
||||
// Start worker
|
||||
_this->run = true;
|
||||
_this->workerThread = std::thread(&RFNMSourceModule::worker, _this);
|
||||
|
||||
_this->running = true;
|
||||
@ -293,20 +199,13 @@ private:
|
||||
_this->running = false;
|
||||
|
||||
// Stop worker
|
||||
_this->run = false;
|
||||
_this->stream.stopWriter();
|
||||
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
||||
_this->stream.clearWriteStop();
|
||||
|
||||
// Stop the RX streaming
|
||||
_this->openDev->rx_stream_stop();
|
||||
|
||||
// Disable channel
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].enable = RFNM_CH_OFF;
|
||||
_this->openDev->set(_this->currentPath.appliesCh);
|
||||
|
||||
// Flush buffers
|
||||
_this->openDev->rx_flush();
|
||||
_this->openDev->librfnm_s->rx.ch[0].enable = RFNM_CH_ON;
|
||||
_this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
||||
|
||||
// Close device
|
||||
delete _this->openDev;
|
||||
@ -322,8 +221,8 @@ private:
|
||||
static void tune(double freq, void* ctx) {
|
||||
RFNMSourceModule* _this = (RFNMSourceModule*)ctx;
|
||||
if (_this->running) {
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].freq = freq;
|
||||
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||
_this->openDev->librfnm_s->rx.ch[0].freq = freq;
|
||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
||||
if (fail != rfnm_api_failcode::RFNM_API_OK) {
|
||||
flog::error("Failed to tune: {}", (int)fail);
|
||||
}
|
||||
@ -360,48 +259,11 @@ private:
|
||||
core::setInputSampleRate(_this->sampleRate);
|
||||
}
|
||||
|
||||
if (_this->daughterboards.size() > 1) {
|
||||
SmGui::LeftLabel("Daughterboard");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_rfnm_dgb_sel_", _this->name), &_this->dgbId, _this->daughterboards.txt)) {
|
||||
// Open the device
|
||||
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
|
||||
|
||||
// Select the daughterboard
|
||||
_this->selectDaughterboard(dev, _this->dgbId);
|
||||
|
||||
// Close device
|
||||
delete dev;
|
||||
|
||||
// TODO: Save
|
||||
}
|
||||
}
|
||||
|
||||
if (_this->paths.size() > 1) {
|
||||
SmGui::LeftLabel("Antenna Path");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_rfnm_path_sel_", _this->name), &_this->pathId, _this->paths.txt)) {
|
||||
// Open the device
|
||||
librfnm* dev = new librfnm(librfnm_transport::LIBRFNM_TRANSPORT_USB, _this->selectedSerial);
|
||||
|
||||
// Select the atennna path
|
||||
_this->selectPath(dev, _this->dgbId, _this->paths.key(_this->pathId));
|
||||
|
||||
// Close device
|
||||
delete dev;
|
||||
|
||||
// TODO: Save
|
||||
}
|
||||
}
|
||||
|
||||
if (_this->running) { SmGui::EndDisabled(); }
|
||||
|
||||
SmGui::LeftLabel("Bandwidth");
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::Combo(CONCAT("##_rfnm_bw_sel_", _this->name), &_this->bwId, _this->bandwidths.txt)) {
|
||||
if (_this->running) {
|
||||
// TODO: Set
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
@ -409,16 +271,16 @@ private:
|
||||
SmGui::FillWidth();
|
||||
if (SmGui::SliderInt(CONCAT("##_rfnm_gain_", _this->name), &_this->gain, _this->gainMin, _this->gainMax)) {
|
||||
if (_this->running) {
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].gain = _this->gain;
|
||||
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||
_this->openDev->librfnm_s->rx.ch[0].gain = _this->gain;
|
||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
|
||||
if (SmGui::Checkbox(CONCAT("FM Notch##_rfnm_", _this->name), &_this->fmNotch)) {
|
||||
if (_this->running) {
|
||||
_this->openDev->s->rx.ch[_this->currentPath.chId].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
||||
rfnm_api_failcode fail = _this->openDev->set(_this->currentPath.appliesCh);
|
||||
_this->openDev->librfnm_s->rx.ch[0].fm_notch = _this->fmNotch ? rfnm_fm_notch::RFNM_FM_NOTCH_ON : rfnm_fm_notch::RFNM_FM_NOTCH_OFF;
|
||||
rfnm_api_failcode fail = _this->openDev->set(LIBRFNM_APPLY_CH0_RX);
|
||||
}
|
||||
// TODO: Save
|
||||
}
|
||||
@ -427,18 +289,12 @@ private:
|
||||
void worker() {
|
||||
librfnm_rx_buf* lrxbuf;
|
||||
int sampCount = bufferSize/4;
|
||||
uint8_t ch = (1 << currentPath.chId);
|
||||
|
||||
// Define number of buffers per swap to maintain 200 fps
|
||||
int maxBufCount = STREAM_BUFFER_SIZE / sampCount;
|
||||
int bufCount = (sampleRate / sampCount) / 200;
|
||||
if (bufCount <= 0) { bufCount = 1; }
|
||||
if (bufCount > maxBufCount) { bufCount = maxBufCount; }
|
||||
// TODO: Define number of buffers per swap to maintain 200 fps
|
||||
|
||||
int count = 0;
|
||||
while (run) {
|
||||
while (true) {
|
||||
// Receive a buffer
|
||||
auto fail = openDev->rx_dqbuf(&lrxbuf, ch, 1000);
|
||||
auto fail = openDev->rx_dqbuf(&lrxbuf, LIBRFNM_CH0, 1000);
|
||||
if (fail == rfnm_api_failcode::RFNM_API_DQBUF_NO_DATA) {
|
||||
flog::error("Dequeue buffer didn't have any data");
|
||||
continue;
|
||||
@ -446,17 +302,13 @@ private:
|
||||
else if (fail) { break; }
|
||||
|
||||
// Convert buffer to CF32
|
||||
volk_16i_s32f_convert_32f((float*)&stream.writeBuf[(count++)*sampCount], (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
|
||||
volk_16i_s32f_convert_32f((float*)stream.writeBuf, (int16_t*)lrxbuf->buf, 32768.0f, sampCount * 2);
|
||||
|
||||
// Reque buffer
|
||||
openDev->rx_qbuf(lrxbuf);
|
||||
|
||||
// Swap data
|
||||
if (count >= bufCount) {
|
||||
if (!stream.swap(count*sampCount)) { break; }
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if (!stream.swap(sampCount)) { break; }
|
||||
}
|
||||
|
||||
flog::debug("Worker exiting");
|
||||
@ -471,15 +323,12 @@ private:
|
||||
double freq;
|
||||
|
||||
OptionList<std::string, std::string> devices;
|
||||
OptionList<std::string, int> daughterboards;
|
||||
OptionList<std::string, PathConfig> paths;
|
||||
OptionList<int, int> bandwidths;
|
||||
OptionList<int, int> samplerates;
|
||||
int gainMin = 0;
|
||||
int gainMax = 0;
|
||||
|
||||
int devId = 0;
|
||||
int dgbId = 0;
|
||||
int pathId = 0;
|
||||
int srId = 0;
|
||||
int bwId = 0;
|
||||
int gain = 0;
|
||||
@ -487,11 +336,12 @@ private:
|
||||
std::string selectedSerial;
|
||||
librfnm* openDev;
|
||||
int bufferSize = -1;
|
||||
std::string selectedPath;
|
||||
PathConfig currentPath;
|
||||
librfnm_rx_buf rxBuf[LIBRFNM_MIN_RX_BUFCNT];
|
||||
|
||||
std::atomic<bool> run = false;
|
||||
#ifdef __ANDROID__
|
||||
int devFd = 0;
|
||||
#endif
|
||||
|
||||
std::thread workerThread;
|
||||
};
|
||||
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
|
||||
#define RFSPACE_MAX_SIZE 8192
|
||||
#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000
|
||||
|
@ -121,23 +121,14 @@ public:
|
||||
#ifndef __ANDROID__
|
||||
devCount = rtlsdr_get_device_count();
|
||||
char buf[1024];
|
||||
char venBuf[256];
|
||||
char prodBuf[256];
|
||||
char snBuf[256];
|
||||
char snBuf[1024];
|
||||
for (int i = 0; i < devCount; i++) {
|
||||
// Gather device info
|
||||
const char* devName = rtlsdr_get_device_name(i);
|
||||
int snErr = rtlsdr_get_device_usb_strings(i, venBuf, prodBuf, snBuf);
|
||||
int snErr = rtlsdr_get_device_usb_strings(i, NULL, NULL, snBuf);
|
||||
|
||||
// Build name
|
||||
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
|
||||
sprintf(buf, "[%s] %s##%d", (!snErr && snBuf[0]) ? snBuf : "No Serial", devName, i);
|
||||
devNames.push_back(buf);
|
||||
devListTxt += buf;
|
||||
devListTxt += '\0';
|
||||
@ -208,6 +199,8 @@ public:
|
||||
config.conf["devices"][selectedDevName]["tunerAgc"] = tunerAgc;
|
||||
config.conf["devices"][selectedDevName]["gain"] = gainId;
|
||||
}
|
||||
if (gainId >= gainList.size()) { gainId = gainList.size() - 1; }
|
||||
updateGainTxt();
|
||||
|
||||
// Load config
|
||||
if (config.conf["devices"][selectedDevName].contains("sampleRate")) {
|
||||
@ -247,11 +240,9 @@ public:
|
||||
|
||||
if (config.conf["devices"][selectedDevName].contains("gain")) {
|
||||
gainId = config.conf["devices"][selectedDevName]["gain"];
|
||||
updateGainTxt();
|
||||
}
|
||||
|
||||
if (gainId >= gainList.size()) { gainId = gainList.size() - 1; }
|
||||
updateGainTxt();
|
||||
|
||||
config.release(created);
|
||||
|
||||
rtlsdr_close(openDev);
|
||||
@ -604,4 +595,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
|
||||
MOD_EXPORT void _END_() {
|
||||
config.disableAutoSave();
|
||||
config.save();
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
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)
|
@ -1,2 +0,0 @@
|
||||
build/
|
||||
.vscode/
|
@ -1,87 +0,0 @@
|
||||
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)
|
@ -1,32 +0,0 @@
|
||||
# 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)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user