3 Commits

Author SHA1 Message Date
AlexandreRouma
f8078ac3f0 more work on DAB decoding 2024-09-23 00:51:06 +02:00
AlexandreRouma
064f25ee73 work on the time sync algo 2024-09-19 17:28:03 +02:00
AlexandreRouma
d87ae23560 beginning of cleaner OFDM demod code 2024-09-19 05:47:58 +02:00
116 changed files with 3333 additions and 9760 deletions

View File

@@ -50,7 +50,7 @@ jobs:
run: 7z x ${{runner.workspace}}/SDRplay.zip -o"C:/Program Files/" run: 7z x ${{runner.workspace}}/SDRplay.zip -o"C:/Program Files/"
- name: Download codec2 - name: Download codec2
run: git clone https://github.com/drowe67/codec2 run: git clone https://github.com/AlexandreRouma/codec2
- name: Prepare MinGW - name: Prepare MinGW
run: C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -c "pacman --noconfirm -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja" run: C:/msys64/msys2_shell.cmd -defterm -here -no-start -mingw64 -c "pacman --noconfirm -S --needed base-devel mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja"
@@ -79,12 +79,9 @@ jobs:
- name: Install libfobos - name: Install libfobos
run: git clone https://github.com/AlexandreRouma/libfobos ; cd libfobos ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install . run: git clone https://github.com/AlexandreRouma/libfobos ; cd libfobos ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; cmake --install .
- name: Install libhydrasdr
run: git clone https://github.com/hydrasdr/rfone_host; cd rfone_host/ ; mkdir build ; cd build ; cmake .. -DCMAKE_BUILD_TYPE=Release "-DLIBUSB_LIBRARIES=C:\Program Files\PothosSDR\lib\libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIR=C:\Program Files\PothosSDR\include\libusb-1.0" "-DTHREADS_PTHREADS_WIN32_LIBRARY=C:\Program Files\PothosSDR\lib\pthreadVC2.lib" "-DTHREADS_PTHREADS_INCLUDE_DIR=C:\Program Files\PothosSDR\include" ; cmake --build . --config Release ; cmake --install .
- name: Prepare CMake - name: Prepare CMake
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON run: cmake -DCOPY_MSVC_REDISTRIBUTABLES=ON "$Env:GITHUB_WORKSPACE" "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
- name: Build - name: Build
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
@@ -101,7 +98,7 @@ jobs:
path: ${{runner.workspace}}/sdrpp_windows_x64.zip path: ${{runner.workspace}}/sdrpp_windows_x64.zip
build_macos_intel: build_macos_intel:
runs-on: macos-15-intel runs-on: macos-12
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -110,7 +107,7 @@ jobs:
run: cmake -E make_directory ${{runner.workspace}}/build run: cmake -E make_directory ${{runner.workspace}}/build
- name: Install dependencies - name: Install dependencies
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool 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 spdlog && pip3 install mako
- name: Install volk - name: Install volk
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
@@ -122,7 +119,7 @@ jobs:
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install libad9361 - name: Install libad9361
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5 .. && make -j3 && sudo make install && cd ../../ run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install LimeSuite - name: Install LimeSuite
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
@@ -139,12 +136,9 @@ jobs:
- name: Install more recent librtlsdr - name: Install more recent librtlsdr
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../ run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
- name: Install libhydrasdr
run: git clone https://github.com/hydrasdr/rfone_host && cd rfone_host && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Prepare CMake - name: Prepare CMake
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
run: cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build - name: Build
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
@@ -161,7 +155,7 @@ jobs:
path: ${{runner.workspace}}/sdrpp_macos_intel.zip path: ${{runner.workspace}}/sdrpp_macos_intel.zip
build_macos_arm: build_macos_arm:
runs-on: macos-15 runs-on: macos-14
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -182,7 +176,7 @@ jobs:
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install libad9361 - name: Install libad9361
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release -DCMAKE_POLICY_VERSION_MINIMUM=3.5 .. && make -j3 && sudo make install && cd ../../ run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install LimeSuite - name: Install LimeSuite
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../ run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
@@ -199,12 +193,9 @@ jobs:
- name: Install more recent librtlsdr - name: Install more recent librtlsdr
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../ run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
- name: Install libhydrasdr
run: git clone https://github.com/hydrasdr/rfone_host && cd rfone_host && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Prepare CMake - name: Prepare CMake
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
run: cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=OFF -DOPT_BUILD_PERSEUS_SOURCE=OFF -DOPT_BUILD_AUDIO_SOURCE=OFF -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build - name: Build
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
@@ -220,7 +211,29 @@ jobs:
name: sdrpp_macos_arm name: sdrpp_macos_arm
path: ${{runner.workspace}}/sdrpp_macos_arm.zip path: ${{runner.workspace}}/sdrpp_macos_arm.zip
build_debian_bullseye_amd64: build_debian_buster:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- name: Recover Deb Archive
working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_buster_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bullseye:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -242,29 +255,7 @@ jobs:
name: sdrpp_debian_bullseye_amd64 name: sdrpp_debian_bullseye_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bullseye_aarch64: build_debian_bookworm:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bullseye && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- name: Recover Deb Archive
working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_bullseye_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bookworm_amd64:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -286,73 +277,7 @@ jobs:
name: sdrpp_debian_bookworm_amd64 name: sdrpp_debian_bookworm_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_bookworm_aarch64: build_debian_sid:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bookworm && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- name: Recover Deb Archive
working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_bookworm_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_trixie_amd64:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_trixie && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- name: Recover Deb Archive
working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_trixie_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_trixie_aarch64:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_trixie && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- name: Recover Deb Archive
working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_trixie_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_sid_amd64:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -374,29 +299,7 @@ jobs:
name: sdrpp_debian_sid_amd64 name: sdrpp_debian_sid_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_debian_sid_aarch64: build_ubuntu_focal:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_sid && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- name: Recover Deb Archive
working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_sid_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_focal_amd64:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -418,29 +321,7 @@ jobs:
name: sdrpp_ubuntu_focal_amd64 name: sdrpp_ubuntu_focal_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_focal_aarch64: build_ubuntu_jammy:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_focal && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- name: Recover Deb Archive
working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v4
with:
name: sdrpp_ubuntu_focal_aarch64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_jammy_amd64:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -461,15 +342,15 @@ jobs:
with: with:
name: sdrpp_ubuntu_jammy_amd64 name: sdrpp_ubuntu_jammy_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_jammy_aarch64: build_ubuntu_mantic:
runs-on: ubuntu-24.04-arm runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Create Docker Image - name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_jammy && docker build . --tag sdrpp_build run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_mantic && docker build . --tag sdrpp_build
- name: Run Container - name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
@@ -481,10 +362,10 @@ jobs:
- name: Save Deb Archive - name: Save Deb Archive
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: sdrpp_ubuntu_jammy_aarch64 name: sdrpp_ubuntu_mantic_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_noble_amd64: build_ubuntu_noble:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -506,27 +387,32 @@ jobs:
name: sdrpp_ubuntu_noble_amd64 name: sdrpp_ubuntu_noble_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_noble_aarch64: build_raspios_bullseye_armhf:
runs-on: ubuntu-24.04-arm runs-on: ARM
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Create Build Environment
run: rm -rf ${{runner.workspace}}/build && cmake -E make_directory ${{runner.workspace}}/build
- name: Create Docker Image - name: Prepare CMake
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_noble && docker build . --tag sdrpp_build working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_USRP_SOURCE=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
- name: Run Container - name: Build
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh working-directory: ${{runner.workspace}}/build
run: make VERBOSE=1 -j3
- name: Recover Deb Archive - name: Create Dev Archive
working-directory: ${{runner.workspace}} working-directory: ${{runner.workspace}}
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./ run: sh $GITHUB_WORKSPACE/make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk2-dev, librtaudio-dev' && mv sdrpp_debian_amd64.deb sdrpp_debian_armhf.deb
- name: Save Deb Archive - name: Save Deb Archive
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: sdrpp_ubuntu_noble_aarch64 name: sdrpp_raspios_bullseye_armhf
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb path: ${{runner.workspace}}/sdrpp_debian_armhf.deb
build_android: build_android:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -556,26 +442,7 @@ jobs:
path: ${{runner.workspace}}/sdrpp.apk path: ${{runner.workspace}}/sdrpp.apk
create_full_archive: create_full_archive:
needs: [ 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']
'build_windows',
'build_macos_intel',
'build_macos_arm',
'build_debian_bullseye_amd64',
'build_debian_bullseye_aarch64',
'build_debian_bookworm_amd64',
'build_debian_bookworm_aarch64',
'build_debian_trixie_amd64',
'build_debian_trixie_aarch64',
'build_debian_sid_amd64',
'build_debian_sid_aarch64',
'build_ubuntu_focal_amd64',
'build_ubuntu_focal_aarch64',
'build_ubuntu_jammy_amd64',
'build_ubuntu_jammy_aarch64',
'build_ubuntu_noble_amd64',
'build_ubuntu_noble_aarch64',
'build_android'
]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -588,20 +455,15 @@ jobs:
mv sdrpp_windows_x64/sdrpp_windows_x64.zip sdrpp_all/ && mv sdrpp_windows_x64/sdrpp_windows_x64.zip sdrpp_all/ &&
mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ && mv sdrpp_macos_intel/sdrpp_macos_intel.zip sdrpp_all/ &&
mv sdrpp_macos_arm/sdrpp_macos_arm.zip sdrpp_all/ && mv sdrpp_macos_arm/sdrpp_macos_arm.zip sdrpp_all/ &&
mv sdrpp_debian_bullseye_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bullseye_amd64.deb && mv sdrpp_debian_buster_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_buster_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_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_amd64.deb &&
mv sdrpp_debian_bookworm_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_bookworm_aarch64.deb &&
mv sdrpp_debian_trixie_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_trixie_amd64.deb &&
mv sdrpp_debian_trixie_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_trixie_aarch64.deb &&
mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb && mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
mv sdrpp_debian_sid_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_aarch64.deb &&
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb && mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
mv sdrpp_ubuntu_focal_aarch64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_aarch64.deb &&
mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb && mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb &&
mv sdrpp_ubuntu_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_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_raspios_bullseye_armhf/sdrpp_debian_armhf.deb sdrpp_all/sdrpp_raspios_bullseye_armhf.deb &&
mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4

6
.gitignore vendored
View File

@@ -16,8 +16,4 @@ poggers_decoder
m17_decoder/libcorrect m17_decoder/libcorrect
SDR++.app SDR++.app
android/deps android/deps
android/app/assets android/app/assets
source_modules/badgesdr_source
decoder_modules/mystery_decoder
decoder_modules/tetra_decoder
misc_modules/modulation_monitor

View File

@@ -14,14 +14,11 @@ option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Dependencies:
option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)" ON) option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)" ON)
option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF) option(OPT_BUILD_BADGESDR_SOURCE "Build BadgeSDR Source Module (Dependencies: libusb)" OFF)
option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF) option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF)
option(OPT_BUILD_DRAGONLABS_SOURCE "Build Dragon Labs Source Module (Dependencies: libdlcr)" OFF)
option(OPT_BUILD_FILE_SOURCE "Wav file source" ON) option(OPT_BUILD_FILE_SOURCE "Wav file source" ON)
option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF) option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: libfobos)" OFF)
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON) option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF) option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON) option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
option(OPT_BUILD_HYDRASDR_SOURCE "Build HydraSDR Source Module (Dependencies: libhydrasdr)" OFF)
option(OPT_BUILD_KCSDR_SOURCE "Build KCSDR Source Module (Dependencies: libkcsdr)" OFF)
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF) option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON) option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF) option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
@@ -46,7 +43,7 @@ option(OPT_BUILD_NEW_PORTAUDIO_SINK "Build the new PortAudio Sink Module (Depend
option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF) option(OPT_BUILD_PORTAUDIO_SINK "Build PortAudio Sink Module (Dependencies: portaudio)" OFF)
# Decoders # Decoders
option(OPT_BUILD_ATV_DECODER "Build ATV decoder (no dependencies required)" ON) 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_DAB_DECODER "Build the DAB/DAB+ decoder (no dependencies required)" OFF)
option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF) option(OPT_BUILD_FALCON9_DECODER "Build the falcon9 live decoder (Dependencies: ffplay)" OFF)
option(OPT_BUILD_KG_SSTV_DECODER "Build the KG SSTV (KG-STV) decoder module (no dependencies required)" OFF) option(OPT_BUILD_KG_SSTV_DECODER "Build the KG SSTV (KG-STV) decoder module (no dependencies required)" OFF)
@@ -55,7 +52,6 @@ option(OPT_BUILD_METEOR_DEMODULATOR "Build the meteor demodulator module (no dep
option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON) option(OPT_BUILD_PAGER_DECODER "Build the pager decoder module (no dependencies required)" ON)
option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON) option(OPT_BUILD_RADIO "Main audio modulation decoder (AM, FM, SSB, etc...)" ON)
option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF) option(OPT_BUILD_RYFI_DECODER "RyFi data link decoder" OFF)
option(OPT_BUILD_VOR_RECEIVER "VOR beacon receiver" OFF)
option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF) option(OPT_BUILD_WEATHER_SAT_DECODER "Build the HRPT decoder module (no dependencies required)" OFF)
# Misc # Misc
@@ -144,10 +140,6 @@ if (OPT_BUILD_BLADERF_SOURCE)
add_subdirectory("source_modules/bladerf_source") add_subdirectory("source_modules/bladerf_source")
endif (OPT_BUILD_BLADERF_SOURCE) endif (OPT_BUILD_BLADERF_SOURCE)
if (OPT_BUILD_DRAGONLABS_SOURCE)
add_subdirectory("source_modules/dragonlabs_source")
endif (OPT_BUILD_DRAGONLABS_SOURCE)
if (OPT_BUILD_FILE_SOURCE) if (OPT_BUILD_FILE_SOURCE)
add_subdirectory("source_modules/file_source") add_subdirectory("source_modules/file_source")
endif (OPT_BUILD_FILE_SOURCE) endif (OPT_BUILD_FILE_SOURCE)
@@ -168,14 +160,6 @@ if (OPT_BUILD_HERMES_SOURCE)
add_subdirectory("source_modules/hermes_source") add_subdirectory("source_modules/hermes_source")
endif (OPT_BUILD_HERMES_SOURCE) endif (OPT_BUILD_HERMES_SOURCE)
if (OPT_BUILD_HYDRASDR_SOURCE)
add_subdirectory("source_modules/hydrasdr_source")
endif (OPT_BUILD_HYDRASDR_SOURCE)
if (OPT_BUILD_KCSDR_SOURCE)
add_subdirectory("source_modules/kcsdr_source")
endif (OPT_BUILD_KCSDR_SOURCE)
if (OPT_BUILD_LIMESDR_SOURCE) if (OPT_BUILD_LIMESDR_SOURCE)
add_subdirectory("source_modules/limesdr_source") add_subdirectory("source_modules/limesdr_source")
endif (OPT_BUILD_LIMESDR_SOURCE) endif (OPT_BUILD_LIMESDR_SOURCE)
@@ -208,10 +192,6 @@ if (OPT_BUILD_RTL_TCP_SOURCE)
add_subdirectory("source_modules/rtl_tcp_source") add_subdirectory("source_modules/rtl_tcp_source")
endif (OPT_BUILD_RTL_TCP_SOURCE) endif (OPT_BUILD_RTL_TCP_SOURCE)
if (OPT_BUILD_SDDC_SOURCE)
add_subdirectory("source_modules/sddc_source")
endif (OPT_BUILD_SDDC_SOURCE)
if (OPT_BUILD_SDRPP_SERVER_SOURCE) if (OPT_BUILD_SDRPP_SERVER_SOURCE)
add_subdirectory("source_modules/sdrpp_server_source") add_subdirectory("source_modules/sdrpp_server_source")
endif (OPT_BUILD_SDRPP_SERVER_SOURCE) endif (OPT_BUILD_SDRPP_SERVER_SOURCE)
@@ -300,10 +280,6 @@ if (OPT_BUILD_RYFI_DECODER)
add_subdirectory("decoder_modules/ryfi_decoder") add_subdirectory("decoder_modules/ryfi_decoder")
endif (OPT_BUILD_RYFI_DECODER) endif (OPT_BUILD_RYFI_DECODER)
if (OPT_BUILD_VOR_RECEIVER)
add_subdirectory("decoder_modules/vor_receiver")
endif (OPT_BUILD_VOR_RECEIVER)
if (OPT_BUILD_WEATHER_SAT_DECODER) if (OPT_BUILD_WEATHER_SAT_DECODER)
add_subdirectory("decoder_modules/weather_sat_decoder") add_subdirectory("decoder_modules/weather_sat_decoder")
endif (OPT_BUILD_WEATHER_SAT_DECODER) endif (OPT_BUILD_WEATHER_SAT_DECODER)
@@ -414,4 +390,4 @@ endif ()
configure_file(${CMAKE_SOURCE_DIR}/cmake_uninstall.cmake ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY) 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) add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
# Create headers target # Create headers target

View File

@@ -10,11 +10,11 @@ android {
minSdkVersion 28 minSdkVersion 28
targetSdkVersion 28 targetSdkVersion 28
versionCode 1 versionCode 1
versionName "1.2.1" versionName "1.2.0"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments "-DOPT_BACKEND_GLFW=OFF", "-DOPT_BACKEND_ANDROID=ON", "-DOPT_BUILD_SOAPY_SOURCE=OFF", "-DOPT_BUILD_ANDROID_AUDIO_SINK=ON", "-DOPT_BUILD_AUDIO_SINK=OFF", "-DOPT_BUILD_DISCORD_PRESENCE=OFF", "-DOPT_BUILD_M17_DECODER=ON", "-DOPT_BUILD_PLUTOSDR_SOURCE=ON", "-DOPT_BUILD_AUDIO_SOURCE=OFF", "-DOPT_BUILD_HYDRASDR_SOURCE=ON" arguments "-DOPT_BACKEND_GLFW=OFF", "-DOPT_BACKEND_ANDROID=ON", "-DOPT_BUILD_SOAPY_SOURCE=OFF", "-DOPT_BUILD_ANDROID_AUDIO_SINK=ON", "-DOPT_BUILD_AUDIO_SINK=OFF", "-DOPT_BUILD_DISCORD_PRESENCE=OFF", "-DOPT_BUILD_M17_DECODER=ON", "-DOPT_BUILD_PLUTOSDR_SOURCE=ON", "-DOPT_BUILD_AUDIO_SOURCE=OFF"
} }
} }
} }

View File

@@ -1,9 +1,3 @@
# AI USE IS FORBIDDEN
Use of AI for the creation of code, pull requests or issues is forbidden.
Submitting code, pull requests or issues generated by AI will result in a permanent ban from all of my (Alexandre Rouma) repositories.
# Pull Requests # Pull Requests
Code pull requests are **NOT welcome**. Please open an issue discussing potential bugfixes or feature requests instead. Code pull requests are **NOT welcome**. Please open an issue discussing potential bugfixes or feature requests instead.
@@ -70,4 +64,4 @@ Please follow this guide to properly format the JSON files for custom color maps
The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSON files is important for reference and readability. The following guides will show you how to properly format the JSON files for their respective uses. The ability to add new radio band allocation identifiers and color maps relies on JSON files. Proper formatting of these JSON files is important for reference and readability. The following guides will show you how to properly format the JSON files for their respective uses.
**IMPORTANT: JSON File cannot contain comments, there are only in this example for clarity** **IMPORTANT: JSON File cannot contain comments, there are only in this example for clarity**

View File

@@ -11,7 +11,6 @@ namespace backend {
extern const std::vector<DevVIDPID> AIRSPY_VIDPIDS; extern const std::vector<DevVIDPID> AIRSPY_VIDPIDS;
extern const std::vector<DevVIDPID> AIRSPYHF_VIDPIDS; extern const std::vector<DevVIDPID> AIRSPYHF_VIDPIDS;
extern const std::vector<DevVIDPID> HACKRF_VIDPIDS; extern const std::vector<DevVIDPID> HACKRF_VIDPIDS;
extern const std::vector<DevVIDPID> HYDRASDR_VIDPIDS;
extern const std::vector<DevVIDPID> RTL_SDR_VIDPIDS; extern const std::vector<DevVIDPID> RTL_SDR_VIDPIDS;
int getDeviceFD(int& vid, int& pid, const std::vector<DevVIDPID>& allowedVidPids); int getDeviceFD(int& vid, int& pid, const std::vector<DevVIDPID>& allowedVidPids);

View File

@@ -408,11 +408,6 @@ namespace backend {
{ 0x1d50, 0xcc15 } { 0x1d50, 0xcc15 }
}; };
const std::vector<DevVIDPID> HYDRASDR_VIDPIDS = {
{ 0x1d50, 0x60a1 },
{ 0x38af, 0x0001 }
};
const std::vector<DevVIDPID> RTL_SDR_VIDPIDS = { const std::vector<DevVIDPID> RTL_SDR_VIDPIDS = {
{ 0x0bda, 0x2832 }, { 0x0bda, 0x2832 },
{ 0x0bda, 0x2838 }, { 0x0bda, 0x2838 },

View File

@@ -99,10 +99,7 @@ namespace backend {
glfwWindowHint(GLFW_CLIENT_API, OPENGL_VERSIONS_IS_ES[i] ? GLFW_OPENGL_ES_API : GLFW_OPENGL_API); glfwWindowHint(GLFW_CLIENT_API, OPENGL_VERSIONS_IS_ES[i] ? GLFW_OPENGL_ES_API : GLFW_OPENGL_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_VERSIONS_MAJOR[i]); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, OPENGL_VERSIONS_MAJOR[i]);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_VERSIONS_MINOR[i]); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, OPENGL_VERSIONS_MINOR[i]);
#if GLFW_VERSION_MAJOR > 3 || (GLFW_VERSION_MAJOR == 3 && GLFW_VERSION_MINOR >= 4)
glfwWindowHintString(GLFW_WAYLAND_APP_ID, "sdrpp");
#endif
// Create window with graphics context // Create window with graphics context
monitor = glfwGetPrimaryMonitor(); monitor = glfwGetPrimaryMonitor();
window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL); window = glfwCreateWindow(winWidth, winHeight, "SDR++ v" VERSION_STR " (Built at " __TIME__ ", " __DATE__ ")", NULL, NULL);

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 2.8)
project(Correct C) project(Correct C)
include(CheckLibraryExists) include(CheckLibraryExists)
include(CheckIncludeFiles) include(CheckIncludeFiles)

View File

@@ -147,11 +147,11 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["menuElements"][3]["name"] = "Sinks"; defConfig["menuElements"][3]["name"] = "Sinks";
defConfig["menuElements"][3]["open"] = true; defConfig["menuElements"][3]["open"] = true;
defConfig["menuElements"][4]["name"] = "Frequency Manager"; defConfig["menuElements"][3]["name"] = "Frequency Manager";
defConfig["menuElements"][4]["open"] = true; defConfig["menuElements"][3]["open"] = true;
defConfig["menuElements"][5]["name"] = "VFO Color"; defConfig["menuElements"][4]["name"] = "VFO Color";
defConfig["menuElements"][5]["open"] = true; defConfig["menuElements"][4]["open"] = true;
defConfig["menuElements"][6]["name"] = "Band Plan"; defConfig["menuElements"][6]["name"] = "Band Plan";
defConfig["menuElements"][6]["open"] = true; defConfig["menuElements"][6]["open"] = true;
@@ -181,12 +181,8 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true; defConfig["moduleInstances"]["Harogic Source"]["enabled"] = true;
defConfig["moduleInstances"]["Hermes Source"]["module"] = "hermes_source"; defConfig["moduleInstances"]["Hermes Source"]["module"] = "hermes_source";
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true; defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
defConfig["moduleInstances"]["HydraSDR Source"]["module"] = "hydrasdr_source";
defConfig["moduleInstances"]["HydraSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source"; defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true; defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["Network Source"]["module"] = "network_source";
defConfig["moduleInstances"]["Network Source"]["enabled"] = true;
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source"; defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true; defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source"; defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
@@ -234,19 +230,12 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["modules"] = json::array(); defConfig["modules"] = json::array();
defConfig["offsets"]["SpyVerter"] = 120000000.0; defConfig["offsetMode"] = (int)0; // Off
defConfig["offsets"]["Ham-It-Up"] = 125000000.0; defConfig["offset"] = 0.0;
defConfig["offsets"]["MMDS S-band (1998MHz)"] = -1998000000.0;
defConfig["offsets"]["DK5AV X-Band"] = -6800000000.0;
defConfig["offsets"]["Ku LNB (9750MHz)"] = -9750000000.0;
defConfig["offsets"]["Ku LNB (10700MHz)"] = -10700000000.0;
defConfig["selectedOffset"] = "None";
defConfig["manualOffset"] = 0.0;
defConfig["showMenu"] = true; defConfig["showMenu"] = true;
defConfig["showWaterfall"] = true; defConfig["showWaterfall"] = true;
defConfig["source"] = ""; defConfig["source"] = "";
defConfig["decimation"] = 1; defConfig["decimationPower"] = 0;
defConfig["iqCorrection"] = false; defConfig["iqCorrection"] = false;
defConfig["invertIQ"] = false; defConfig["invertIQ"] = false;
@@ -297,7 +286,6 @@ int sdrpp_main(int argc, char* argv[]) {
core::configManager.conf["modules"][modCount++] = "airspyhf_source.so"; core::configManager.conf["modules"][modCount++] = "airspyhf_source.so";
core::configManager.conf["modules"][modCount++] = "hackrf_source.so"; core::configManager.conf["modules"][modCount++] = "hackrf_source.so";
core::configManager.conf["modules"][modCount++] = "hermes_source.so"; core::configManager.conf["modules"][modCount++] = "hermes_source.so";
core::configManager.conf["modules"][modCount++] = "hydrasdr_source.so";
core::configManager.conf["modules"][modCount++] = "plutosdr_source.so"; core::configManager.conf["modules"][modCount++] = "plutosdr_source.so";
core::configManager.conf["modules"][modCount++] = "rfspace_source.so"; core::configManager.conf["modules"][modCount++] = "rfspace_source.so";
core::configManager.conf["modules"][modCount++] = "rtl_sdr_source.so"; core::configManager.conf["modules"][modCount++] = "rtl_sdr_source.so";
@@ -328,18 +316,12 @@ int sdrpp_main(int argc, char* argv[]) {
// Remove unused elements // Remove unused elements
auto items = core::configManager.conf.items(); auto items = core::configManager.conf.items();
auto newConf = core::configManager.conf;
bool configCorrected = false;
for (auto const& item : items) { for (auto const& item : items) {
if (!defConfig.contains(item.key())) { if (!defConfig.contains(item.key())) {
flog::info("Unused key in config {0}, repairing", item.key()); flog::info("Unused key in config {0}, repairing", item.key());
newConf.erase(item.key()); core::configManager.conf.erase(item.key());
configCorrected = true;
} }
} }
if (configCorrected) {
core::configManager.conf = newConf;
}
// Update to new module representation in config if needed // Update to new module representation in config if needed
for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) { for (auto [_name, inst] : core::configManager.conf["moduleInstances"].items()) {

View File

@@ -70,18 +70,15 @@ namespace sdrpp_credits {
"Flinger Films", "Flinger Films",
"Frank Werner (HB9FXQ)", "Frank Werner (HB9FXQ)",
"gringogrigio", "gringogrigio",
"Jandro",
"Jeff Moe", "Jeff Moe",
"Joe Cupano", "Joe Cupano",
"KD1SQ", "KD1SQ",
"Kezza", "Kezza",
"Krys Kamieniecki", "Krys Kamieniecki",
"Lee Donaghy", "Lee Donaghy",
"Lee (KD1SQ)", "Lee KD1SQ",
".lozenge. (Hank Hill)", ".lozenge. (Hank Hill)",
"Martin Herren (HB9FXX)", "Martin Herren (HB9FXX)",
"NeoVilsonWong",
"Nitin (VU2JEK)",
"ON4MU", "ON4MU",
"Passion-Radio.com", "Passion-Radio.com",
"Paul Maine", "Paul Maine",

View File

@@ -163,10 +163,10 @@ namespace dsp {
private: private:
Processor<T, T>* blockBefore(Processor<T, T>* block) { Processor<T, T>* blockBefore(Processor<T, T>* block) {
Processor<T, T>* prev = NULL; // TODO: This is wrong and must be fixed when I get more time
for (auto& ln : links) { for (auto& ln : links) {
if (ln == block) { return prev; } if (ln == block) { return NULL; }
if (states[ln]) { prev = ln; } if (states[ln]) { return ln; }
} }
return NULL; return NULL;
} }

View File

@@ -22,17 +22,18 @@ namespace dsp::demod {
dsp::taps::free(filterTaps); dsp::taps::free(filterTaps);
} }
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass) { void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass, bool highPass) {
_samplerate = samplerate; _samplerate = samplerate;
_bandwidth = bandwidth; _bandwidth = bandwidth;
_lowPass = lowPass; _lowPass = lowPass;
_highPass = highPass;
demod.init(NULL, bandwidth / 2.0, _samplerate); demod.init(NULL, bandwidth / 2.0, _samplerate);
loadDummyTaps(); loadDummyTaps();
fir.init(NULL, filterTaps); fir.init(NULL, filterTaps);
// Initialize taps // Initialize taps
updateFilter(lowPass); updateFilter(lowPass, highPass);
if constexpr (std::is_same_v<T, float>) { if constexpr (std::is_same_v<T, float>) {
demod.out.free(); demod.out.free();
@@ -48,7 +49,7 @@ namespace dsp::demod {
base_type::tempStop(); base_type::tempStop();
_samplerate = samplerate; _samplerate = samplerate;
demod.setDeviation(_bandwidth / 2.0, _samplerate); demod.setDeviation(_bandwidth / 2.0, _samplerate);
updateFilter(_lowPass); updateFilter(_lowPass, _highPass);
base_type::tempStart(); base_type::tempStart();
} }
@@ -58,13 +59,19 @@ namespace dsp::demod {
if (bandwidth == _bandwidth) { return; } if (bandwidth == _bandwidth) { return; }
_bandwidth = bandwidth; _bandwidth = bandwidth;
demod.setDeviation(_bandwidth / 2.0, _samplerate); demod.setDeviation(_bandwidth / 2.0, _samplerate);
updateFilter(_lowPass); updateFilter(_lowPass, _highPass);
} }
void setLowPass(bool lowPass) { void setLowPass(bool lowPass) {
assert(base_type::_block_init); assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
updateFilter(lowPass); updateFilter(lowPass, _highPass);
}
void setHighPass(bool highPass) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
updateFilter(_lowPass, highPass);
} }
void reset() { void reset() {
@@ -79,14 +86,14 @@ namespace dsp::demod {
inline int process(int count, dsp::complex_t* in, T* out) { inline int process(int count, dsp::complex_t* in, T* out) {
if constexpr (std::is_same_v<T, float>) { if constexpr (std::is_same_v<T, float>) {
demod.process(count, in, out); demod.process(count, in, out);
if (_lowPass) { if (filtering) {
std::lock_guard<std::mutex> lck(filterMtx); std::lock_guard<std::mutex> lck(filterMtx);
fir.process(count, out, out); fir.process(count, out, out);
} }
} }
if constexpr (std::is_same_v<T, stereo_t>) { if constexpr (std::is_same_v<T, stereo_t>) {
demod.process(count, in, demod.out.writeBuf); demod.process(count, in, demod.out.writeBuf);
if (_lowPass) { if (filtering) {
std::lock_guard<std::mutex> lck(filterMtx); std::lock_guard<std::mutex> lck(filterMtx);
fir.process(count, demod.out.writeBuf, demod.out.writeBuf); fir.process(count, demod.out.writeBuf, demod.out.writeBuf);
} }
@@ -107,17 +114,25 @@ namespace dsp::demod {
} }
private: private:
void updateFilter(bool lowPass) { void updateFilter(bool lowPass, bool highPass) {
std::lock_guard<std::mutex> lck(filterMtx); std::lock_guard<std::mutex> lck(filterMtx);
// Update values // Update values
_lowPass = lowPass; _lowPass = lowPass;
_highPass = highPass;
filtering = (lowPass || highPass);
// Free filter taps // Free filter taps
dsp::taps::free(filterTaps); dsp::taps::free(filterTaps);
// Generate filter depending on the low pass settings // Generate filter depending on low and high pass settings
if (_lowPass) { if (_lowPass && _highPass) {
filterTaps = dsp::taps::bandPass<float>(300.0, _bandwidth / 2.0, 100.0, _samplerate);
}
else if (_highPass) {
filterTaps = dsp::taps::highPass(300.0, 100.0, _samplerate);
}
else if (_lowPass) {
filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate); filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
} }
else { else {
@@ -137,6 +152,7 @@ namespace dsp::demod {
double _samplerate; double _samplerate;
double _bandwidth; double _bandwidth;
bool _lowPass; bool _lowPass;
bool _highPass;
bool filtering; bool filtering;
Quadrature demod; Quadrature demod;

View File

@@ -1,303 +0,0 @@
#pragma once
#include "../channel/rx_vfo.h"
#include "../demod/quadrature.h"
#include "../filter/fir.h"
#include "../taps/high_pass.h"
#include <fftw3.h>
#include <map>
#include <atomic>
#define CTCSS_DECODE_SAMPLERATE 500//250.0
#define CTCSS_DECODE_BANDWIDTH 200.0
#define CTCSS_DECODE_OFFSET 160.55
namespace dsp::noise_reduction {
enum CTCSSTone {
/**
* Indicates that any valid tone will let audio through.
*/
CTCSS_TONE_ANY = -2,
/**
* Indicates that no tone is being received, or to act as a decoder only, letting audio through continuously.
*/
CTCSS_TONE_NONE = -1,
/**
* CTCSS Tone Frequency.
*/
CTCSS_TONE_67Hz,
CTCSS_TONE_69_3Hz,
CTCSS_TONE_71_9Hz,
CTCSS_TONE_74_4Hz,
CTCSS_TONE_77Hz,
CTCSS_TONE_79_7Hz,
CTCSS_TONE_82_5Hz,
CTCSS_TONE_85_4Hz,
CTCSS_TONE_88_5Hz,
CTCSS_TONE_91_5Hz,
CTCSS_TONE_94_8Hz,
CTCSS_TONE_97_4Hz,
CTCSS_TONE_100Hz,
CTCSS_TONE_103_5Hz,
CTCSS_TONE_107_2Hz,
CTCSS_TONE_110_9Hz,
CTCSS_TONE_114_8Hz,
CTCSS_TONE_118_8Hz,
CTCSS_TONE_123Hz,
CTCSS_TONE_127_3Hz,
CTCSS_TONE_131_8Hz,
CTCSS_TONE_136_5Hz,
CTCSS_TONE_141_3Hz,
CTCSS_TONE_146_2Hz,
CTCSS_TONE_150Hz,
CTCSS_TONE_151_4Hz,
CTCSS_TONE_156_7Hz,
CTCSS_TONE_159_8Hz,
CTCSS_TONE_162_2Hz,
CTCSS_TONE_165_5Hz,
CTCSS_TONE_167_9Hz,
CTCSS_TONE_171_3Hz,
CTCSS_TONE_173_8Hz,
CTCSS_TONE_177_3Hz,
CTCSS_TONE_179_9Hz,
CTCSS_TONE_183_5Hz,
CTCSS_TONE_186_2Hz,
CTCSS_TONE_189_9Hz,
CTCSS_TONE_192_8Hz,
CTCSS_TONE_196_6Hz,
CTCSS_TONE_199_5Hz,
CTCSS_TONE_203_5Hz,
CTCSS_TONE_206_5Hz,
CTCSS_TONE_210_7Hz,
CTCSS_TONE_218_1Hz,
CTCSS_TONE_225_7Hz,
CTCSS_TONE_229_1Hz,
CTCSS_TONE_233_6Hz,
CTCSS_TONE_241_8Hz,
CTCSS_TONE_250_3Hz,
CTCSS_TONE_254_1Hz,
_CTCSS_TONE_COUNT
};
const float CTCSS_TONES[_CTCSS_TONE_COUNT] = {
67.0f,
69.3f,
71.9f,
74.4f,
77.0f,
79.7f,
82.5f,
85.4f,
88.5f,
91.5f,
94.8f,
97.4f,
100.0f,
103.5f,
107.2f,
110.9f,
114.8f,
118.8f,
123.0f,
127.3f,
131.8f,
136.5f,
141.3f,
146.2f,
150.0f,
151.4f,
156.7f,
159.8f,
162.2f,
165.5f,
167.9f,
171.3f,
173.8f,
177.3f,
179.9f,
183.5f,
186.2f,
189.9f,
192.8f,
196.6f,
199.5f,
203.5f,
206.5f,
210.7f,
218.1f,
225.7f,
229.1f,
233.6f,
241.8f,
250.3f,
254.1f
};
class CTCSSSquelch : public Processor<stereo_t, stereo_t> {
using base_type = Processor<stereo_t, stereo_t>;
public:
CTCSSSquelch() {}
CTCSSSquelch(stream<stereo_t>* in, double samplerate) { init(in, samplerate); }
~CTCSSSquelch() {
// If not initialized, do nothing
if (!base_type::_block_init) { return; }
// Stop the DSP thread
base_type::stop();
}
void init(stream<stereo_t>* in, double samplerate) {
// Save settings
_samplerate = samplerate;
// Create dummy taps just for initialization
float dummy[1] = { 1.0f };
auto dummyTaps = dsp::taps::fromArray(1, dummy);
// Initialize the DDC and FM demod
ddc.init(NULL, samplerate, CTCSS_DECODE_SAMPLERATE, CTCSS_DECODE_BANDWIDTH, CTCSS_DECODE_OFFSET);
fm.init(NULL, 1.0, CTCSS_DECODE_SAMPLERATE);
// Initilize the base block class
base_type::init(in);
}
void setSamplerate(double samplerate) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
_samplerate = samplerate;
ddc.setInSamplerate(samplerate);
base_type::tempStart();
}
void setRequiredTone(CTCSSTone tone) {
assert(base_type::_block_init);
requiredTone = tone;
}
CTCSSTone getCurrentTone() {
assert(base_type::_block_init);
return currentTone;
}
inline int process(int count, const stereo_t* in, stereo_t* out) {
// Shift and resample to the correct samplerate
int ddcOutCount = ddc.process(count, (complex_t*)in, ddc.out.writeBuf);
// FM Demod the CTCSS tone
fm.process(ddcOutCount, ddc.out.writeBuf, fm.out.writeBuf);
// Get the required tone
const CTCSSTone rtone = requiredTone;
// Detect the tone frequency
for (int i = 0; i < ddcOutCount; i++) {
// Compute the running mean
const float val = fm.out.writeBuf[i];
mean = 0.95f*mean + 0.05f*val;
// Compute the running variance
const float err = val - mean;
var = 0.95f*var + 0.05f*err*err;
// Run a schmitt trigger on the variance
bool nvarOk = varOk ? (var < 1100.0f) : (var < 1000.0f);
// Check if the tone has to be rematched
if (nvarOk && (!varOk || mean < minFreq || mean > maxFreq)) {
// Compute the absolute frequency
float freq = mean + CTCSS_DECODE_OFFSET;
// Check it against the known tones
if (freq < CTCSS_TONES[0] - 2.5) {
currentTone = CTCSS_TONE_NONE;
}
else if (freq > CTCSS_TONES[_CTCSS_TONE_COUNT-1] + 2.5) {
currentTone = CTCSS_TONE_NONE;
}
else if (freq < CTCSS_TONES[0]) {
currentTone = (CTCSSTone)0;
}
else if (freq > CTCSS_TONES[_CTCSS_TONE_COUNT-1]) {
currentTone = (CTCSSTone)(_CTCSS_TONE_COUNT-1);
}
else {
int a = 0;
int b = _CTCSS_TONE_COUNT-1;
while (b - a > 1) {
int c = (a + b) >> 1;
((CTCSS_TONES[c] < freq) ? a : b) = c;
}
currentTone = (CTCSSTone)((freq - CTCSS_TONES[a] < CTCSS_TONES[b] - freq) ? a : b);
}
// Update the mute status
mute = !(currentTone == rtone || (currentTone != CTCSS_TONE_NONE && rtone == CTCSS_TONE_ANY));
// Unmuted the audio if needed
// TODO
// Recompute min and max freq if a valid tone is detected
if (currentTone != CTCSS_TONE_NONE) {
float c = CTCSS_TONES[currentTone];
float l = (currentTone > CTCSS_TONE_67Hz) ? CTCSS_TONES[currentTone - 1] : c - 2.5f;
float r = (currentTone < CTCSS_TONE_254_1Hz) ? CTCSS_TONES[currentTone + 1] : c + 2.5f;
minFreq = (l+c) / 2.0f;
maxFreq = (r+c) / 2.0f;
}
}
// Check for a rising edge on the variance
if (!nvarOk && varOk) {
// Mute the audio
// TODO
mute = true;
currentTone = CTCSS_TONE_NONE;
}
// Save the new variance state
varOk = nvarOk;
}
// DEBUG ONLY
if ((rtone != CTCSS_TONE_NONE) && mute) {
memset(out, 0, count * sizeof(stereo_t));
}
else {
memcpy(out, in, count * sizeof(stereo_t));
}
return count;
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
process(count, base_type::_in->readBuf, base_type::out.writeBuf);
base_type::_in->flush();
if (!base_type::out.swap(count)) { return -1; }
return count;
}
private:
double _samplerate;
std::atomic<CTCSSTone> requiredTone = CTCSS_TONE_ANY;
float mean = 0.0f;
float var = 0.0f;
bool varOk = false;
float minFreq = 0.0f;
float maxFreq = 0.0f;
bool mute = true;
std::atomic<CTCSSTone> currentTone = CTCSS_TONE_NONE;
channel::RxVFO ddc;
demod::Quadrature fm;
};
}

View File

@@ -3,14 +3,14 @@
// TODO: Rewrite better!!!!! // TODO: Rewrite better!!!!!
namespace dsp::noise_reduction { namespace dsp::noise_reduction {
class PowerSquelch : public Processor<complex_t, complex_t> { class Squelch : public Processor<complex_t, complex_t> {
using base_type = Processor<complex_t, complex_t>; using base_type = Processor<complex_t, complex_t>;
public: public:
PowerSquelch() {} Squelch() {}
PowerSquelch(stream<complex_t>* in, double level) {} Squelch(stream<complex_t>* in, double level) {}
~PowerSquelch() { ~Squelch() {
if (!base_type::_block_init) { return; } if (!base_type::_block_init) { return; }
base_type::stop(); base_type::stop();
buffer::free(normBuffer); buffer::free(normBuffer);
@@ -31,11 +31,8 @@ namespace dsp::noise_reduction {
} }
inline int process(int count, const complex_t* in, complex_t* out) { inline int process(int count, const complex_t* in, complex_t* out) {
// Compute the amplitude of each sample float sum;
volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)in, count); volk_32fc_magnitude_32f(normBuffer, (lv_32fc_t*)in, count);
// Compute the mean amplitude
float sum = 0.0f;
volk_32f_accumulator_s32f(&sum, normBuffer, count); volk_32f_accumulator_s32f(&sum, normBuffer, count);
sum /= (float)count; sum /= (float)count;
@@ -49,6 +46,8 @@ namespace dsp::noise_reduction {
return count; return count;
} }
//DEFAULT_PROC_RUN();
int run() { int run() {
int count = base_type::_in->read(); int count = base_type::_in->read();
if (count < 0) { return -1; } if (count < 0) { return -1; }

View File

@@ -5,6 +5,8 @@ namespace dsp {
template <class T> template <class T>
class Source : public block { class Source : public block {
public: public:
Source() {}
Source() { init(); } Source() { init(); }
virtual ~Source() {} virtual ~Source() {}

View File

@@ -5,120 +5,113 @@
#include <gui/main_window.h> #include <gui/main_window.h>
#include <gui/style.h> #include <gui/style.h>
#include <signal_path/signal_path.h> #include <signal_path/signal_path.h>
#include <utils/optionlist.h>
#include <gui/dialogs/dialog_box.h>
namespace sourcemenu { namespace sourcemenu {
int offsetMode = 0;
int sourceId = 0; int sourceId = 0;
EventHandler<std::string> sourcesChangedHandler; double customOffset = 0.0;
EventHandler<std::string> sourceUnregisterHandler; double effectiveOffset = 0.0;
OptionList<std::string, std::string> sources; int decimationPower = 0;
std::string selectedSource;
int decimId = 0;
OptionList<int, int> decimations;
bool iqCorrection = false; bool iqCorrection = false;
bool invertIQ = false; bool invertIQ = false;
int offsetId = 0; EventHandler<std::string> sourceRegisteredHandler;
double manualOffset = 0.0; EventHandler<std::string> sourceUnregisterHandler;
std::string selectedOffset; EventHandler<std::string> sourceUnregisteredHandler;
double effectiveOffset = 0.0;
OptionList<std::string, double> offsets;
std::map<std::string, double> namedOffsets;
bool showAddOffsetDialog = false; std::vector<std::string> sourceNames;
char newOffsetName[1024]; std::string sourceNamesTxt;
double newOffset = 0.0; std::string selectedSource;
bool showDelOffsetDialog = false;
std::string delOffsetName = "";
// Offset IDs
enum { enum {
OFFSET_ID_NONE, OFFSET_MODE_NONE,
OFFSET_ID_MANUAL, OFFSET_MODE_CUSTOM,
OFFSET_ID_CUSTOM_BASE 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() { const char* offsetModesTxt = "None\0"
// Compute the effective offset "Custom\0"
switch (offsetId) { "SpyVerter\0"
case OFFSET_ID_NONE: "Ham-It-Up\0"
effectiveOffset = 0; "MMDS S-band (1998MHz)\0"
break; "DK5AV X-Band\0"
case OFFSET_ID_MANUAL: "Ku LNB (9750MHz)\0"
effectiveOffset = manualOffset; "Ku LNB (10700MHz)\0";
break;
default:
effectiveOffset = namedOffsets[offsets.name(offsetId)];
break;
}
// 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); sigpath::sourceManager.setTuningOffset(effectiveOffset);
} }
void selectOffsetById(int id) {
// Update the offset mode
offsetId = id;
selectedOffset = offsets.name(id);
// Update the offset
updateOffset();
}
void selectOffsetByName(const std::string& name) {
// If the name doesn't exist, select 'None'
if (!offsets.nameExists(name)) {
selectOffsetById(OFFSET_ID_NONE);
return;
}
// Select using the ID associated with the name
selectOffsetById(offsets.nameId(name));
}
void refreshSources() { void refreshSources() {
// Get sources sourceNames = sigpath::sourceManager.getSourceNames();
auto sourceNames = sigpath::sourceManager.getSourceNames(); sourceNamesTxt.clear();
// Define source options
sources.clear();
for (auto name : sourceNames) { for (auto name : sourceNames) {
sources.define(name, name, name); sourceNamesTxt += name;
sourceNamesTxt += '\0';
} }
} }
void selectSource(std::string name) { void selectSource(std::string name) {
// If there is no source, give up if (sourceNames.empty()) {
if (sources.empty()) {
sourceId = 0;
selectedSource.clear(); selectedSource.clear();
return; 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 void onSourceRegistered(std::string name, void* ctx) {
if (!sources.valueExists(name)) { refreshSources();
selectSource(sources.value(0));
if (selectedSource.empty()) {
sourceId = 0;
selectSource(sourceNames[0]);
return; return;
} }
// Update the GUI variables sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
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);
} }
void onSourceUnregister(std::string name, void* ctx) { void onSourceUnregister(std::string name, void* ctx) {
@@ -127,173 +120,60 @@ namespace sourcemenu {
// TODO: Stop everything // TODO: Stop everything
} }
void reloadOffsets() { void onSourceUnregistered(std::string name, void* ctx) {
// Clear list refreshSources();
offsets.clear();
namedOffsets.clear();
// Define special offset modes if (sourceNames.empty()) {
offsets.define("None", OFFSET_ID_NONE); selectedSource = "";
offsets.define("Manual", OFFSET_ID_MANUAL); return;
// 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();
} }
// Define custom offsets if (name == selectedSource) {
for (auto& [name, offset] : namedOffsets) { sourceId = std::clamp<int>(sourceId, 0, sourceNames.size() - 1);
offsets.define(name, offsets.size()); selectSource(sourceNames[sourceId]);
return;
} }
// Release the config file sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
core::configManager.release();
} }
void init() { void init() {
// Load offset modes
reloadOffsets();
// Define decimation values
decimations.define(1, "None", 1);
decimations.define(2, "2x", 2);
decimations.define(4, "4x", 4);
decimations.define(8, "8x", 8);
decimations.define(16, "16x", 16);
decimations.define(32, "32x", 32);
decimations.define(64, "64x", 64);
// Acquire the config file
core::configManager.acquire(); core::configManager.acquire();
std::string selected = core::configManager.conf["source"];
// Load other settings customOffset = core::configManager.conf["offset"];
std::string selectedSource = core::configManager.conf["source"]; offsetMode = core::configManager.conf["offsetMode"];
manualOffset = core::configManager.conf["manualOffset"]; decimationPower = core::configManager.conf["decimationPower"];
std::string selectedOffset = core::configManager.conf["selectedOffset"];
iqCorrection = core::configManager.conf["iqCorrection"]; iqCorrection = core::configManager.conf["iqCorrection"];
invertIQ = core::configManager.conf["invertIQ"]; invertIQ = core::configManager.conf["invertIQ"];
int decimation = core::configManager.conf["decimation"];
if (decimations.keyExists(decimation)) {
decimId = decimations.keyId(decimation);
}
// Release the config file
core::configManager.release();
// Select the source module
refreshSources();
selectSource(selectedSource);
// Update frontend settings
sigpath::iqFrontEnd.setDCBlocking(iqCorrection); sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
sigpath::iqFrontEnd.setInvertIQ(invertIQ); sigpath::iqFrontEnd.setInvertIQ(invertIQ);
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId)); updateOffset();
selectOffsetByName(selectedOffset);
// Register handlers refreshSources();
sourcesChangedHandler.handler = onSourcesChanged; selectSource(selected);
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
sourceRegisteredHandler.handler = onSourceRegistered;
sourceUnregisterHandler.handler = onSourceUnregister; sourceUnregisterHandler.handler = onSourceUnregister;
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourcesChangedHandler); sourceUnregisteredHandler.handler = onSourceUnregistered;
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler); sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourcesChangedHandler); sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
}
void addOffset(const std::string& name, double offset) { core::configManager.release();
// Acquire the config file
core::configManager.acquire();
// Define a new offset
core::configManager.conf["offsets"][name] = offset;
// Acquire the config file
core::configManager.release(true);
// Reload the offsets
reloadOffsets();
// Attempt to re-select the same one
selectOffsetByName(selectedOffset);
}
void delOffset(const std::string& name) {
// Acquire the config file
core::configManager.acquire();
// Define a new offset
core::configManager.conf["offsets"].erase(name);
// Acquire the config file
core::configManager.release(true);
// Reload the offsets
reloadOffsets();
// Attempt to re-select the same one
selectOffsetByName(selectedOffset);
}
bool addOffsetDialog() {
bool open = true;
gui::mainWindow.lockWaterfallControls = true;
float menuWidth = ImGui::GetContentRegionAvail().x;
const char* id = "Add offset##sdrpp_add_offset_dialog_";
ImGui::OpenPopup(id);
if (ImGui::BeginPopup(id, ImGuiWindowFlags_NoResize)) {
ImGui::LeftLabel("Name");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
ImGui::InputText("##sdrpp_add_offset_name", newOffsetName, 1023);
ImGui::LeftLabel("Offset");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
ImGui::InputDouble("##sdrpp_add_offset_offset", &newOffset);
bool nameExists = offsets.nameExists(newOffsetName);
bool reservedName = !strcmp(newOffsetName, "None") || !strcmp(newOffsetName, "Manual");
bool denyApply = !newOffsetName[0] || nameExists || reservedName;
if (nameExists) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "An offset with the given name already exists.");
}
else if (reservedName) {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "The given name is reserved.");
}
if (denyApply) { style::beginDisabled(); }
if (ImGui::Button("Apply")) {
addOffset(newOffsetName, newOffset);
open = false;
}
if (denyApply) { style::endDisabled(); }
ImGui::SameLine();
if (ImGui::Button("Cancel")) {
open = false;
}
ImGui::EndPopup();
}
return open;
} }
void draw(void* ctx) { void draw(void* ctx) {
float itemWidth = ImGui::GetContentRegionAvail().x; float itemWidth = ImGui::GetContentRegionAvail().x;
float lineHeight = ImGui::GetTextLineHeightWithSpacing();
float spacing = lineHeight - ImGui::GetTextLineHeight();
bool running = gui::mainWindow.sdrIsRunning(); bool running = gui::mainWindow.sdrIsRunning();
if (running) { style::beginDisabled(); } if (running) { style::beginDisabled(); }
ImGui::SetNextItemWidth(itemWidth); ImGui::SetNextItemWidth(itemWidth);
if (ImGui::Combo("##source", &sourceId, sources.txt)) { if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
std::string newSource = sources.value(sourceId); selectSource(sourceNames[sourceId]);
selectSource(newSource);
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["source"] = newSource; core::configManager.conf["source"] = sourceNames[sourceId];
core::configManager.release(true); core::configManager.release(true);
} }
@@ -316,45 +196,21 @@ namespace sourcemenu {
} }
ImGui::LeftLabel("Offset mode"); ImGui::LeftLabel("Offset mode");
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX() - 2.0f*(lineHeight + 1.5f*spacing)); ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##_sdrpp_offset", &offsetId, offsets.txt)) { if (ImGui::Combo("##_sdrpp_offset_mode", &offsetMode, offsetModesTxt)) {
selectOffsetById(offsetId); updateOffset();
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["selectedOffset"] = offsets.key(offsetId); core::configManager.conf["offsetMode"] = offsetMode;
core::configManager.release(true); core::configManager.release(true);
} }
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::BeginDisabled(); }
if (ImGui::Button("-##_sdrpp_offset_del_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
delOffsetName = selectedOffset;
showDelOffsetDialog = true;
}
if (offsetId < OFFSET_ID_CUSTOM_BASE) { ImGui::EndDisabled(); }
ImGui::SameLine();
ImGui::SetCursorPosX(ImGui::GetCursorPosX() - spacing);
if (ImGui::Button("+##_sdrpp_offset_add_", ImVec2(lineHeight + 0.5f*spacing, 0))) {
strcpy(newOffsetName, "New Offset");
showAddOffsetDialog = true;
}
// Offset delete confirmation
if (ImGui::GenericDialog("sdrpp_del_offset_confirm", showDelOffsetDialog, GENERIC_DIALOG_BUTTONS_YES_NO, []() {
ImGui::Text("Deleting offset named \"%s\". Are you sure?", delOffsetName.c_str());
}) == GENERIC_DIALOG_BUTTON_YES) {
delOffset(delOffsetName);
}
// Offset add diaglog
if (showAddOffsetDialog) { showAddOffsetDialog = addOffsetDialog(); }
ImGui::LeftLabel("Offset"); ImGui::LeftLabel("Offset");
ImGui::FillWidth(); ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
if (offsetId == OFFSET_ID_MANUAL) { if (offsetMode == OFFSET_MODE_CUSTOM) {
if (ImGui::InputDouble("##freq_offset", &manualOffset, 1.0, 100.0)) { if (ImGui::InputDouble("##freq_offset", &customOffset, 1.0, 100.0)) {
updateOffset(); updateOffset();
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["manualOffset"] = manualOffset; core::configManager.conf["offset"] = customOffset;
core::configManager.release(true); core::configManager.release(true);
} }
} }
@@ -366,11 +222,11 @@ namespace sourcemenu {
if (running) { style::beginDisabled(); } if (running) { style::beginDisabled(); }
ImGui::LeftLabel("Decimation"); ImGui::LeftLabel("Decimation");
ImGui::FillWidth(); ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
if (ImGui::Combo("##source_decim", &decimId, decimations.txt)) { if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) {
sigpath::iqFrontEnd.setDecimation(decimations.value(decimId)); sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
core::configManager.acquire(); core::configManager.acquire();
core::configManager.conf["decimation"] = decimations.key(decimId); core::configManager.conf["decimationPower"] = decimationPower;
core::configManager.release(true); core::configManager.release(true);
} }
if (running) { style::endDisabled(); } if (running) { style::endDisabled(); }

View File

@@ -3,7 +3,6 @@
#include <gui/style.h> #include <gui/style.h>
#include <gui/gui.h> #include <gui/gui.h>
#include <backend.h> #include <backend.h>
#include <utils/hrfreq.h>
#ifndef IMGUI_DEFINE_MATH_OPERATORS #ifndef IMGUI_DEFINE_MATH_OPERATORS
#define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS
@@ -91,7 +90,6 @@ void FrequencySelect::moveCursorToDigit(int i) {
void FrequencySelect::draw() { void FrequencySelect::draw() {
auto window = ImGui::GetCurrentWindow(); auto window = ImGui::GetCurrentWindow();
auto io = ImGui::GetIO();
widgetPos = ImGui::GetWindowContentRegionMin(); widgetPos = ImGui::GetWindowContentRegionMin();
ImVec2 cursorPos = ImGui::GetCursorPos(); ImVec2 cursorPos = ImGui::GetCursorPos();
widgetPos.x += window->Pos.x + cursorPos.x; widgetPos.x += window->Pos.x + cursorPos.x;
@@ -134,7 +132,7 @@ void FrequencySelect::draw() {
ImVec2 mousePos = ImGui::GetMousePos(); ImVec2 mousePos = ImGui::GetMousePos();
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left); bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right); bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
int mw = io.MouseWheel; int mw = ImGui::GetIO().MouseWheel;
bool onDigit = false; bool onDigit = false;
bool hovered = false; bool hovered = false;
@@ -176,7 +174,7 @@ void FrequencySelect::draw() {
moveCursorToDigit(i + 1); moveCursorToDigit(i + 1);
} }
auto chars = io.InputQueueCharacters; auto chars = ImGui::GetIO().InputQueueCharacters;
// For each keyboard characters, type it // For each keyboard characters, type it
for (int j = 0; j < chars.Size; j++) { for (int j = 0; j < chars.Size; j++) {
@@ -196,34 +194,6 @@ void FrequencySelect::draw() {
} }
} }
digitHovered = hovered; digitHovered = hovered;
if (isInArea(mousePos, digitTopMins[0], digitBottomMaxs[11])) {
bool shortcutKey = io.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
bool ctrlOnly = (io.KeyMods == ImGuiKeyModFlags_Ctrl);
bool shiftOnly = (io.KeyMods == ImGuiKeyModFlags_Shift);
bool copy = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_C)) || (ctrlOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
bool paste = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_V)) || (shiftOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
if (copy) {
// Convert the freqency to a string
std::string freqStr = hrfreq::toString(frequency);
// Write it to the clipboard
ImGui::SetClipboardText(freqStr.c_str());
}
if (paste) {
// Attempt to parse the clipboard as a number
const char* clip = ImGui::GetClipboardText();
// If the clipboard is not empty, attempt to parse it
if (clip) {
double newFreq;
if (hrfreq::fromString(clip, newFreq)) {
setFrequency(abs(newFreq));
frequencyChanged = true;
}
}
}
}
} }
uint64_t freq = 0; uint64_t freq = 0;

View File

@@ -85,8 +85,8 @@ void SourceManager::tune(double freq) {
return; return;
} }
// TODO: No need to always retune the hardware in Panadapter mode // TODO: No need to always retune the hardware in Panadapter mode
selectedHandler->tuneHandler(abs(((tuneMode == TuningMode::NORMAL) ? (freq + tuneOffset) : ifFreq)), selectedHandler->ctx); selectedHandler->tuneHandler(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset, selectedHandler->ctx);
onRetune.emit(freq + tuneOffset); onRetune.emit(freq);
currentFreq = freq; currentFreq = freq;
} }

View File

@@ -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
}
}

View File

@@ -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);
}

View File

@@ -1,3 +1,3 @@
#pragma once #pragma once
#define VERSION_STR "1.3.0" #define VERSION_STR "1.2.0"

View File

@@ -1,66 +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);
volk_32f_s32f_multiply_32f(out, out, -1.0f, count);
return count;
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
phase = 0.0f;
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
process(count, base_type::_in->readBuf, base_type::out.writeBuf);
base_type::_in->flush();
if (!base_type::out.swap(count)) { return -1; }
return count;
}
protected:
float _invDeviation;
float phase = 0.0f;
};
}

View File

@@ -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

View 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;
}
};
}

View 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)

View File

@@ -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)

View File

@@ -5,33 +5,6 @@
#include <dsp/multirate/polyphase_bank.h> #include <dsp/multirate/polyphase_bank.h>
#include <dsp/math/step.h> #include <dsp/math/step.h>
#define LINE_SIZE 945
#define SYNC_LEN 70
#define SYNC_SIDE_LEN 17
#define SYNC_L_START (LINE_SIZE - SYNC_SIDE_LEN)
#define SYNC_R_START (SYNC_LEN/2)
#define SYNC_R_END (SYNC_R_START + (SYNC_LEN/2) + SYNC_SIDE_LEN)
#define SYNC_HALF_LEN ((SYNC_LEN/2) + SYNC_SIDE_LEN)
#define EQUAL_LEN 35
#define HBLANK_START SYNC_LEN
#define HBLANK_END 155
#define HBLANK_LEN (HBLANK_END - HBLANK_START + 1)
#define SYNC_LEVEL (-0.428)
#define COLORBURST_START 84
#define COLORBURST_LEN 33
#define MAX_LOCK 1000
dsp::complex_t PHASE_REF[2] = {
{ -0.707106781186547f, 0.707106781186547f },
{ -0.707106781186547f, -0.707106781186547f }
};
class LineSync : public dsp::Processor<float, float> { class LineSync : public dsp::Processor<float, float> {
using base_type = dsp::Processor<float, float>; using base_type = dsp::Processor<float, float>;
public: public:
@@ -54,17 +27,41 @@ public:
_interpPhaseCount = interpPhaseCount; _interpPhaseCount = interpPhaseCount;
_interpTapCount = interpTapCount; _interpTapCount = interpTapCount;
pcl.init(_muGain, _omegaGain, 0.0, 0.0, 1.0, _omega, _omega * (1.0 - omegaRelLimit), _omega * (1.0 + omegaRelLimit));
generateInterpTaps(); generateInterpTaps();
buffer = dsp::buffer::alloc<float>(STREAM_BUFFER_SIZE + _interpTapCount); buffer = dsp::buffer::alloc<float>(STREAM_BUFFER_SIZE + _interpTapCount);
bufStart = &buffer[_interpTapCount - 1]; bufStart = &buffer[_interpTapCount - 1];
// TODO: Needs tuning, so do the gains
maxPeriod = (int32_t)(1.0001 * (float)(1 << 30));
minPeriod = (int32_t)(0.9999 * (float)(1 << 30));
base_type::init(in); base_type::init(in);
} }
void setOmegaGain(double omegaGain) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_omegaGain = omegaGain;
pcl.setCoefficients(_muGain, _omegaGain);
}
void setMuGain(double muGain) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_muGain = muGain;
pcl.setCoefficients(_muGain, _omegaGain);
}
void setOmegaRelLimit(double omegaRelLimit) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
_omegaRelLimit = omegaRelLimit;
pcl.setFreqLimits(_omega * (1.0 - _omegaRelLimit), _omega * (1.0 + _omegaRelLimit));
}
void setSyncLevel(float level) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
syncLevel = level;
}
void setInterpParams(int interpPhaseCount, int interpTapCount) { void setInterpParams(int interpPhaseCount, int interpTapCount) {
assert(base_type::_block_init); assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
@@ -84,7 +81,8 @@ public:
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop(); base_type::tempStop();
offset = 0; offset = 0;
phase = 0; pcl.phase = 0.0f;
pcl.freq = _omega;
base_type::tempStart(); base_type::tempStart();
} }
@@ -95,120 +93,61 @@ public:
// Copy data to work buffer // Copy data to work buffer
memcpy(bufStart, base_type::_in->readBuf, count * sizeof(float)); memcpy(bufStart, base_type::_in->readBuf, count * sizeof(float));
// Process samples while they are available if (test2) {
test2 = false;
offset += 5;
}
// Process all samples
while (offset < count) { while (offset < count) {
// While the offset is negative, out put zeros // Calculate new output value
while (offset < 0 && pixel < LINE_SIZE) { int phase = std::clamp<int>(floorf(pcl.phase * (float)_interpPhaseCount), 0, _interpPhaseCount - 1);
// Output a zero float outVal;
base_type::out.writeBuf[pixel++] = 0.0f; volk_32f_x2_dot_prod_32f(&outVal, &buffer[offset], interpBank.phases[phase], _interpTapCount);
base_type::out.writeBuf[outCount++] = outVal;
// Increment the phase // If the end of the line is reached, process it and determin error
phase += period; float error = 0;
offset += (phase >> 30); if (outCount >= 720) {
phase &= 0x3FFFFFFF; // Compute averages.
}
// Process as much of a line as possible
while (offset < count && pixel < LINE_SIZE) {
// Compute the output sample
volk_32f_x2_dot_prod_32f(&base_type::out.writeBuf[pixel++], &buffer[offset], interpBank.phases[(phase >> 23) & 0x7F], _interpTapCount);
// Increment the phase
phase += period;
offset += (phase >> 30);
phase &= 0x3FFFFFFF;
}
// If the line is done, process it
if (pixel == LINE_SIZE) {
// Compute averages. (TODO: Try faster method)
float left = 0.0f, right = 0.0f; float left = 0.0f, right = 0.0f;
int lc = 0, rc = 0; for (int i = (720-17); i < 720; i++) {
for (int i = SYNC_L_START; i < LINE_SIZE; i++) {
left += base_type::out.writeBuf[i]; left += base_type::out.writeBuf[i];
lc++;
} }
for (int i = 0; i < SYNC_R_START; i++) { for (int i = 0; i < 27; i++) {
left += base_type::out.writeBuf[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]; 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 if (++counter >= 100) {
float error = (left - right) * (1.0f/((float)SYNC_HALF_LEN)); counter = 0;
//flog::warn("Left: {}, Right: {}, Error: {}, Freq: {}, Phase: {}", left, right, error, pcl.freq, pcl.phase);
// Compute the change in phase and frequency due to the error
float periodDelta = error * _omegaGain;
float phaseDelta = error * _muGain;
// Normalize the phase delta (TODO: Make faster)
while (phaseDelta <= -1.0f) {
phaseDelta += 1.0f;
offset--;
}
while (phaseDelta >= 1.0f) {
phaseDelta -= 1.0f;
offset++;
}
// Update the period (TODO: Clamp error*omegaGain to prevent weird shit with corrupt samples)
period += (int32_t)(periodDelta * (float)(1 << 30));
period = std::clamp<uint32_t>(period, minPeriod, maxPeriod);
// Update the phase
phase += (int32_t)(phaseDelta * (float)(1 << 30));
// Normalize the phase
uint32_t overflow = phase >> 30;
if (overflow) {
if (error < 0) {
offset -= 4 - overflow;
}
else {
offset += overflow;
}
}
phase &= 0x3FFFFFFF;
// Find the lowest value
float lowest = INFINITY;
int lowestId = -1;
for (int i = 0; i < LINE_SIZE; i++) {
float val = base_type::out.writeBuf[i];
if (val < lowest) {
lowest = val;
lowestId = i;
}
}
// Check the the line is in lock
bool lineLocked = (lowestId < SYNC_R_END || lowestId >= SYNC_L_START);
// Update the lock status based on the line lock
if (!lineLocked && locked) {
locked--;
}
else if (lineLocked && locked < MAX_LOCK) {
locked++;
}
// If not locked, attempt to lock by forcing the sync to happen at the right spot
// TODO: This triggers waaaay too easily at low SNR
if (!locked && fastLock) {
offset += lowestId - SYNC_R_START;
locked = MAX_LOCK / 2;
} }
// Output line // Output line
if (!base_type::out.swap(LINE_SIZE)) { break; } if (!base_type::out.swap(outCount)) { break; }
pixel = 0; 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; offset -= count;
// Update delay buffer // Update delay buffer
@@ -216,15 +155,16 @@ public:
// Swap if some data was generated // Swap if some data was generated
base_type::_in->flush(); 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; int counter = 0;
bool fastLock = true;
protected: protected:
void generateInterpTaps() { void generateInterpTaps() {
@@ -235,6 +175,7 @@ protected:
} }
dsp::multirate::PolyphaseBank<float> interpBank; dsp::multirate::PolyphaseBank<float> interpBank;
dsp::loop::PhaseControlLoop<double, false> pcl;
double _omega; double _omega;
double _omegaGain; double _omegaGain;
@@ -242,14 +183,11 @@ protected:
double _omegaRelLimit; double _omegaRelLimit;
int _interpPhaseCount; int _interpPhaseCount;
int _interpTapCount; int _interpTapCount;
float* buffer;
float* bufStart;
uint32_t phase = 0;
uint32_t maxPeriod;
uint32_t minPeriod;
float syncLevel = -0.03f;
int offset = 0; int offset = 0;
int pixel = 0; int outCount = 0;
float* buffer;
float* bufStart;
float syncLevel = -0.03f;
}; };

View File

@@ -16,13 +16,9 @@
#include <dsp/filter/fir.h> #include <dsp/filter/fir.h>
#include <dsp/taps/from_array.h> #include <dsp/taps/from_array.h>
#include "amplitude.h" #include "chrominance_filter.h"
#include <dsp/demod/am.h>
#include <dsp/loop/fast_agc.h>
#include "filters.h" #include "chroma_pll.h"
#include <dsp/math/normalize_phase.h>
#include <fstream>
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
@@ -33,25 +29,24 @@ SDRPP_MOD_INFO{/* Name: */ "atv_decoder",
/* Max instances */ -1 /* 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 { class ATVDecoderModule : public ModuleManager::Instance {
public: public:
ATVDecoderModule(std::string name) : img(768, 576) { ATVDecoderModule(std::string name) : img(720, 625) {
this->name = name; 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(vfo->output, SAMPLE_RATE, SAMPLE_RATE / 2.0f);
demod.init(&agc.out, SAMPLE_RATE, SAMPLE_RATE / 2.0f);
// demod.init(vfo->output, dsp::demod::AM<float>::CARRIER, 8000000.0f, 50.0 / SAMPLE_RATE, 50.0 / SAMPLE_RATE, 0.0f, SAMPLE_RATE);
// demod.init(vfo->output, SAMPLE_RATE, SAMPLE_RATE / 2.0f);
sync.init(&demod.out, 1.0f, 1e-6, 1.0, 0.05); sync.init(&demod.out, 1.0f, 1e-6, 1.0, 0.05);
sink.init(&sync.out, handler, this); sink.init(&sync.out, handler, this);
r2c.init(NULL); r2c.init(NULL);
chromaTaps = dsp::taps::fromArray(CHROMA_FIR_SIZE, CHROMA_FIR);
fir.init(NULL, chromaTaps);
pll.init(NULL, 0.01, 0.0, dsp::math::hzToRads(4433618.75, SAMPLE_RATE), dsp::math::hzToRads(4433618.75*0.90, SAMPLE_RATE), dsp::math::hzToRads(4433618.75*1.1, SAMPLE_RATE));
agc.start();
demod.start(); demod.start();
sync.start(); sync.start();
sink.start(); sink.start();
@@ -63,10 +58,7 @@ class ATVDecoderModule : public ModuleManager::Instance {
if (vfo) { if (vfo) {
sigpath::vfoManager.deleteVFO(vfo); sigpath::vfoManager.deleteVFO(vfo);
} }
agc.stop();
demod.stop(); demod.stop();
sync.stop();
sink.stop();
gui::menu.removeEntry(name); gui::menu.removeEntry(name);
} }
@@ -90,218 +82,142 @@ class ATVDecoderModule : public ModuleManager::Instance {
style::beginDisabled(); style::beginDisabled();
} }
// Ideal width for testing: 750pixels
ImGui::FillWidth(); ImGui::FillWidth();
_this->img.draw(); _this->img.draw();
ImGui::TextUnformatted("Horizontal Sync:"); ImGui::LeftLabel("Sync");
ImGui::SameLine(); ImGui::FillWidth();
if (_this->sync.locked > 750) { 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"); ImGui::TextColored(ImVec4(0, 1, 0, 1), "Locked");
} }
else { else {
ImGui::TextUnformatted("Not locked"); ImGui::TextUnformatted("Not locked");
} }
ImGui::TextUnformatted("Vertical Sync:"); ImGui::Checkbox("Force Lock", &_this->sync.forceLock);
ImGui::SameLine();
if (_this->vlock > 15) {
ImGui::TextColored(ImVec4(0, 1, 0, 1), "Locked");
}
else {
ImGui::TextUnformatted("Not locked");
}
ImGui::Checkbox("Fast Lock", &_this->sync.fastLock);
ImGui::Checkbox("Color Mode", &_this->colorMode);
if (!_this->enabled) { if (!_this->enabled) {
style::endDisabled(); style::endDisabled();
} }
ImGui::Text("Gain: %f", _this->gain);
ImGui::Text("Offset: %f", _this->offset);
ImGui::Text("Subcarrier: %f", _this->subcarrierFreq);
} }
uint32_t pp = 0;
static void handler(float *data, int count, void *ctx) { static void handler(float *data, int count, void *ctx) {
ATVDecoderModule *_this = (ATVDecoderModule *)ctx; ATVDecoderModule *_this = (ATVDecoderModule *)ctx;
// Correct the offset // Convert line to complex
#if VOLK_VERSION_MAJOR > 2 || (VOLK_VERSION_MAJOR == 2 && VOLK_VERSION_MINOR >= 3) _this->r2c.process(720, data, _this->r2c.out.writeBuf);
volk_32f_s32f_add_32f(data, data, _this->offset, count);
#else // Isolate the chroma subcarrier
const float ofs = _this->offset; _this->fir.process(720, _this->r2c.out.writeBuf, _this->fir.out.writeBuf);
// Run chroma carrier through the PLL
_this->pll.process(720, _this->fir.out.writeBuf, _this->pll.out.writeBuf, ((_this->ypos%2)==1) ^ _this->evenFrame);
// Render line to the image without color
int lypos = _this->ypos - 1;
if (lypos < 0) { lypos = 624; }
uint32_t* lastLine = &((uint32_t *)_this->img.buffer)[(lypos < 313) ? (lypos*720*2) : ((((lypos - 313)*2)+1)*720) ];
uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[(_this->ypos < 313) ? (_this->ypos*720*2) : ((((_this->ypos - 313)*2)+1)*720) ];
//uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[_this->ypos*720];
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
data[i] += ofs; 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;
} }
#endif
// Vertical scan logic
// Correct the gain _this->ypos++;
volk_32f_s32f_multiply_32f(data, data, _this->gain, count); bool rollover = _this->ypos >= 625;
if (rollover) {
// Compute the sync levels {
float syncLLevel = 0.0f; std::lock_guard<std::mutex> lck(_this->evenFrameMtx);
float syncRLevel = 0.0f; _this->evenFrame = !_this->evenFrame;
volk_32f_accumulator_s32f(&syncLLevel, data, EQUAL_LEN);
volk_32f_accumulator_s32f(&syncRLevel, &data[EQUAL_LEN], SYNC_LEN - EQUAL_LEN);
syncLLevel *= 1.0f / EQUAL_LEN;
syncRLevel *= 1.0f / (SYNC_LEN - EQUAL_LEN);
float syncLevel = (syncLLevel + syncRLevel) * 0.5f; // TODO: It's technically correct but if the sizes were different it wouldn't be
// Compute the blanking level
float blankLevel = 0.0f;
volk_32f_accumulator_s32f(&blankLevel, &data[HBLANK_START], HBLANK_LEN);
blankLevel /= (float)HBLANK_LEN;
// Run the offset control loop
_this->offset -= (blankLevel / _this->gain)*0.001;
_this->offset = std::clamp<float>(_this->offset, -1.0f, 1.0f);
_this->gain -= (blankLevel - syncLevel + SYNC_LEVEL)*0.01f;
_this->gain = std::clamp<float>(_this->gain, 0.1f, 10.0f);
// Detect the sync type
uint16_t shortSync = (syncLLevel < 0.5f*SYNC_LEVEL) && (syncRLevel > 0.5f*SYNC_LEVEL) && (blankLevel > 0.5f*SYNC_LEVEL);
uint16_t longSync = (syncLLevel < 0.5f*SYNC_LEVEL) && (syncRLevel < 0.5f*SYNC_LEVEL) && (blankLevel < 0.5f*SYNC_LEVEL);
// Save sync type to history
_this->syncHistory = (_this->syncHistory << 2) | (longSync << 1) | shortSync;
// // If the line has a colorburst, decode it
// dsp::complex_t* buf1 = _this->r2c.out.readBuf;
// dsp::complex_t* buf2 = _this->r2c.out.writeBuf;
// if (true) {
// // Convert the line into complex
// _this->r2c.process(count, data, buf1);
// // Extract the chroma subcarrier (TODO: Optimise by running only where needed)
// for (int i = COLORBURST_START; i < count-(CHROMA_BANDPASS_DELAY+1); i++) {
// volk_32fc_x2_dot_prod_32fc((lv_32fc_t*)&buf2[i], (lv_32fc_t*)&buf1[i - CHROMA_BANDPASS_DELAY], (lv_32fc_t*)CHROMA_BANDPASS, CHROMA_BANDPASS_SIZE);
// }
// // Down convert the chroma subcarrier (TODO: Optimise by running only where needed)
// lv_32fc_t startPhase = { 1.0f, 0.0f };
// lv_32fc_t phaseDelta = { sinf(_this->subcarrierFreq), cosf(_this->subcarrierFreq) };
// #if VOLK_VERSION >= 030100
// volk_32fc_s32fc_x2_rotator2_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], &phaseDelta, &startPhase, count - COLORBURST_START);
// #else
// volk_32fc_s32fc_x2_rotator_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], phaseDelta, &startPhase, count - COLORBURST_START);
// #endif
// // Compute the phase of the burst
// dsp::complex_t burstAvg = { 0.0f, 0.0f };
// volk_32fc_accumulator_s32fc((lv_32fc_t*)&burstAvg, (lv_32fc_t*)&buf2[COLORBURST_START], COLORBURST_LEN);
// float burstAmp = burstAvg.amplitude();
// if (burstAmp*(1.0f/(float)COLORBURST_LEN) < 0.02f) {
// printf("%d\n", _this->line);
// }
// burstAvg *= (1.0f / (burstAmp*burstAmp));
// burstAvg = burstAvg.conj();
// // Normalize the chroma data (TODO: Optimise by running only where needed)
// volk_32fc_s32fc_multiply_32fc((lv_32fc_t*)&buf2[COLORBURST_START], (lv_32fc_t*)&buf2[COLORBURST_START], *((lv_32fc_t*)&burstAvg), count - COLORBURST_START);
// // Compute the frequency error of the burst
// float phase = buf2[COLORBURST_START].phase();
// float error = 0.0f;
// for (int i = COLORBURST_START+1; i < COLORBURST_START+COLORBURST_LEN; i++) {
// float cphase = buf2[i].phase();
// error += dsp::math::normalizePhase(cphase - phase);
// phase = cphase;
// }
// error *= (1.0f / (float)(COLORBURST_LEN-1));
// // Update the subcarrier freq
// _this->subcarrierFreq += error*0.0001f;
// }
// Render the line if it's visible
if (_this->ypos >= 34 && _this->ypos <= 34+576-1) {
uint32_t* currentLine = &((uint32_t *)_this->img.buffer)[(_this->ypos - 34)*768];
if (_this->colorMode) {
// for (int i = 155; i < (155+768); i++) {
// int imval1 = std::clamp<float>(fabsf(buf2[i-155+COLORBURST_START].re*5.0f) * 255.0f, 0, 255);
// int imval2 = std::clamp<float>(fabsf(buf2[i-155+COLORBURST_START].im*5.0f) * 255.0f, 0, 255);
// currentLine[i-155] = 0xFF000000 | (imval2 << 8) | imval1;
// }
} }
else {
for (int i = 155; i < (155+768); i++) {
int imval = std::clamp<float>(data[i] * 255.0f, 0, 255);
currentLine[i-155] = 0xFF000000 | (imval << 16) | (imval << 8) | imval;
}
}
}
// 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->ypos = 0;
_this->line = 0;
// Swap the video buffer
_this->img.swap(); _this->img.swap();
} }
else {
_this->ypos += 2; // Measure vsync levels
_this->line++; 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; std::string name;
bool enabled = true; bool enabled = true;
VFOManager::VFO *vfo = NULL; VFOManager::VFO *vfo = NULL;
// dsp::demod::Quadrature demod; dsp::demod::Quadrature demod;
dsp::loop::FastAGC<dsp::complex_t> agc;
dsp::demod::Amplitude demod;
//dsp::demod::AM<float> demod;
LineSync sync; LineSync sync;
dsp::sink::Handler<float> sink; dsp::sink::Handler<float> sink;
dsp::convert::RealToComplex r2c; dsp::convert::RealToComplex r2c;
dsp::tap<dsp::complex_t> chromaTaps;
dsp::filter::FIR<dsp::complex_t, dsp::complex_t> fir;
dsp::loop::ChromaPLL pll;
int ypos = 0;
bool 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; ImGui::ImageDisplay img;
}; };

View File

@@ -5,139 +5,139 @@
#include "dab_phase_sym.h" #include "dab_phase_sym.h"
namespace dab { namespace dab {
class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> { // class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>; // using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
public: // public:
CyclicSync() {} // CyclicSync() {}
// TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves // // 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); } // 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) { // 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 // // Computer the number of samples for the symbol and its cyclic prefix
symbolSamps = round(samplerate * symbolLength); // symbolSamps = round(samplerate * symbolLength);
prefixSamps = round(samplerate * cyclicPrefixLength); // prefixSamps = round(samplerate * cyclicPrefixLength);
// Allocate and clear the delay buffer // // Allocate and clear the delay buffer
delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000); // delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
dsp::buffer::clear(delayBuf, symbolSamps); // dsp::buffer::clear(delayBuf, symbolSamps);
// Allocate and clear the history buffer // // Allocate and clear the history buffer
histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps); // histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
dsp::buffer::clear(histBuf, prefixSamps); // dsp::buffer::clear(histBuf, prefixSamps);
// Compute the delay input addresses // // Compute the delay input addresses
delayBufInput = &delayBuf[symbolSamps]; // delayBufInput = &delayBuf[symbolSamps];
// Compute the correlation AGC configuration // // Compute the correlation AGC configuration
this->agcRate = agcRate; // this->agcRate = agcRate;
agcRateInv = 1.0f - agcRate; // agcRateInv = 1.0f - agcRate;
base_type::init(in); // base_type::init(in);
} // }
void reset() { // void reset() {
assert(base_type::_block_init); // assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); // std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop(); // base_type::tempStop();
base_type::tempStart(); // base_type::tempStart();
} // }
int run() { // int run() {
int count = base_type::_in->read(); // int count = base_type::_in->read();
if (count < 0) { return -1; } // if (count < 0) { return -1; }
// Copy the data into the normal delay buffer // // Copy the data into the normal delay buffer
memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t)); // memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
// Flush the input stream // // Flush the input stream
base_type::_in->flush(); // base_type::_in->flush();
// Do cross-correlation // // Do cross-correlation
for (int i = 0; i < count; i++) { // for (int i = 0; i < count; i++) {
// Get the current history slot // // Get the current history slot
dsp::complex_t* slot = &histBuf[histId++]; // dsp::complex_t* slot = &histBuf[histId++];
// Wrap around the history slot index (TODO: Check that the history buffer's length is correct) // // Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
histId %= prefixSamps; // histId %= prefixSamps;
// Kick out last value from the correlation // // Kick out last value from the correlation
corr -= *slot; // corr -= *slot;
// Save input value and compute the new prodct // // Save input value and compute the new prodct
dsp::complex_t val = delayBuf[i]; // dsp::complex_t val = delayBuf[i];
dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps]; // dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
// Add the new value to the correlation // // Add the new value to the correlation
*slot = prod; // *slot = prod;
// Add the new value to the history buffer // // Add the new value to the history buffer
corr += prod; // corr += prod;
// Compute sample amplitude // // Compute sample amplitude
float rcorr = corr.amplitude(); // float rcorr = corr.amplitude();
// If a high enough peak is reached, reset the symbol counter // // 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 // if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
peakCorr = rcorr; // peakCorr = rcorr;
peakLCorr = lastCorr; // peakLCorr = lastCorr;
samplesSincePeak = 0; // samplesSincePeak = 0;
} // }
// If this is the sample right after the peak, save it // // If this is the sample right after the peak, save it
if (samplesSincePeak == 1) { // if (samplesSincePeak == 1) {
peakRCorr = rcorr; // peakRCorr = rcorr;
} // }
// Write the sample to the output // // Write the sample to the output
out.writeBuf[samplesSincePeak++] = val; // out.writeBuf[samplesSincePeak++] = val;
// If the end of the symbol is reached, send it off // // If the end of the symbol is reached, send it off
if (samplesSincePeak >= symbolSamps) { // if (samplesSincePeak >= symbolSamps) {
if (!out.swap(symbolSamps)) { // if (!out.swap(symbolSamps)) {
return -1; // return -1;
} // }
samplesSincePeak = 0; // samplesSincePeak = 0;
peakCorr = 0; // peakCorr = 0;
} // }
// Update the average correlation // // Update the average correlation
lastCorr = rcorr; // lastCorr = rcorr;
// Update the average correlation value // // Update the average correlation value
avgCorr = agcRate*rcorr + agcRateInv*avgCorr; // avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
} // }
// Move unused data // // Move unused data
memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t)); // memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
return count; // return count;
} // }
protected: // protected:
int symbolSamps; // int symbolSamps;
int prefixSamps; // int prefixSamps;
int histId = 0; // int histId = 0;
dsp::complex_t* histBuf; // dsp::complex_t* histBuf;
dsp::complex_t* delayBuf; // dsp::complex_t* delayBuf;
dsp::complex_t* delayBufInput; // dsp::complex_t* delayBufInput;
dsp::complex_t corr = { 0.0f, 0.0f }; // dsp::complex_t corr = { 0.0f, 0.0f };
int samplesSincePeak = 0; // int samplesSincePeak = 0;
float lastCorr = 0.0f; // float lastCorr = 0.0f;
float peakCorr = 0.0f; // float peakCorr = 0.0f;
float peakLCorr = 0.0f; // float peakLCorr = 0.0f;
float peakRCorr = 0.0f; // float peakRCorr = 0.0f;
// Note only required for DAB // // Note only required for DAB
float avgCorr = 0.0f; // float avgCorr = 0.0f;
float agcRate; // float agcRate;
float agcRateInv; // float agcRateInv;
}; // };
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> { class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>; using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;

View File

@@ -14,6 +14,7 @@
#include <chrono> #include <chrono>
#include "dab_dsp.h" #include "dab_dsp.h"
#include <gui/widgets/constellation_diagram.h> #include <gui/widgets/constellation_diagram.h>
#include "ofdm.h"
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
@@ -35,7 +36,7 @@ public:
M17DecoderModule(std::string name) { M17DecoderModule(std::string name) {
this->name = name; this->name = name;
file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary); file = std::ofstream("sync5.f32", std::ios::out | std::ios::binary);
// Load config // Load config
config.acquire(); config.acquire();
@@ -47,7 +48,7 @@ public:
vfo->setSnapInterval(250); vfo->setSnapInterval(250);
// Initialize DSP here // Initialize DSP here
csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE); csync.init(vfo->output, 2048, 504, 1e-3, INPUT_SAMPLE_RATE, 1e-6, 0.01, 0.005);
ffsync.init(&csync.out); ffsync.init(&csync.out);
ns.init(&ffsync.out, handler, this); ns.init(&ffsync.out, handler, this);
@@ -131,8 +132,9 @@ private:
std::string name; std::string name;
bool enabled = true; bool enabled = true;
dab::CyclicSync csync; //dab::CyclicSync csync;
dab::FrameFreqSync ffsync; dab::FrameFreqSync ffsync;
dsp::ofdm::CyclicTimeSync csync;
dsp::sink::Handler<dsp::complex_t> ns; dsp::sink::Handler<dsp::complex_t> ns;
ImGui::ConstellationDiagram constDiagram; ImGui::ConstellationDiagram constDiagram;

View File

@@ -0,0 +1,324 @@
#pragma once
#include <dsp/processor.h>
#include <dsp/loop/phase_control_loop.h>
#include <dsp/taps/windowed_sinc.h>
#include <dsp/multirate/polyphase_bank.h>
#include <dsp/math/step.h>
namespace dsp::ofdm {
class CyclicTimeSync : public Processor<complex_t, complex_t> {
using base_type = Processor<complex_t, complex_t> ;
public:
CyclicTimeSync() {}
CyclicTimeSync(stream<complex_t>* in, int fftSize, int cpSize, double usefulSymbolTime, double samplerate,
double omegaGain, double muGain, double omegaRelLimit, int interpPhaseCount = 128, int interpTapCount = 8) {
init(in, fftSize, cpSize, usefulSymbolTime, samplerate, omegaGain, muGain, omegaRelLimit, interpPhaseCount, interpTapCount);
}
~CyclicTimeSync() {
if (!base_type::_block_init) { return; }
base_type::stop();
dsp::multirate::freePolyphaseBank(interpBank);
buffer::free(corrSampCache);
buffer::free(corrProdCache);
buffer::free(buffer);
}
void init(stream<complex_t>* in, int fftSize, int cpSize, double usefulSymbolTime, double samplerate,
double omegaGain, double muGain, double omegaRelLimit, int interpPhaseCount = 128, int interpTapCount = 8) {
// Save parameters
this->fftSize = fftSize;
this->cpSize = cpSize;
period = fftSize + cpSize;
// Compute the interpolator settings
omega = (usefulSymbolTime * samplerate) / (double)fftSize;
this->omegaGain = omegaGain;
this->muGain = muGain;
this->omegaRelLimit = omegaRelLimit;
this->interpPhaseCount = interpPhaseCount;
this->interpTapCount = interpTapCount;
// Compute the correlator AGC settings
// TODO: Compute it using he FFT and CP sizes
this->corrAgcRate = 1e-4;
corrAgcInvRate = 1.0f - corrAgcRate;
// Initialize the control loop
pcl.init(muGain, omegaGain, 0.0, 0.0, 1.0, omega, omega * (1.0 - omegaRelLimit), omega * (1.0 + omegaRelLimit));
// Generate the interpolation taps
generateInterpTaps();
// Allocate the buffers
corrSampCache = buffer::alloc<complex_t>(fftSize);
corrProdCache = buffer::alloc<complex_t>(cpSize);
buffer = buffer::alloc<complex_t>(STREAM_BUFFER_SIZE + interpTapCount);
bufStart = &buffer[interpTapCount - 1];
// Clear the buffers
buffer::clear(corrSampCache, fftSize);
buffer::clear(corrProdCache, cpSize);
buffer::clear(buffer, interpTapCount - 1);
base_type::init(in);
}
void setOmegaGain(double omegaGain) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
this->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);
this->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);
this->omegaRelLimit = omegaRelLimit;
pcl.setFreqLimits(omega * (1.0 - omegaRelLimit), omega * (1.0 + omegaRelLimit));
}
void setInterpParams(int interpPhaseCount, int interpTapCount) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
this->interpPhaseCount = interpPhaseCount;
this->interpTapCount = interpTapCount;
dsp::multirate::freePolyphaseBank(interpBank);
buffer::free(buffer);
generateInterpTaps();
buffer = buffer::alloc<complex_t>(STREAM_BUFFER_SIZE + interpTapCount);
bufStart = &buffer[interpTapCount - 1];
base_type::tempStart();
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
offset = 0;
pcl.phase = 0.0f;
pcl.freq = omega;
// TODO: The rest
base_type::tempStart();
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
// Copy data to work buffer
memcpy(bufStart, base_type::_in->readBuf, count * sizeof(complex_t));
// Process all samples
while (offset < count) {
// Get the cache slots
complex_t* sampSlot = &corrSampCache[corrSampCacheId++];
complex_t* prodSlot = &corrProdCache[corrProdCacheId++];
corrSampCacheId %= fftSize;
corrProdCacheId %= cpSize;
// Compute the interpolated sample
complex_t sample;
int phase = std::clamp<int>(floorf(pcl.phase * (float)interpPhaseCount), 0, interpPhaseCount - 1);
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&sample, (lv_32fc_t*)&buffer[offset], interpBank.phases[phase], interpTapCount);
// Write the sample to the output
if (outCount >= cpSize) {
out.writeBuf[outCount - cpSize] = sample;
}
// Send out a symbol when it's fully received
if ((++outCount) >= fftSize+cpSize) {
if (!out.swap(outCount)) { break; }
outCount = 0;
}
// Run autocorrelation
complex_t prod = sample.conj()*(*sampSlot);
corr += prod;
corr -= *prodSlot;
// Write back the new sample and product value to the cache
*sampSlot = sample;
*prodSlot = prod;
// Compute the correlation level
float corrLvl = corr.amplitude();
// Detect peak in autocorrelation (TODO: level check maybe not needed now that corrPeak is reset to corrLvl)
if (corrLvl > corrAvg && corrLvl > corrPeak) {
// Save the current correlation as the peak
corrPeak = corrLvl;
// Save the value of the previous correlation as the left side of the peak
corrPeakL = corrLast;
// Reset the peak distance counter
sincePeak = 0;
}
// The first sample after a peak is the right-side sample
if (sincePeak == 1) {
corrPeakR = corrLvl;
}
else if (sincePeak == cpSize) {
// Start the useful symbol counter
sinceCp = 0;
// Compute the fractional error (TODO: Probably very inaccurate with noise, use real slopes instead)
if (corrPeakL > corrPeakR) {
float maxSlope = corrPeakR - corrPeak;
float slope = corrPeak - corrPeakL;
fracErr = std::clamp<float>(0.5f * (1.0f + slope / maxSlope), -0.5f, 0.5f);
}
else {
float maxSlope = corrPeak - corrPeakL;
float slope = corrPeakR - corrPeak;
fracErr = std::clamp<float>(-0.5f * (1.0f + slope / maxSlope), -0.5f, 0.5f);
}
}
else if (sincePeak == fftSize) {
// Reset the peak detector
corrPeak = corrAvg;
}
// NOTE: THIS IS ONLY NEEDED FOR DAB
// Detect a wider-than-normal distance to adapt the output counter
else if (sincePeak == 2656) {
// Reset the output counter
outCount = 50;
}
// Last sample of useful symbol
if (sinceCp == fftSize) {
// If the fractional error is valid, run closed-loop
float err = 0.0f;
if (!std::isnan(fracErr)) {
// Compute the measured period using the distance to the last symbol
float measuredPeriod = (float)sinceLastSym - fracErr;
// NOTE: THIS IS ONLY NEEDED FOR DAB
if (measuredPeriod > 3828.0f) {
// Null symbol
err = measuredPeriod - (2552.0f+2656.0f);
}
else {
// Regular symbol
err = measuredPeriod - period;
}
err = std::clamp<float>(err, -10.0f, 10.0f);
// Run the control loop in closed-loop mode
pcl.advance(err);
}
else {
// Otherwise, run open-loop
pcl.advancePhase();
}
// printf("%d\n", outCount);
// Nudge the symbol window if it's too out of sync
if (outCount > 100) {
// TODO: MOVE THE LAST SAMPLES OR THE SYMBOL WILL BE CORRUPTED!
outCount = 50;
flog::debug("NUDGE!");
}
// Reset the period counter
sinceLastSym = 0;
}
else {
// Run the control loop in open-loop mode
pcl.advancePhase();
}
// Update the offset and phase
float delta = floorf(pcl.phase);
offset += delta;
pcl.phase -= delta;
// Update the last correlation level
corrLast = corrLvl;
// Update correlation AGC
corrAvg = corrAvg*corrAgcInvRate + corrLvl*corrAgcRate;
// Increment the distance counters (TODO: Check if they happen at the right point, eg. after being reset to zero)
sincePeak++;
sinceLastSym++;
sinceCp++;
}
// Prepare offset for next buffer of samples
offset -= count;
// Update delay buffer
memmove(buffer, &buffer[count], (interpTapCount - 1) * sizeof(complex_t));
// Swap if some data was generated
base_type::_in->flush();
return count;
}
protected:
void generateInterpTaps() {
double bw = 0.5 / (double)interpPhaseCount;
dsp::tap<float> lp = dsp::taps::windowedSinc<float>(interpPhaseCount * interpTapCount, dsp::math::hzToRads(bw, 1.0), dsp::window::nuttall, interpPhaseCount);
interpBank = dsp::multirate::buildPolyphaseBank<float>(interpPhaseCount, lp);
taps::free(lp);
}
// OFDM Configuration
int fftSize;
int cpSize;
float period;
// Interpolator
dsp::multirate::PolyphaseBank<float> interpBank;
int interpPhaseCount;
int interpTapCount;
int offset = 0;
complex_t* buffer = NULL;
complex_t* bufStart;
// Control loop
loop::PhaseControlLoop<float, false> pcl;
double omega;
double omegaGain;
double muGain;
double omegaRelLimit;
float fracErr = 0.0f;
// Autocorrelator
complex_t corr = {0.0f, 0.0f};
complex_t* corrSampCache = NULL;
complex_t* corrProdCache = NULL;
int corrSampCacheId = 0;
int corrProdCacheId = 0;
float corrAgcRate;
float corrAgcInvRate;
float corrAvg = 0;
float corrLast = 0;
float corrPeakR = 0;
float corrPeak = 0;
float corrPeakL = 0;
// Peak detection
int sincePeak = 0;
int sinceLastSym = 0;
int sinceCp = 0;
// Other shit to categorize
int outCount = 0;
};
};

View File

@@ -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

View File

@@ -0,0 +1,31 @@
cyclicLen = 4
usefulLen = 12
A = 0*12 + 1*13 + 2*14 + 3*15
B = 1*13 + 2*14 + 3*15 + 4*16 = A - 0*12 + 4*16
C = 2*14 + 3*15 + 4*16 + 5*17 = B - 1*13 + 5*17
D = 3*15 + 4*16 + 5*17 + 6*18 = C - 2*14 + 6*18
E = 4*16 + 5*17 + 6*18 + 7*19 = D - 3*15 + 7*19
F = 5*17 + 6*18 + 7*19 + 8*20 = E - 4*16 + 8*20
G = 6*18 + 7*19 + 8*20 + 9*21 = F - 5*17 + 9*21
H = 7*19 + 8*20 + 9*21 + 10*22 = G - 6*18 + 10*22
I = 8*20 + 9*21 + 10*22 + 11*23 = H - 7*19 + 11*23
J = 9*21 + 10*22 + 11*23 + 12*24 = I - 8*20 + 12*24
K = 10*22 + 11*23 + 12*24 + 13*25 = J - 9*21 + 13*25
L = 11*23 + 12*24 + 13*25 + 14*26 = K - 10*22 + 14*26
M = 12*24 + 13*25 + 14*26 + 15*27 = L - 11*23 + 15*27
N = 13*25 + 14*26 + 15*27 + 16*28 = M - 12*24 + 16*28
O = 14*26 + 15*27 + 16*28 + 17*29 = N - 13*25 + 17*29
P = 15*27 + 16*28 + 17*29 + 18*30 = O - 14*26 + 18*30
Q = 16*28 + 17*29 + 18*30 + 19*31 = P - 15*27 + 19*31
R = 17*29 + 18*30 + 19*31 + 20*32 = Q - 16*28 + 20*32
S = 18*30 + 19*31 + 20*32 + 21*33 = R - 17*29 + 21*33
T = 19*31 + 20*32 + 21*33 + 22*34 = S - 18*30 + 22*34
U = 20*32 + 21*33 + 22*34 + 23*35 = T - 19*31 + 23*35
Conclusion:
sampCacheLen = usefulLen
prodCacheLen = cyclicLen
Peak correlation occurs when the current interpolated value is the last FFT sample

View File

@@ -0,0 +1,397 @@
5209
2553
2551
2551
2552
2551
2553
2555
2549
2552
2552
2554
2550
2553
2553
2550
2553
2552
2550
2552
2548
2558
2551
2546
2559
2551
2552
2552
2551
2554
2555
2549
2550
2549
2557
2551
2554
2550
2552
2546
2559
2551
2554
2550
2550
2557
2550
2556
2543
2556
2551
2554
2551
2553
2554
2550
2551
2552
2552
2553
2550
2553
2546
2558
2552
2552
2552
2550
2555
2551
2551
2554
2553
2553
2549
2552
5208
2554
2550
2552
2551
2553
2551
2553
2551
2553
2550
2554
2553
2551
2553
2552
2546
2557
2551
2553
2552
2551
2553
2551
2548
2557
2553
2551
2562
2539
2559
2548
2551
2551
2550
2556
2551
2554
2550
2552
2552
2548
2556
2550
2554
2553
2553
2550
2552
2552
2550
2555
2558
2545
2552
2553
2548
2555
2552
2552
2557
2550
2548
2553
2554
2550
2552
2547
2558
2551
2552
2552
2552
2555
2549
2552
5208
2552
2552
2552
2550
2555
2550
2553
2553
2551
2552
2547
2557
2552
2552
2552
2552
2552
2552
2552
2546
2558
2551
2552
2553
2551
2553
2553
2552
2551
2552
2552
2551
2553
2552
2552
2552
2552
2552
2552
2556
2548
2551
2555
2550
2552
2555
2549
2552
2552
2550
2553
2551
2554
2552
2551
2553
2553
2551
2548
2550
2555
2554
2553
2556
2547
2552
2554
2552
2550
2552
2552
2553
2552
2551
2552
5209
2553
2551
2551
2547
2558
2550
2553
2552
2553
2552
2549
2551
2557
2549
2554
2551
2554
2550
2553
2553
2551
2551
2548
2559
2549
2552
2552
2552
2551
2551
2550
2557
2551
2552
2555
2549
2557
2549
2550
2553
2549
2554
2553
2553
2550
2552
2553
2550
2553
2553
2551
2547
2557
2552
2552
2551
2553
2549
2555
2556
2550
2550
2553
2551
2552
2553
2551
2552
2552
2552
2553
2551
2541
2563
2552
5210
2550
2551
2552
2553
2553
2551
2552
2552
2552
2552
2558
2547
2551
2552
2552
2552
2552
2550
2554
2551
2553
2552
2551
2553
2551
2553
2553
2550
2551
2555
2550
2552
2552
2553
2553
2550
2552
2553
2550
2553
2558
2546
2553
2554
2550
2552
2553
2548
2554
2552
2552
2552
2551
2554
2550
2553
2553
2550
2551
2552
2554
2553
2551
2551
2555
2550
2553
2552
2550
2554
2550
2553
2553
2553
2550
5210
2553
2549
2553
2551
2552
2552
2556
2548
2549
2555
2551
2557
2548
2552
2552
2552

View File

@@ -20,16 +20,6 @@ enum IFNRPreset {
IFNR_PRESET_BROADCAST IFNR_PRESET_BROADCAST
}; };
enum SquelchMode {
SQUELCH_MODE_OFF,
SQUELCH_MODE_POWER,
SQUELCH_MODE_SNR,
SQUELCH_MODE_CTCSS_MUTE,
SQUELCH_MODE_CTCSS_DECODE,
SQUELCH_MODE_DCS_MUTE,
SQUELCH_MODE_DCS_DECODE,
};
namespace demod { namespace demod {
class Demodulator { class Demodulator {
public: public:
@@ -55,8 +45,6 @@ namespace demod {
virtual int getDefaultDeemphasisMode() = 0; virtual int getDefaultDeemphasisMode() = 0;
virtual bool getFMIFNRAllowed() = 0; virtual bool getFMIFNRAllowed() = 0;
virtual bool getNBAllowed() = 0; virtual bool getNBAllowed() = 0;
virtual bool getHighPassAllowed() = 0;
virtual bool getSquelchAllowed() = 0;
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0; virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
}; };
} }

View File

@@ -40,12 +40,6 @@ namespace demod {
void showMenu() { void showMenu() {
float menuWidth = ImGui::GetContentRegionAvail().x; float menuWidth = ImGui::GetContentRegionAvail().x;
if (ImGui::Checkbox(("Carrier AGC##_radio_am_carrier_agc_" + name).c_str(), &carrierAgc)) {
demod.setAGCMode(carrierAgc ? dsp::demod::AM<dsp::stereo_t>::AGCMode::CARRIER : dsp::demod::AM<dsp::stereo_t>::AGCMode::AUDIO);
_config->acquire();
_config->conf[name][getName()]["carrierAgc"] = carrierAgc;
_config->release(true);
}
ImGui::LeftLabel("AGC Attack"); ImGui::LeftLabel("AGC Attack");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::SliderFloat(("##_radio_am_agc_attack_" + name).c_str(), &agcAttack, 1.0f, 200.0f)) { if (ImGui::SliderFloat(("##_radio_am_agc_attack_" + name).c_str(), &agcAttack, 1.0f, 200.0f)) {
@@ -62,6 +56,12 @@ namespace demod {
_config->conf[name][getName()]["agcDecay"] = agcDecay; _config->conf[name][getName()]["agcDecay"] = agcDecay;
_config->release(true); _config->release(true);
} }
if (ImGui::Checkbox(("Carrier AGC##_radio_am_carrier_agc_" + name).c_str(), &carrierAgc)) {
demod.setAGCMode(carrierAgc ? dsp::demod::AM<dsp::stereo_t>::AGCMode::CARRIER : dsp::demod::AM<dsp::stereo_t>::AGCMode::AUDIO);
_config->acquire();
_config->conf[name][getName()]["carrierAgc"] = carrierAgc;
_config->release(true);
}
} }
void setBandwidth(double bandwidth) { demod.setBandwidth(bandwidth); } void setBandwidth(double bandwidth) { demod.setBandwidth(bandwidth); }
@@ -86,8 +86,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; } bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return false; } bool getNBAllowed() { return false; }
bool getHighPassAllowed() { return true; }
bool getSquelchAllowed() { return true; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private: private:

View File

@@ -92,8 +92,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; } bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return false; } bool getNBAllowed() { return false; }
bool getHighPassAllowed() { return false; }
bool getSquelchAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private: private:

View File

@@ -79,8 +79,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; } bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return true; } bool getNBAllowed() { return true; }
bool getHighPassAllowed() { return true; }
bool getSquelchAllowed() { return true; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private: private:

View File

@@ -79,8 +79,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; } bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return true; } bool getNBAllowed() { return true; }
bool getHighPassAllowed() { return true; }
bool getSquelchAllowed() { return true; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private: private:

View File

@@ -22,11 +22,14 @@ namespace demod {
if (config->conf[name][getName()].contains("lowPass")) { if (config->conf[name][getName()].contains("lowPass")) {
_lowPass = config->conf[name][getName()]["lowPass"]; _lowPass = config->conf[name][getName()]["lowPass"];
} }
if (config->conf[name][getName()].contains("highPass")) {
_highPass = config->conf[name][getName()]["highPass"];
}
_config->release(); _config->release();
// Define structure // Define structure
demod.init(input, getIFSampleRate(), bandwidth, _lowPass); demod.init(input, getIFSampleRate(), bandwidth, _lowPass, _highPass);
} }
void start() { demod.start(); } void start() { demod.start(); }
@@ -40,6 +43,12 @@ namespace demod {
_config->conf[name][getName()]["lowPass"] = _lowPass; _config->conf[name][getName()]["lowPass"] = _lowPass;
_config->release(true); _config->release(true);
} }
if (ImGui::Checkbox(("High Pass##_radio_wfm_highpass_" + name).c_str(), &_highPass)) {
demod.setHighPass(_highPass);
_config->acquire();
_config->conf[name][getName()]["highPass"] = _highPass;
_config->release(true);
}
} }
void setBandwidth(double bandwidth) { void setBandwidth(double bandwidth) {
@@ -66,8 +75,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return true; } bool getFMIFNRAllowed() { return true; }
bool getNBAllowed() { return false; } bool getNBAllowed() { return false; }
bool getHighPassAllowed() { return true; }
bool getSquelchAllowed() { return true; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private: private:
@@ -76,6 +83,7 @@ namespace demod {
ConfigManager* _config = NULL; ConfigManager* _config = NULL;
bool _lowPass = true; bool _lowPass = true;
bool _highPass = false;
std::string name; std::string name;
}; };

View File

@@ -59,8 +59,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; } bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return true; } bool getNBAllowed() { return true; }
bool getHighPassAllowed() { return false; }
bool getSquelchAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; } dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
private: private:

View File

@@ -80,8 +80,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; } int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; } bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return true; } bool getNBAllowed() { return true; }
bool getHighPassAllowed() { return true; }
bool getSquelchAllowed() { return true; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private: private:

View File

@@ -100,18 +100,18 @@ namespace demod {
} }
void showMenu() { void showMenu() {
if (ImGui::Checkbox(("Low Pass##_radio_wfm_lowpass_" + name).c_str(), &_lowPass)) {
demod.setLowPass(_lowPass);
_config->acquire();
_config->conf[name][getName()]["lowPass"] = _lowPass;
_config->release(true);
}
if (ImGui::Checkbox(("Stereo##_radio_wfm_stereo_" + name).c_str(), &_stereo)) { if (ImGui::Checkbox(("Stereo##_radio_wfm_stereo_" + name).c_str(), &_stereo)) {
setStereo(_stereo); setStereo(_stereo);
_config->acquire(); _config->acquire();
_config->conf[name][getName()]["stereo"] = _stereo; _config->conf[name][getName()]["stereo"] = _stereo;
_config->release(true); _config->release(true);
} }
if (ImGui::Checkbox(("Low Pass##_radio_wfm_lowpass_" + name).c_str(), &_lowPass)) {
demod.setLowPass(_lowPass);
_config->acquire();
_config->conf[name][getName()]["lowPass"] = _lowPass;
_config->release(true);
}
if (ImGui::Checkbox(("Decode RDS##_radio_wfm_rds_" + name).c_str(), &_rds)) { if (ImGui::Checkbox(("Decode RDS##_radio_wfm_rds_" + name).c_str(), &_rds)) {
demod.setRDSOut(_rds); demod.setRDSOut(_rds);
_config->acquire(); _config->acquire();
@@ -270,8 +270,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; } int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; }
bool getFMIFNRAllowed() { return true; } bool getFMIFNRAllowed() { return true; }
bool getNBAllowed() { return false; } bool getNBAllowed() { return false; }
bool getHighPassAllowed() { return true; }
bool getSquelchAllowed() { return true; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; } dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
// ============= DEDICATED FUNCTIONS ============= // ============= DEDICATED FUNCTIONS =============

View File

@@ -5,14 +5,10 @@ enum {
RADIO_IFACE_CMD_SET_MODE, RADIO_IFACE_CMD_SET_MODE,
RADIO_IFACE_CMD_GET_BANDWIDTH, RADIO_IFACE_CMD_GET_BANDWIDTH,
RADIO_IFACE_CMD_SET_BANDWIDTH, RADIO_IFACE_CMD_SET_BANDWIDTH,
RADIO_IFACE_CMD_GET_SQUELCH_MODE, RADIO_IFACE_CMD_GET_SQUELCH_ENABLED,
RADIO_IFACE_CMD_SET_SQUELCH_MODE, RADIO_IFACE_CMD_SET_SQUELCH_ENABLED,
RADIO_IFACE_CMD_GET_SQUELCH_LEVEL, RADIO_IFACE_CMD_GET_SQUELCH_LEVEL,
RADIO_IFACE_CMD_SET_SQUELCH_LEVEL, RADIO_IFACE_CMD_SET_SQUELCH_LEVEL,
RADIO_IFACE_CMD_GET_CTCSS_TONE,
RADIO_IFACE_CMD_SET_CTCSS_TONE,
RADIO_IFACE_CMD_GET_HIGHPASS,
RADIO_IFACE_CMD_SET_HIGHPASS
}; };
enum { enum {

View File

@@ -8,8 +8,7 @@
#include <dsp/chain.h> #include <dsp/chain.h>
#include <dsp/noise_reduction/noise_blanker.h> #include <dsp/noise_reduction/noise_blanker.h>
#include <dsp/noise_reduction/fm_if.h> #include <dsp/noise_reduction/fm_if.h>
#include <dsp/noise_reduction/power_squelch.h> #include <dsp/noise_reduction/squelch.h>
#include <dsp/noise_reduction/ctcss_squelch.h>
#include <dsp/multirate/rational_resampler.h> #include <dsp/multirate/rational_resampler.h>
#include <dsp/filter/deephasis.h> #include <dsp/filter/deephasis.h>
#include <core.h> #include <core.h>
@@ -50,22 +49,6 @@ public:
ifnrPresets.define("Voice", IFNR_PRESET_VOICE); ifnrPresets.define("Voice", IFNR_PRESET_VOICE);
ifnrPresets.define("Narrow Band", IFNR_PRESET_NARROW_BAND); ifnrPresets.define("Narrow Band", IFNR_PRESET_NARROW_BAND);
squelchModes.define("off", "Off", SQUELCH_MODE_OFF);
squelchModes.define("power", "Power", SQUELCH_MODE_POWER);
//squelchModes.define("snr", "SNR", SQUELCH_MODE_SNR);
squelchModes.define("ctcss_mute", "CTCSS (Mute)", SQUELCH_MODE_CTCSS_MUTE);
squelchModes.define("ctcss_decode", "CTCSS (Decode Only)", SQUELCH_MODE_CTCSS_DECODE);
//squelchModes.define("dcs_mute", "DCS (Mute)", SQUELCH_MODE_DCS_MUTE);
//squelchModes.define("dcs_decode", "DCS (Decode Only)", SQUELCH_MODE_DCS_DECODE);
for (int i = 0; i < dsp::noise_reduction::_CTCSS_TONE_COUNT; i++) {
float tone = dsp::noise_reduction::CTCSS_TONES[i];
char buf[64];
sprintf(buf, "%.1fHz", tone);
ctcssTones.define((int)round(tone) * 10, buf, (dsp::noise_reduction::CTCSSTone)i);
}
ctcssTones.define(-1, "Any", dsp::noise_reduction::CTCSS_TONE_ANY);
// Initialize the config if it doesn't exist // Initialize the config if it doesn't exist
bool created = false; bool created = false;
config.acquire(); config.acquire();
@@ -89,24 +72,19 @@ public:
nb.init(NULL, 500.0 / 24000.0, 10.0); nb.init(NULL, 500.0 / 24000.0, 10.0);
fmnr.init(NULL, 32); fmnr.init(NULL, 32);
powerSquelch.init(NULL, MIN_SQUELCH); squelch.init(NULL, MIN_SQUELCH);
ifChain.addBlock(&nb, false); ifChain.addBlock(&nb, false);
ifChain.addBlock(&powerSquelch, false); ifChain.addBlock(&squelch, false);
ifChain.addBlock(&fmnr, false); ifChain.addBlock(&fmnr, false);
// Initialize audio DSP chain // Initialize audio DSP chain
afChain.init(&dummyAudioStream); afChain.init(&dummyAudioStream);
ctcss.init(NULL, 50000.0);
resamp.init(NULL, 250000.0, 48000.0); resamp.init(NULL, 250000.0, 48000.0);
hpTaps = dsp::taps::highPass(300.0, 100.0, 48000.0);
hpf.init(NULL, hpTaps);
deemp.init(NULL, 50e-6, 48000.0); deemp.init(NULL, 50e-6, 48000.0);
afChain.addBlock(&ctcss, false);
afChain.addBlock(&resamp, true); afChain.addBlock(&resamp, true);
afChain.addBlock(&hpf, false);
afChain.addBlock(&deemp, false); afChain.addBlock(&deemp, false);
// Initialize the sink // Initialize the sink
@@ -255,33 +233,6 @@ private:
} }
} }
// Squelch
if (_this->squelchAllowed) {
ImGui::LeftLabel("Squelch Mode");
ImGui::FillWidth();
if (ImGui::Combo(("##_radio_sqelch_mode_" + _this->name).c_str(), &_this->squelchModeId, _this->squelchModes.txt)) {
_this->setSquelchMode(_this->squelchModes[_this->squelchModeId]);
}
switch (_this->squelchModes[_this->squelchModeId]) {
case SQUELCH_MODE_POWER:
ImGui::LeftLabel("Squelch Level");
ImGui::FillWidth();
if (ImGui::SliderFloat(("##_radio_sqelch_lvl_" + _this->name).c_str(), &_this->squelchLevel, _this->MIN_SQUELCH, _this->MAX_SQUELCH, "%.3fdB")) {
_this->setSquelchLevel(_this->squelchLevel);
}
break;
case SQUELCH_MODE_CTCSS_MUTE:
if (_this->squelchModes[_this->squelchModeId] == SQUELCH_MODE_CTCSS_MUTE) {
ImGui::LeftLabel("CTCSS Tone");
ImGui::FillWidth();
if (ImGui::Combo(("##_radio_ctcss_tone_" + _this->name).c_str(), &_this->ctcssToneId, _this->ctcssTones.txt)) {
_this->setCTCSSTone(_this->ctcssTones[_this->ctcssToneId]);
}
}
}
}
// Noise blanker // Noise blanker
if (_this->nbAllowed) { if (_this->nbAllowed) {
if (ImGui::Checkbox(("Noise blanker (W.I.P.)##_radio_nb_ena_" + _this->name).c_str(), &_this->nbEnabled)) { if (ImGui::Checkbox(("Noise blanker (W.I.P.)##_radio_nb_ena_" + _this->name).c_str(), &_this->nbEnabled)) {
@@ -295,6 +246,19 @@ private:
} }
if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); } if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); }
} }
// Squelch
if (ImGui::Checkbox(("Squelch##_radio_sqelch_ena_" + _this->name).c_str(), &_this->squelchEnabled)) {
_this->setSquelchEnabled(_this->squelchEnabled);
}
if (!_this->squelchEnabled && _this->enabled) { style::beginDisabled(); }
ImGui::SameLine();
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::SliderFloat(("##_radio_sqelch_lvl_" + _this->name).c_str(), &_this->squelchLevel, _this->MIN_SQUELCH, _this->MAX_SQUELCH, "%.3fdB")) {
_this->setSquelchLevel(_this->squelchLevel);
}
if (!_this->squelchEnabled && _this->enabled) { style::endDisabled(); }
// FM IF Noise Reduction // FM IF Noise Reduction
if (_this->FMIFNRAllowed) { if (_this->FMIFNRAllowed) {
@@ -312,53 +276,9 @@ private:
} }
} }
// High pass
if (_this->highPassAllowed) {
if (ImGui::Checkbox(("High Pass##_radio_hpf_" + _this->name).c_str(), &_this->highPass)) {
_this->setHighPass(_this->highPass);
}
}
// Demodulator specific menu // Demodulator specific menu
_this->selectedDemod->showMenu(); _this->selectedDemod->showMenu();
// Display the squelch diagnostics
switch (_this->squelchModes[_this->squelchModeId]) {
case SQUELCH_MODE_CTCSS_MUTE:
ImGui::TextUnformatted("Received Tone:");
ImGui::SameLine();
{
auto ctone = _this->ctcss.getCurrentTone();
auto dtone = _this->ctcssTones[_this->ctcssToneId];
if (ctone != dsp::noise_reduction::CTCSS_TONE_NONE) {
if (dtone == dsp::noise_reduction::CTCSS_TONE_ANY || ctone == dtone) {
ImGui::TextColored(ImVec4(0, 1, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
}
else {
ImGui::TextColored(ImVec4(1, 0, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
}
}
else {
ImGui::TextUnformatted("None");
}
}
break;
case SQUELCH_MODE_CTCSS_DECODE:
ImGui::TextUnformatted("Received Tone:");
ImGui::SameLine();
{
auto ctone = _this->ctcss.getCurrentTone();
if (ctone != dsp::noise_reduction::CTCSS_TONE_NONE) {
ImGui::TextColored(ImVec4(0, 1, 0, 1), "%.1fHz", dsp::noise_reduction::CTCSS_TONES[_this->ctcss.getCurrentTone()]);
}
else {
ImGui::TextUnformatted("None");
}
}
break;
}
if (!_this->enabled) { style::endDisabled(); } if (!_this->enabled) { style::endDisabled(); }
} }
@@ -367,13 +287,13 @@ private:
switch (id) { switch (id) {
case DemodID::RADIO_DEMOD_NFM: demod = new demod::NFM(); break; case DemodID::RADIO_DEMOD_NFM: demod = new demod::NFM(); break;
case DemodID::RADIO_DEMOD_WFM: demod = new demod::WFM(); break; case DemodID::RADIO_DEMOD_WFM: demod = new demod::WFM(); break;
case DemodID::RADIO_DEMOD_AM: demod = new demod::AM(); break; case DemodID::RADIO_DEMOD_AM: demod = new demod::AM(); break;
case DemodID::RADIO_DEMOD_DSB: demod = new demod::DSB(); break; case DemodID::RADIO_DEMOD_DSB: demod = new demod::DSB(); break;
case DemodID::RADIO_DEMOD_USB: demod = new demod::USB(); break; case DemodID::RADIO_DEMOD_USB: demod = new demod::USB(); break;
case DemodID::RADIO_DEMOD_CW: demod = new demod::CW(); break; case DemodID::RADIO_DEMOD_CW: demod = new demod::CW(); break;
case DemodID::RADIO_DEMOD_LSB: demod = new demod::LSB(); break; case DemodID::RADIO_DEMOD_LSB: demod = new demod::LSB(); break;
case DemodID::RADIO_DEMOD_RAW: demod = new demod::RAW(); break; case DemodID::RADIO_DEMOD_RAW: demod = new demod::RAW(); break;
default: demod = NULL; break; default: demod = NULL; break;
} }
if (!demod) { return NULL; } if (!demod) { return NULL; }
@@ -440,20 +360,15 @@ private:
maxBandwidth = selectedDemod->getMaxBandwidth(); maxBandwidth = selectedDemod->getMaxBandwidth();
bandwidthLocked = selectedDemod->getBandwidthLocked(); bandwidthLocked = selectedDemod->getBandwidthLocked();
snapInterval = selectedDemod->getDefaultSnapInterval(); snapInterval = selectedDemod->getDefaultSnapInterval();
squelchLevel = MIN_SQUELCH;
deempAllowed = selectedDemod->getDeempAllowed(); deempAllowed = selectedDemod->getDeempAllowed();
deempId = deempModes.valueId((DeemphasisMode)selectedDemod->getDefaultDeemphasisMode()); deempId = deempModes.valueId((DeemphasisMode)selectedDemod->getDefaultDeemphasisMode());
squelchModeId = squelchModes.valueId(SQUELCH_MODE_OFF); squelchEnabled = false;
squelchLevel = MIN_SQUELCH;
ctcssToneId = ctcssTones.valueId(dsp::noise_reduction::CTCSS_TONE_67Hz);
highPass = false;
postProcEnabled = selectedDemod->getPostProcEnabled(); postProcEnabled = selectedDemod->getPostProcEnabled();
FMIFNRAllowed = selectedDemod->getFMIFNRAllowed(); FMIFNRAllowed = selectedDemod->getFMIFNRAllowed();
FMIFNREnabled = false; FMIFNREnabled = false;
fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE); fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE);
nbAllowed = selectedDemod->getNBAllowed(); nbAllowed = selectedDemod->getNBAllowed();
squelchAllowed = selectedDemod->getSquelchAllowed();
highPassAllowed = selectedDemod->getHighPassAllowed();
nbEnabled = false; nbEnabled = false;
nbLevel = 0.0f; nbLevel = 0.0f;
double ifSamplerate = selectedDemod->getIFSampleRate(); double ifSamplerate = selectedDemod->getIFSampleRate();
@@ -465,24 +380,11 @@ private:
if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) { if (config.conf[name][selectedDemod->getName()].contains("snapInterval")) {
snapInterval = config.conf[name][selectedDemod->getName()]["snapInterval"]; snapInterval = config.conf[name][selectedDemod->getName()]["snapInterval"];
} }
if (config.conf[name][selectedDemod->getName()].contains("squelchMode")) {
std::string squelchModeStr = config.conf[name][selectedDemod->getName()]["squelchMode"];
if (squelchModes.keyExists(squelchModeStr)) {
squelchModeId = squelchModes.keyId(squelchModeStr);
}
}
if (config.conf[name][selectedDemod->getName()].contains("squelchLevel")) { if (config.conf[name][selectedDemod->getName()].contains("squelchLevel")) {
squelchLevel = config.conf[name][selectedDemod->getName()]["squelchLevel"]; squelchLevel = config.conf[name][selectedDemod->getName()]["squelchLevel"];
} }
if (config.conf[name][selectedDemod->getName()].contains("ctcssTone")) { if (config.conf[name][selectedDemod->getName()].contains("squelchEnabled")) {
int ctcssToneX10 = config.conf[name][selectedDemod->getName()]["ctcssTone"]; squelchEnabled = config.conf[name][selectedDemod->getName()]["squelchEnabled"];
if (ctcssTones.keyExists(ctcssToneX10)) {
ctcssToneId = ctcssTones.keyId(ctcssToneX10);
}
}
if (config.conf[name][selectedDemod->getName()].contains("highPass")) {
highPass = config.conf[name][selectedDemod->getName()]["highPass"];
} }
if (config.conf[name][selectedDemod->getName()].contains("deempMode")) { if (config.conf[name][selectedDemod->getName()].contains("deempMode")) {
if (!config.conf[name][selectedDemod->getName()]["deempMode"].is_string()) { if (!config.conf[name][selectedDemod->getName()]["deempMode"].is_string()) {
@@ -532,22 +434,16 @@ private:
setFMIFNREnabled(FMIFNRAllowed ? FMIFNREnabled : false); setFMIFNREnabled(FMIFNRAllowed ? FMIFNREnabled : false);
// Configure squelch // Configure squelch
setSquelchMode(squelchAllowed ? squelchModes[squelchModeId] : SQUELCH_MODE_OFF);
setSquelchLevel(squelchLevel); setSquelchLevel(squelchLevel);
setCTCSSTone(ctcssTones[ctcssToneId]); setSquelchEnabled(squelchEnabled);
// Configure AF chain // Configure AF chain
if (postProcEnabled) { if (postProcEnabled) {
// Configure resampler // Configure resampler
afChain.stop(); afChain.stop();
double afsr = selectedDemod->getAFSampleRate(); resamp.setInSamplerate(selectedDemod->getAFSampleRate());
ctcss.setSamplerate(afsr);
resamp.setInSamplerate(afsr);
afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
setAudioSampleRate(audioSampleRate); setAudioSampleRate(audioSampleRate);
afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
// Configure the HPF
setHighPass(highPass && highPassAllowed);
// Configure deemphasis // Configure deemphasis
setDeemphasisMode(deempModes[deempId]); setDeemphasisMode(deempModes[deempId]);
@@ -593,32 +489,12 @@ private:
// Configure resampler // Configure resampler
resamp.setOutSamplerate(audioSampleRate); resamp.setOutSamplerate(audioSampleRate);
// Configure the HPF sample rate
hpTaps = dsp::taps::highPass(300.0, 100.0, audioSampleRate);
hpf.setTaps(hpTaps);
// Configure deemphasis sample rate // Configure deemphasis sample rate
deemp.setSamplerate(audioSampleRate); deemp.setSamplerate(audioSampleRate);
afChain.start(); afChain.start();
} }
void setHighPass(bool enabled) {
// Update the state
highPass = enabled;
// Check if post-processing is enabled and that a demodulator is selected
if (!postProcEnabled || !selectedDemod) { return; }
// Set the state of the HPF in the AF chain
afChain.setBlockEnabled(&hpf, enabled, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
// Save config
config.acquire();
config.conf[name][selectedDemod->getName()]["highPass"] = enabled;
config.release(true);
}
void setDeemphasisMode(DeemphasisMode mode) { void setDeemphasisMode(DeemphasisMode mode) {
deempId = deempModes.valueId(mode); deempId = deempModes.valueId(mode);
if (!postProcEnabled || !selectedDemod) { return; } if (!postProcEnabled || !selectedDemod) { return; }
@@ -646,7 +522,6 @@ private:
void setNBLevel(float level) { void setNBLevel(float level) {
nbLevel = std::clamp<float>(level, MIN_NB, MAX_NB); nbLevel = std::clamp<float>(level, MIN_NB, MAX_NB);
nb.setLevel(nbLevel); nb.setLevel(nbLevel);
if (!selectedDemod) { return; }
// Save config // Save config
config.acquire(); config.acquire();
@@ -654,59 +529,20 @@ private:
config.release(true); config.release(true);
} }
void setSquelchMode(SquelchMode mode) { void setSquelchEnabled(bool enable) {
squelchModeId = squelchModes.valueId(mode); squelchEnabled = enable;
if (!selectedDemod) { return; } if (!selectedDemod) { return; }
ifChain.setBlockEnabled(&squelch, squelchEnabled, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
// Disable all squelch blocks
ifChain.disableBlock(&powerSquelch, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
afChain.disableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
// Enable the block depending on the mode
switch (mode) {
case SQUELCH_MODE_OFF:
break;
case SQUELCH_MODE_POWER:
// Enable the power squelch block
ifChain.enableBlock(&powerSquelch, [=](dsp::stream<dsp::complex_t>* out){ selectedDemod->setInput(out); });
break;
case SQUELCH_MODE_SNR:
// TODO
break;
case SQUELCH_MODE_CTCSS_MUTE:
// Set the required tone and enable the CTCSS squelch block
ctcss.setRequiredTone(ctcssTones[ctcssToneId]);
afChain.enableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
break;
case SQUELCH_MODE_CTCSS_DECODE:
// Set the required tone to none and enable the CTCSS squelch block
ctcss.setRequiredTone(dsp::noise_reduction::CTCSS_TONE_NONE);
afChain.enableBlock(&ctcss, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
break;
case SQUELCH_MODE_DCS_MUTE:
// TODO
break;
case SQUELCH_MODE_DCS_DECODE:
// TODO
break;
}
// Save config // Save config
config.acquire(); config.acquire();
config.conf[name][selectedDemod->getName()]["squelchMode"] = squelchModes.key(squelchModeId); config.conf[name][selectedDemod->getName()]["squelchEnabled"] = squelchEnabled;
config.release(true); config.release(true);
} }
void setSquelchLevel(float level) { void setSquelchLevel(float level) {
squelchLevel = std::clamp<float>(level, MIN_SQUELCH, MAX_SQUELCH); squelchLevel = std::clamp<float>(level, MIN_SQUELCH, MAX_SQUELCH);
powerSquelch.setLevel(squelchLevel); squelch.setLevel(squelchLevel);
if (!selectedDemod) { return; }
// Save config // Save config
config.acquire(); config.acquire();
@@ -714,24 +550,6 @@ private:
config.release(true); config.release(true);
} }
void setCTCSSTone(dsp::noise_reduction::CTCSSTone tone) {
// Check for an invalid value
if (tone == dsp::noise_reduction::CTCSS_TONE_NONE) { return; }
// If not in CTCSS mute mode, do nothing
if (squelchModes[squelchModeId] != SQUELCH_MODE_CTCSS_MUTE) { return; }
// Set the tone
ctcssToneId = ctcssTones.valueId(tone);
ctcss.setRequiredTone(tone);
if (!selectedDemod) { return; }
// Save config
config.acquire();
config.conf[name][selectedDemod->getName()]["ctcssTone"] = ctcssTones.key(ctcssToneId);
config.release(true);
}
void setFMIFNREnabled(bool enabled) { void setFMIFNREnabled(bool enabled) {
FMIFNREnabled = enabled; FMIFNREnabled = enabled;
if (!selectedDemod) { return; } if (!selectedDemod) { return; }
@@ -801,13 +619,13 @@ private:
if (_this->bandwidthLocked) { return; } if (_this->bandwidthLocked) { return; }
_this->setBandwidth(*_in); _this->setBandwidth(*_in);
} }
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_MODE && out) { else if (code == RADIO_IFACE_CMD_GET_SQUELCH_ENABLED && out) {
SquelchMode* _out = (SquelchMode*)out; bool* _out = (bool*)out;
*_out = _this->squelchModes[_this->squelchModeId]; *_out = _this->squelchEnabled;
} }
else if (code == RADIO_IFACE_CMD_SET_SQUELCH_MODE && in && _this->enabled) { else if (code == RADIO_IFACE_CMD_SET_SQUELCH_ENABLED && in && _this->enabled) {
SquelchMode* _in = (SquelchMode*)in; bool* _in = (bool*)in;
_this->setSquelchMode(*_in); _this->setSquelchEnabled(*_in);
} }
else if (code == RADIO_IFACE_CMD_GET_SQUELCH_LEVEL && out) { else if (code == RADIO_IFACE_CMD_GET_SQUELCH_LEVEL && out) {
float* _out = (float*)out; float* _out = (float*)out;
@@ -817,22 +635,6 @@ private:
float* _in = (float*)in; float* _in = (float*)in;
_this->setSquelchLevel(*_in); _this->setSquelchLevel(*_in);
} }
else if (code == RADIO_IFACE_CMD_GET_CTCSS_TONE && out) {
dsp::noise_reduction::CTCSSTone* _out = (dsp::noise_reduction::CTCSSTone*)out;
*_out = _this->ctcssTones[_this->ctcssToneId];
}
else if (code == RADIO_IFACE_CMD_SET_CTCSS_TONE && in && _this->enabled) {
dsp::noise_reduction::CTCSSTone* _in = (dsp::noise_reduction::CTCSSTone*)in;
_this->setCTCSSTone(*_in);
}
else if (code == RADIO_IFACE_CMD_GET_HIGHPASS && out) {
bool* _out = (bool*)out;
*_out = _this->highPass;
}
else if (code == RADIO_IFACE_CMD_SET_HIGHPASS && in && _this->enabled) {
bool* _in = (bool*)in;
_this->setHighPass(*_in);
}
else { else {
return; return;
} }
@@ -853,15 +655,12 @@ private:
dsp::chain<dsp::complex_t> ifChain; dsp::chain<dsp::complex_t> ifChain;
dsp::noise_reduction::NoiseBlanker nb; dsp::noise_reduction::NoiseBlanker nb;
dsp::noise_reduction::FMIF fmnr; dsp::noise_reduction::FMIF fmnr;
dsp::noise_reduction::PowerSquelch powerSquelch; dsp::noise_reduction::Squelch squelch;
// Audio chain // Audio chain
dsp::stream<dsp::stereo_t> dummyAudioStream; dsp::stream<dsp::stereo_t> dummyAudioStream;
dsp::chain<dsp::stereo_t> afChain; dsp::chain<dsp::stereo_t> afChain;
dsp::noise_reduction::CTCSSSquelch ctcss;
dsp::multirate::RationalResampler<dsp::stereo_t> resamp; dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
dsp::tap<float> hpTaps;
dsp::filter::FIR<dsp::stereo_t, float> hpf;
dsp::filter::Deemphasis<dsp::stereo_t> deemp; dsp::filter::Deemphasis<dsp::stereo_t> deemp;
SinkManager::Stream stream; SinkManager::Stream stream;
@@ -870,8 +669,6 @@ private:
OptionList<std::string, DeemphasisMode> deempModes; OptionList<std::string, DeemphasisMode> deempModes;
OptionList<std::string, IFNRPreset> ifnrPresets; OptionList<std::string, IFNRPreset> ifnrPresets;
OptionList<std::string, SquelchMode> squelchModes;
OptionList<int, dsp::noise_reduction::CTCSSTone> ctcssTones;
double audioSampleRate = 48000.0; double audioSampleRate = 48000.0;
float minBandwidth; float minBandwidth;
@@ -882,13 +679,8 @@ private:
int selectedDemodID = 1; int selectedDemodID = 1;
bool postProcEnabled; bool postProcEnabled;
int squelchModeId = 0; bool squelchEnabled = false;
float squelchLevel; float squelchLevel;
int ctcssToneId = 0;
bool squelchAllowed = false;
bool highPass = false;
bool highPassAllowed = false;
int deempId = 0; int deempId = 0;
bool deempAllowed; bool deempAllowed;

View File

@@ -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/")

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;
};
}

View File

@@ -9,11 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
libcodec2-dev autoconf libtool xxd libspdlog-dev libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus # Install libperseus
@@ -46,32 +45,10 @@ make -j2
make install make install
cd ../../ cd ../../
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@@ -9,11 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
libcodec2-dev autoconf libtool xxd libspdlog-dev libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus # Install libperseus
@@ -46,32 +45,10 @@ make -j2
make install make install
cd ../../ cd ../../
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@@ -1,4 +1,4 @@
FROM debian:trixie FROM debian:buster
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
COPY do_build.sh /root COPY do_build.sh /root
RUN chmod +x /root/do_build.sh RUN chmod +x /root/do_build.sh

View File

@@ -4,16 +4,15 @@ cd /root
# Install dependencies and tools # Install dependencies and tools
apt update apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libairspyhf-dev libairspy-dev \ 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 \ libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd libspdlog-dev libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus # Install libperseus
@@ -46,33 +45,11 @@ make -j2
make install make install
cd ../../ cd ../../
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev' sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk1-dev, librtaudio-dev, libzstd-dev'

View File

@@ -9,11 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l
libcodec2-dev autoconf libtool xxd libspdlog-dev libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus # Install libperseus
@@ -46,32 +45,10 @@ make -j2
make install make install
cd ../../ cd ../../
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@@ -15,11 +15,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev
libcodec2-dev libudev-dev autoconf libtool xxd libspdlog-dev libcodec2-dev libudev-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install a more recent libusb version # Install a more recent libusb version
@@ -83,33 +82,11 @@ echo 'Version: 0.7' >> /usr/share/pkgconfig/codec2.pc
echo 'Libs: -L/usr/include/x86_64-linux-gnu/ -lcodec2' >> /usr/share/pkgconfig/codec2.pc echo 'Libs: -L/usr/include/x86_64-linux-gnu/ -lcodec2' >> /usr/share/pkgconfig/codec2.pc
echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc echo 'Cflags: -I/usr/include/codec2' >> /usr/share/pkgconfig/codec2.pc
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
# Build SDR++ Itself # Build SDR++ Itself
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_BLADERF_SOURCE=OFF -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_OVERRIDE_STD_FILESYSTEM=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
# Generate package # Generate package

View File

@@ -9,11 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
libcodec2-dev autoconf libtool xxd libspdlog-dev libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus # Install libperseus
@@ -46,32 +45,10 @@ make -j2
make install make install
cd ../../ cd ../../
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@@ -9,11 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
libcodec2-dev autoconf libtool xxd libspdlog-dev libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus # Install libperseus
@@ -46,32 +45,10 @@ make -j2
make install make install
cd ../../ cd ../../
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@@ -1,4 +1,4 @@
FROM ubuntu:oracular FROM ubuntu:mantic
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
COPY do_build.sh /root COPY do_build.sh /root
RUN chmod +x /root/do_build.sh RUN chmod +x /root/do_build.sh

View File

@@ -9,11 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l
libcodec2-dev autoconf libtool xxd libspdlog-dev libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus # Install libperseus
@@ -46,32 +45,10 @@ make -j2
make install make install
cd ../../ cd ../../
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@@ -9,11 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l
libcodec2-dev autoconf libtool xxd libspdlog-dev libcodec2-dev autoconf libtool xxd libspdlog-dev
# Install SDRPlay libraries # Install SDRPlay libraries
SDRPLAY_ARCH=$(dpkg --print-architecture) wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.1.run
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1.run
7z x ./SDRplay_RSP_API-Linux-3.15.2.run 7z x ./SDRplay_RSP_API-Linux-3.15.1
7z x ./SDRplay_RSP_API-Linux-3.15.2 cp x86_64/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp $SDRPLAY_ARCH/libsdrplay_api.so.3.15 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/ cp inc/* /usr/include/
# Install libperseus # Install libperseus
@@ -46,32 +45,10 @@ make -j2
make install make install
cd ../../ cd ../../
# Install libhydrasdr
git clone https://github.com/hydrasdr/rfone_host
cd rfone_host
mkdir build
cd build
cmake ..
make -j2
make install
cd ../../
# Install libdlcr
wget https://dragnlabs.com/host-tools/dlcr_host_v0.3.0.zip
mkdir dlcr_host
cd dlcr_host
7z x ../dlcr_host_v0.3.0.zip
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
make -j2
make install
cd ../../
cd SDRPlusPlus cd SDRPlusPlus
mkdir build mkdir build
cd build cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON -DOPT_BUILD_HYDRASDR_SOURCE=ON -DOPT_BUILD_DRAGONLABS_SOURCE=ON cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_RFNM_SOURCE=ON -DOPT_BUILD_FOBOSSDR_SOURCE=ON
make VERBOSE=1 -j2 make VERBOSE=1 -j2
cd .. cd ..

View File

@@ -28,8 +28,6 @@ bundle_is_not_to_be_installed() {
if [ "$1" = "Security" ]; then echo 1; fi if [ "$1" = "Security" ]; then echo 1; fi
if [ "$1" = "AppleFSCompression" ]; then echo 1; fi if [ "$1" = "AppleFSCompression" ]; then echo 1; fi
if [ "$1" = "libsdrplay_api.so.3.14" ]; then echo 1; fi if [ "$1" = "libsdrplay_api.so.3.14" ]; then echo 1; fi
if [ "$1" = "libsdrplay_api.so.3.15" ]; then echo 1; fi
if [ "$1" = "libxml2.2.dylib" ]; then echo 1; fi
} }
# ========================= FOR INTERNAL USE ONLY ========================= # ========================= FOR INTERNAL USE ONLY =========================
@@ -228,4 +226,4 @@ bundle_sign() {
fi fi
codesign --force --deep -s - $1 codesign --force --deep -s - $1
} }

View File

@@ -8,7 +8,7 @@ mkdir sdrpp_debian_amd64/DEBIAN
# Create package info # Create package info
echo Create package info echo Create package info
echo Package: sdrpp >> sdrpp_debian_amd64/DEBIAN/control echo Package: sdrpp >> sdrpp_debian_amd64/DEBIAN/control
echo Version: 1.2.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 Maintainer: Ryzerth >> sdrpp_debian_amd64/DEBIAN/control
echo Architecture: all >> sdrpp_debian_amd64/DEBIAN/control echo Architecture: all >> sdrpp_debian_amd64/DEBIAN/control
echo Description: Bloat-free SDR receiver software >> sdrpp_debian_amd64/DEBIAN/control echo Description: Bloat-free SDR receiver software >> sdrpp_debian_amd64/DEBIAN/control

View File

@@ -22,7 +22,7 @@ cp -R root/res/* $BUNDLE/Contents/Resources/
bundle_create_icns root/res/icons/sdrpp.macos.png $BUNDLE/Contents/Resources/sdrpp bundle_create_icns root/res/icons/sdrpp.macos.png $BUNDLE/Contents/Resources/sdrpp
# Create the property list # Create the property list
bundle_create_plist sdrpp SDR++ org.sdrpp.sdrpp 1.2.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 ========================= # ========================= Install binaries =========================
@@ -35,12 +35,10 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/fobossdr_source/fobossdr_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/fobossdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hydrasdr_source/hydrasdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/network_source/network_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib
@@ -58,7 +56,6 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/sink_modules/n
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/sink_modules/new_portaudio_sink/new_portaudio_sink.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/sink_modules/new_portaudio_sink/new_portaudio_sink.dylib
# Decoder modules # Decoder modules
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/atv_decoder/atv_decoder.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/m17_decoder/m17_decoder.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/m17_decoder/m17_decoder.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/meteor_demodulator/meteor_demodulator.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/meteor_demodulator/meteor_demodulator.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/radio/radio.dylib bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/decoder_modules/radio/radio.dylib

View File

@@ -32,14 +32,9 @@ cp 'C:/Program Files/PothosSDR/bin/hackrf.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windows_x64/modules/ cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windows_x64/modules/
cp $build_dir/source_modules/hydrasdr_source/Release/hydrasdr_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/hydrasdr-host/bin/hydrasdr.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/ cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/ cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/network_source/Release/network_source.dll sdrpp_windows_x64/modules/
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/ cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/ cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
@@ -77,8 +72,6 @@ cp $build_dir/sink_modules/network_sink/Release/network_sink.dll sdrpp_windows_x
# Copy decoder modules # Copy decoder modules
cp $build_dir/decoder_modules/atv_decoder/Release/atv_decoder.dll sdrpp_windows_x64/modules/
cp $build_dir/decoder_modules/m17_decoder/Release/m17_decoder.dll sdrpp_windows_x64/modules/ cp $build_dir/decoder_modules/m17_decoder/Release/m17_decoder.dll sdrpp_windows_x64/modules/
cp "C:/Program Files/codec2/lib/libcodec2.dll" sdrpp_windows_x64/ cp "C:/Program Files/codec2/lib/libcodec2.dll" sdrpp_windows_x64/

View File

@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.13.0) cmake_minimum_required (VERSION 3.2.0)
project (DiscordRPC) project (DiscordRPC)
include(GNUInstallDirs) include(GNUInstallDirs)

View File

@@ -168,9 +168,10 @@ public:
writer.setSamplerate(samplerate); writer.setSamplerate(samplerate);
// Open file // Open file
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : ""; std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
std::string extension = ".wav"; std::string extension = ".wav";
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, recMode, vfoName) + extension); std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
if (!writer.open(expandedPath)) { if (!writer.open(expandedPath)) {
flog::error("Failed to open file for recording: {0}", expandedPath); flog::error("Failed to open file for recording: {0}", expandedPath);
return; return;
@@ -451,7 +452,7 @@ private:
{ RADIO_IFACE_MODE_RAW, "RAW" } { 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 // Get data
time_t now = time(0); time_t now = time(0);
tm* ltm = localtime(&now); tm* ltm = localtime(&now);
@@ -461,9 +462,6 @@ private:
freq += gui::waterfall.vfos[name]->generalOffset; freq += gui::waterfall.vfos[name]->generalOffset;
} }
// Select the recording type string
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
// Format to string // Format to string
char freqStr[128]; char freqStr[128];
char hourStr[128]; char hourStr[128];
@@ -472,7 +470,7 @@ private:
char dayStr[128]; char dayStr[128];
char monStr[128]; char monStr[128];
char yearStr[128]; char yearStr[128];
const char* modeStr = (recMode == RECORDER_MODE_AUDIO) ? "Unknown" : "IQ"; const char* modeStr = "Unknown";
sprintf(freqStr, "%.0lfHz", freq); sprintf(freqStr, "%.0lfHz", freq);
sprintf(hourStr, "%02d", ltm->tm_hour); sprintf(hourStr, "%02d", ltm->tm_hour);
sprintf(minStr, "%02d", ltm->tm_min); sprintf(minStr, "%02d", ltm->tm_min);

View File

@@ -3,7 +3,6 @@
#include <gui/gui.h> #include <gui/gui.h>
#include <gui/style.h> #include <gui/style.h>
#include <signal_path/signal_path.h> #include <signal_path/signal_path.h>
#include <chrono>
SDRPP_MOD_INFO{ SDRPP_MOD_INFO{
/* Name: */ "scanner", /* Name: */ "scanner",

View File

@@ -324,13 +324,12 @@ Modules in beta are still included in releases for the most part but not enabled
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ | | audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ | | bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ | | file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
| fobossdr_source | Working | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ | | fobossdr_source | Beta | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ | | hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
| harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ✅ | | harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ✅ |
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ | | hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
| kcsdr_source | Unfinished | libkcsdr | OPT_BUILD_KCSDR_SOURCE | ⛔ | ⛔ | ⛔ |
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ | | limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
| network_source | Beta | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | | | network_source | Unfinished | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | |
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ | | perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ | | plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
| rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | ✅ | ✅ | | rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | ✅ | ✅ |
@@ -352,8 +351,8 @@ Modules in beta are still included in releases for the most part but not enabled
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) | | android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ | | audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ | | network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
| new_portaudio_sink | Working | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ | | new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
| portaudio_sink | Working | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ | | portaudio_sink | Beta | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
## Decoders ## Decoders
@@ -367,7 +366,6 @@ Modules in beta are still included in releases for the most part but not enabled
| meteor_demodulator | Working | - | OPT_BUILD_METEOR_DEMODULATOR | ✅ | ✅ | ⛔ | | meteor_demodulator | Working | - | OPT_BUILD_METEOR_DEMODULATOR | ✅ | ✅ | ⛔ |
| pager_decoder | Unfinished | - | OPT_BUILD_PAGER_DECODER | ⛔ | ⛔ | ⛔ | | pager_decoder | Unfinished | - | OPT_BUILD_PAGER_DECODER | ⛔ | ⛔ | ⛔ |
| radio | Working | - | OPT_BUILD_RADIO | ✅ | ✅ | ✅ | | radio | Working | - | OPT_BUILD_RADIO | ✅ | ✅ | ✅ |
| radio | Unfinished | - | OPT_BUILD_VOR_RECEIVER | ⛔ | ⛔ | ⛔ |
| weather_sat_decoder | Unfinished | - | OPT_BUILD_WEATHER_SAT_DECODER | ⛔ | ⛔ | ⛔ | | weather_sat_decoder | Unfinished | - | OPT_BUILD_WEATHER_SAT_DECODER | ⛔ | ⛔ | ⛔ |
## Misc ## Misc
@@ -420,8 +418,8 @@ If you still have an issue, please open an issue about it or ask on the discord.
# Contributing # Contributing
Feel free to submit band plans via the GitHub issue tracker. Feel free to submit pull requests and report bugs via the GitHub issue tracker.
For code changes, please create a feature request instead. I will soon publish a contributing.md listing the code style to use.
# Credits # Credits
@@ -441,18 +439,15 @@ For code changes, please create a feature request instead.
* Flinger Films * Flinger Films
* [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ) * [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ)
* gringogrigio * gringogrigio
* Jandro
* Jeff Moe * Jeff Moe
* Joe Cupano * Joe Cupano
* KD1SQ * KD1SQ
* Kezza * Kezza
* Krys Kamieniecki * Krys Kamieniecki
* Lee Donaghy * Lee Donaghy
* Lee (KD1SQ) * Lee KD1SQ
* .lozenge. (Hank Hill) * .lozenge. (Hank Hill)
* Martin Herren (HB9FXX) * Martin Herren (HB9FXX)
* NeoVilsonWong
* Nitin (VU2JEK)
* ON4MU * ON4MU
* [Passion-Radio.com](https://passion-radio.com/) * [Passion-Radio.com](https://passion-radio.com/)
* Paul Maine * Paul Maine

View File

@@ -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
}
]
}

View File

@@ -14,7 +14,7 @@
"name": "Radionavigazione", "name": "Radionavigazione",
"type": "marine", "type": "marine",
"start": 11300, "start": 11300,
"end": 148500 "end": 135500
}, },
{ {
"name": "Radioamatori 137kHz", "name": "Radioamatori 137kHz",
@@ -47,7 +47,7 @@
"end": 520000 "end": 520000
}, },
{ {
"name": "Radioamatori 472kHz", "name": "Radioamatori 472 kHz",
"type": "amateur", "type": "amateur",
"start": 472000, "start": 472000,
"end": 479000 "end": 479000
@@ -65,7 +65,7 @@
"end": 1830000 "end": 1830000
}, },
{ {
"name": "Radioamatori 160m", "name": "Radioamatori 160 m",
"type": "amateur", "type": "amateur",
"start": 1830000, "start": 1830000,
"end": 1850000 "end": 1850000
@@ -77,7 +77,7 @@
"end": 2300000 "end": 2300000
}, },
{ {
"name": "Radiodiffusione OC 120m", "name": "Radiodiffusione OC 120 m",
"type": "broadcast", "type": "broadcast",
"start": 2300000, "start": 2300000,
"end": 2500000 "end": 2500000
@@ -107,7 +107,7 @@
"end": 3200000 "end": 3200000
}, },
{ {
"name": "Radiodiffusione OC 90m", "name": "Radiodiffusione OC 90 m",
"type": "broadcast", "type": "broadcast",
"start": 3200000, "start": 3200000,
"end": 3400000 "end": 3400000
@@ -125,13 +125,13 @@
"end": 3600000 "end": 3600000
}, },
{ {
"name": "Radioamatori 80m", "name": "Radioamatori 80 m",
"type": "amateur", "type": "amateur",
"start": 3500000, "start": 3600000,
"end": 3800000 "end": 3800000
}, },
{ {
"name": "Radiodiffusione OC 75m", "name": "Radiodiffusione OC 75 m",
"type": "broadcast", "type": "broadcast",
"start": 3900000, "start": 3900000,
"end": 4000000 "end": 4000000
@@ -150,19 +150,19 @@
}, },
{ {
"name": "Radiodiffusione OC 60m", "name": "Radiodiffusione OC 60 m",
"type": "broadcast", "type": "broadcast",
"start": 4750000, "start": 4750000,
"end": 4995000 "end": 4995000
}, },
{ {
"name": "Radiodiffusione OC 60m", "name": "Radiodiffusione OC 60 m",
"type": "broadcast", "type": "broadcast",
"start": 5005000, "start": 5005000,
"end": 5060000 "end": 5060000
}, },
{ {
"name": "Radioamatori 60m", "name": "Radioamatori 60 m",
"type": "amateur", "type": "amateur",
"start": 5351500, "start": 5351500,
"end": 5366500 "end": 5366500
@@ -174,7 +174,7 @@
"end": 5730000 "end": 5730000
}, },
{ {
"name": "Radiodiffusione OC 49m", "name": "Radiodiffusione OC 49 m",
"type": "broadcast", "type": "broadcast",
"start": 5900000, "start": 5900000,
"end": 6200000 "end": 6200000
@@ -192,13 +192,13 @@
"end": 6765000 "end": 6765000
}, },
{ {
"name": "Radioamatori 40m", "name": "Radioamatori 40 m",
"type": "amateur", "type": "amateur",
"start": 7000000, "start": 7000000,
"end": 7200000 "end": 7200000
}, },
{ {
"name": "Radiodiffusione OC 41m", "name": "Radiodiffusione OC 41 m",
"type": "broadcast", "type": "broadcast",
"start": 7200000, "start": 7200000,
"end": 7450000 "end": 7450000
@@ -216,7 +216,7 @@
"end": 9040000 "end": 9040000
}, },
{ {
"name": "Radiodiffusione OC 31m", "name": "Radiodiffusione OC 31 m",
"type": "broadcast", "type": "broadcast",
"start": 9400000, "start": 9400000,
"end": 9900000 "end": 9900000
@@ -228,7 +228,7 @@
"end": 10100000 "end": 10100000
}, },
{ {
"name": "Radioamatori 30m", "name": "30m - Radioamateur",
"type": "amateur", "type": "amateur",
"start": 10100000, "start": 10100000,
"end": 10150000 "end": 10150000
@@ -282,7 +282,7 @@
"end": 15100000 "end": 15100000
}, },
{ {
"name": "Radiodiffusione OC 19m", "name": "Radiodiffusione OC 19 m",
"type": "broadcast", "type": "broadcast",
"start": 15100000, "start": 15100000,
"end": 15800000 "end": 15800000
@@ -294,7 +294,7 @@
"end": 17410000 "end": 17410000
}, },
{ {
"name": "Radiodiffusione OC 16m", "name": "Radiodiffusione OC 16 m",
"type": "broadcast", "type": "broadcast",
"start": 17480000, "start": 17480000,
"end": 17900000 "end": 17900000
@@ -306,9 +306,9 @@
"end": 18030000 "end": 18030000
}, },
{ {
"name": "Radioamatori 17m", "name": "Radioamatori 17 m",
"type": "amateur", "type": "amateur",
"start": 18069000, "start": 18068000,
"end": 18168000 "end": 18168000
}, },
{ {
@@ -318,7 +318,7 @@
"end": 18900000 "end": 18900000
}, },
{ {
"name": "Radiodiffusione OC 15m", "name": "Radiodiffusione OC 15 m",
"type": "broadcast", "type": "broadcast",
"start": 18900000, "start": 18900000,
"end": 19020000 "end": 19020000
@@ -336,13 +336,13 @@
"end": 20010000 "end": 20010000
}, },
{ {
"name": "Radioamatori 15m", "name": "Radioamatori 15 m",
"type": "amateur", "type": "amateur",
"start": 21000000, "start": 21000000,
"end": 21450000 "end": 21450000
}, },
{ {
"name": "Radiodiffusione OC 13m", "name": "Radiodiffusione OC 13 m",
"type": "broadcast", "type": "broadcast",
"start": 21450000, "start": 21450000,
"end": 21850000 "end": 21850000
@@ -366,7 +366,7 @@
"end": 23350000 "end": 23350000
}, },
{ {
"name": "Radioamatori 12m", "name": "Radioamatori 12 m",
"type": "amateur", "type": "amateur",
"start": 24890000, "start": 24890000,
"end": 24990000 "end": 24990000
@@ -390,7 +390,7 @@
"end": 25670000 "end": 25670000
}, },
{ {
"name": "Radiodiffusione OC 11m", "name": "Radiodiffusione OC 11 m",
"type": "broadcast", "type": "broadcast",
"start": 25670000, "start": 25670000,
"end": 26100000 "end": 26100000
@@ -408,7 +408,7 @@
"end": 27230000 "end": 27230000
}, },
{ {
"name": "Radioamatori 10m", "name": "Radioamatori 10 m",
"type": "amateur", "type": "amateur",
"start": 28000000, "start": 28000000,
"end": 29700000 "end": 29700000
@@ -420,10 +420,10 @@
"end": 47000000 "end": 47000000
}, },
{ {
"name": "Radioamatori 6m", "name": "Radioamatori 9 m",
"type": "amateur", "type": "amateur",
"start": 50000000, "start": 47000000,
"end": 51000000 "end": 52500000
}, },
{ {
"name": "Wind profiler", "name": "Wind profiler",
@@ -438,7 +438,7 @@
"end": 74800000 "end": 74800000
}, },
{ {
"name": "Radiofari 75MHz", "name": "Radiofari 75 MHz",
"type": "aviation", "type": "aviation",
"start": 74800000, "start": 74800000,
"end": 75200000 "end": 75200000
@@ -446,19 +446,19 @@
{ {
"name": "Radiodiffusione FM", "name": "Radiodiffusione FM",
"type": "broadcast", "type": "broadcast",
"start": 87500000, "start": 80000000,
"end": 108000000 "end": 108000000
}, },
{ {
"name": "VOR/ILS", "name": "VOR/ILS",
"type": "aviation", "type": "aviation",
"start": 108000000, "start": 108000000,
"end": 117975000 "end": 118000000
}, },
{ {
"name": "Mobile aeronautico", "name": "Mobile aeronautico",
"type": "aviation", "type": "aviation",
"start": 117975000, "start": 118000000,
"end": 137000000 "end": 137000000
}, },
{ {
@@ -468,10 +468,10 @@
"end": 138000000 "end": 138000000
}, },
{ {
"name": "Radioamatori 2m", "name": "Radioamatori 2 m",
"type": "amateur", "type": "amateur",
"start": 144000000, "start": 144000000,
"end": 146000000 "end": 148000000
}, },
{ {
"name": "Telefonia satellitare", "name": "Telefonia satellitare",
@@ -552,7 +552,7 @@
"end": 430000000 "end": 430000000
}, },
{ {
"name": "Radioamatori 70cm", "name": "Radioamatori 70 cm",
"type": "amateur", "type": "amateur",
"start": 430000000, "start": 430000000,
"end": 434000000 "end": 434000000
@@ -564,10 +564,10 @@
"end": 435000000 "end": 435000000
}, },
{ {
"name": "Radioamatori 70cm", "name": "Radioamatori 70 cm",
"type": "amateur", "type": "amateur",
"start": 435000000, "start": 435000000,
"end": 438000000 "end": 436000000
}, },
{ {
"name": "Mobile o fisso privato", "name": "Mobile o fisso privato",
@@ -642,16 +642,10 @@
"end": 124000000 "end": 124000000
}, },
{ {
"name": "Radioamatori 23cm", "name": "Radioamatori 23 cm",
"type": "amateur", "type": "amateur",
"start": 1240000000, "start": 1240000000,
"end": 1245000000 "end": 1270000000
},
{
"name": "Radioamatori 23cm",
"type": "amateur",
"start": 1267000000,
"end": 1298000000
}, },
{ {
"name": "Wind profiler", "name": "Wind profiler",
@@ -798,10 +792,10 @@
"end": 2300000000 "end": 2300000000
}, },
{ {
"name": "Radioamatori 13cm", "name": "Radioamatori 13 cm",
"type": "amateur", "type": "amateur",
"start": 2300000000, "start": 2300000000,
"end": 2450000000 "end": 2400000000
}, },
{ {
"name": "ISM, SAP/SAB, 802.11", "name": "ISM, SAP/SAB, 802.11",
@@ -833,6 +827,12 @@
"start": 2900000000, "start": 2900000000,
"end": 3400000000 "end": 3400000000
}, },
{
"name": "Radioamatori 9 cm",
"type": "amateur",
"start": 3400000000,
"end": 3475000000
},
{ {
"name": "Reti numeriche", "name": "Reti numeriche",
"type": "comms", "type": "comms",
@@ -858,21 +858,9 @@
"end": 5650000000 "end": 5650000000
}, },
{ {
"name": "Radioamatori 5cm", "name": "Radioamatori 6 cm",
"type": "amateur", "type": "amateur",
"start": 5650000000, "start": 5650000000,
"end": 5670000000
},
{
"name": "Radioamatori 5cm",
"type": "amateur",
"start": 5760000000,
"end": 5770000000
},
{
"name": "Radioamatori 5cm",
"type": "amateur",
"start": 5830000000,
"end": 5850000000 "end": 5850000000
}, },
{ {
@@ -924,9 +912,9 @@
"end": 10000000000 "end": 10000000000
}, },
{ {
"name": "Radioamatori 3cm", "name": "Radioamatori 3 cm",
"type": "amateur", "type": "amateur",
"start": 10300000000, "start": 10000000000,
"end": 10500000000 "end": 10500000000
}, },
{ {
@@ -1013,16 +1001,10 @@
"start": 23150000000, "start": 23150000000,
"end": 23338000000 "end": 23338000000
}, },
{
"name": "Radioamatori 1,5cm",
"type": "amateur",
"start": 24000000000,
"end": 24050000000
},
{ {
"name": "ISM, SRD e LPR", "name": "ISM, SRD e LPR",
"type": "ism", "type": "ism",
"start": 24050000000, "start": 24000000000,
"end": 24450000000 "end": 24450000000
}, },
{ {
@@ -1104,7 +1086,7 @@
"end": 43500000000 "end": 43500000000
}, },
{ {
"name": "Radioamatori 7mm", "name": "Radioamatori 6 mm",
"type": "amateur", "type": "amateur",
"start": 47000000000, "start": 47000000000,
"end": 47200000000 "end": 47200000000
@@ -1146,9 +1128,9 @@
"end": 76500000000 "end": 76500000000
}, },
{ {
"name": "Radioamatori 4mm", "name": "Radioamatori 4 mm",
"type": "amateur", "type": "amateur",
"start": 75500000000, "start": 76500000000,
"end": 81500000000 "end": 81500000000
}, },
{ {
@@ -1164,25 +1146,19 @@
"end": 122250000000 "end": 122250000000
}, },
{ {
"name": "Radioamatori 2,4mm", "name": "Radioamatori 2,5 mm",
"type": "amateur", "type": "amateur",
"start": 122500000000, "start": 122250000000,
"end": 123000000000 "end": 123000000000
}, },
{ {
"name": "Radioamatori 2,23mm", "name": "Radioamatori 2 mm",
"type": "amateur", "type": "amateur",
"start": 134000000000, "start": 134000000000,
"end": 141000000000 "end": 141000000000
}, },
{ {
"name": "Radioamatori 2,1mm", "name": "Radioamatori 1 mm",
"type": "amateur",
"start": 142000000000,
"end": 144000000000
},
{
"name": "Radioamatori 1mm",
"type": "amateur", "type": "amateur",
"start": 241000000000, "start": 241000000000,
"end": 250000000000 "end": 250000000000

View File

@@ -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
}
]
}

View File

@@ -183,9 +183,6 @@ private:
static void start(void* ctx) { static void start(void* ctx) {
AudioSourceModule* _this = (AudioSourceModule*)ctx; AudioSourceModule* _this = (AudioSourceModule*)ctx;
if (_this->running) { return; } if (_this->running) { return; }
// If no device is selected, give up
if (_this->selectedDevice.empty()) { return; }
// Stream options // Stream options
RtAudio::StreamParameters parameters; RtAudio::StreamParameters parameters;

View File

@@ -0,0 +1,25 @@
cmake_minimum_required(VERSION 3.13)
project(badgesdr_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
if (MSVC)
find_package(libusb CONFIG REQUIRED)
target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS})
target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES})
elseif (ANDROID)
target_link_libraries(badgesdr_source PUBLIC
/sdr-kit/${ANDROID_ABI}/lib/libusb1.0.so
/sdr-kit/${ANDROID_ABI}/lib/librtlsdr.so
)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
target_include_directories(badgesdr_source PRIVATE ${LIBUSB_INCLUDE_DIRS})
target_link_directories(badgesdr_source PRIVATE ${LIBUSB_LIBRARY_DIRS})
target_link_libraries(badgesdr_source PRIVATE ${LIBUSB_LIBRARIES})
endif ()

View File

@@ -0,0 +1,309 @@
#include "badgesdr.h"
#include <stdexcept>
#include <utils/flog.h>
#define R820T_I2C_ADDR 0x1A
namespace BadgeSDR {
enum Commands {
CMD_I2C_RW,
CMD_I2C_STATUS,
CMD_ADC_START,
CMD_ADC_STOP,
CMD_ADC_GET_SAMP_COUNT
};
libusb_context* ctx = NULL;
bool DeviceInfo::operator==(const DeviceInfo& b) const {
return serialNumber == b.serialNumber;
}
void Device::write_reg(uint8_t reg, uint8_t value, void* ctx) {
Device* dev = (Device*)ctx;
dev->writeR820TReg(reg, value);
dev->writeR820TReg(0x1F, 0); // TODO: Figure out why this is needed
}
void Device::read_reg(uint8_t* data, int len, void* ctx) {
Device* dev = (Device*)ctx;
dev->readI2C(R820T_I2C_ADDR, data, len);
}
Device::Device(libusb_device_handle* dev) {
// Save device handle
this->dev = dev;
// Init tuner
r820t = {
16000000, // xtal_freq => 16MHz
3000000, // Set at boot to airspy_m0_m4_conf_t conf0 -> r820t_if_freq
100000000, /* Default Freq 100Mhz */
{
/* 05 */ 0x9F, // LNA manual gain mode, init to 0
/* 06 */ 0x80,
/* 07 */ 0x60,
/* 08 */ 0x80, // Image Gain Adjustment
/* 09 */ 0x40, // Image Phase Adjustment
/* 0A */ 0xA8, // Channel filter [0..3]: 0 = widest, f = narrowest - Optimal. Don't touch!
/* 0B */ 0x0F, // High pass filter - Optimal. Don't touch!
/* 0C */ 0x4F, // VGA control by code, init at 0
/* 0D */ 0x63, // LNA AGC settings: [0..3]: Lower threshold; [4..7]: High threshold
/* 0E */ 0x75,
/* 0F */ 0xE8, // Filter Widest, LDO_5V OFF, clk out ON,
/* 10 */ 0x7C,
/* 11 */ 0x42,
/* 12 */ 0x06,
/* 13 */ 0x00,
/* 14 */ 0x0F,
/* 15 */ 0x00,
/* 16 */ 0xC0,
/* 17 */ 0xA0,
/* 18 */ 0x48,
/* 19 */ 0xCC,
/* 1A */ 0x60,
/* 1B */ 0x00,
/* 1C */ 0x54,
/* 1D */ 0xAE,
/* 1E */ 0x0A,
/* 1F */ 0xC0
},
0 /* uint16_t padding */
};
r820t_init(&r820t, 3000000, write_reg, read_reg, this);
r820t_set_mixer_gain(&r820t, 15);
r820t_set_vga_gain(&r820t, 15);
}
Device::~Device() {
// Release the bulk interface
libusb_release_interface(dev, 0);
// Close device
libusb_close(dev);
}
void Device::setFrequency(double freq) {
r820t_set_freq(&r820t, freq - 2125000);
}
void Device::setLNAGain(int gain) {
r820t_set_lna_gain(&r820t, gain);
}
void Device::setMixerGain(int gain) {
r820t_set_mixer_gain(&r820t, gain);
}
void Device::setVGAGain(int gain) {
r820t_set_vga_gain(&r820t, gain);
}
void Device::start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx, int minBufferSize) {
// Do nothing if already running
if (run) { return; }
// Save handler
this->callback = callback;
this->ctx = ctx;
// Compute buffer size
int bufCount = minBufferSize / 64;
if (minBufferSize % 64) { bufCount++; }
bufferSize = bufCount * 64;
// Mark as running
run = true;
// Start the ADC
startADC();
// Start worker thread
workerThread = std::thread(&Device::worker, this);
}
void Device::stop() {
// Do nothing if already stopped
if (!run) { return; }
// Mark as stopped
run = false;
// Wait for the worker to exit
if (workerThread.joinable()) { workerThread.join(); }
// Stop the ADC
stopADC();
}
int Device::getI2CStatus() {
int status;
int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
if (ret <= 0 || status < 0) {
return -1;
}
return status;
}
int Device::readI2C(uint8_t addr, uint8_t* data, int len) {
// Do read
int bytes = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_RW, 0, addr, data, len, 1000);
if (bytes < 0) {
return -1;
}
// Get status (TODO: Use function)
int status;
int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
if (ret <= 0 || status < 0) {
return -1;
}
return bytes;
}
int Device::writeI2C(uint8_t addr, const uint8_t* data, int len) {
// Do write
int bytes = libusb_control_transfer(dev, (2 << 5), CMD_I2C_RW, 0, addr, (uint8_t*)data, len, 1000);
if (bytes < 0) {
return -1;
}
// Get status (TODO: Use function)
int status;
int ret = libusb_control_transfer(dev, (1 << 7) | (2 << 5), CMD_I2C_STATUS, 0, 0, (uint8_t*)&status, sizeof(status), 1000);
if (ret <= 0 || status < 0) {
return -1;
}
return bytes;
}
uint8_t bitrev(uint8_t val) {
return ((val & 0x01) << 7) | ((val & 0x02) << 5) | ((val & 0x04) << 3) | ((val & 0x08) << 1) | ((val & 0x10) >> 1) | ((val & 0x20) >> 3) | ((val & 0x40) >> 5) | ((val & 0x80) >> 7);
}
uint8_t Device::readR820TReg(uint8_t reg) {
// Read registers up to it (can't read single)
uint8_t regs[0x20];
readI2C(R820T_I2C_ADDR, regs, reg+1);
// Invert bit order
return bitrev(regs[reg]);
}
void Device::writeR820TReg(uint8_t reg, uint8_t val) {
// Write register id then value
uint8_t cmd[2] = { reg, val };
writeI2C(R820T_I2C_ADDR, cmd, 2);
}
int Device::startADC() {
return libusb_control_transfer(dev, (2 << 5), CMD_ADC_START, 0, 0, NULL, 0, 1000);
}
int Device::stopADC() {
return libusb_control_transfer(dev, (2 << 5), CMD_ADC_STOP, 0, 0, NULL, 0, 1000);
}
void Device::worker() {
// Allocate sample buffer
uint8_t* buffer = new uint8_t[bufferSize];
int sampleCount = 0;
while (run) {
// Receive data with bulk transfer
int recvLen = 0;
int val = libusb_bulk_transfer(dev, LIBUSB_ENDPOINT_IN | 1, &buffer[sampleCount], bufferSize - sampleCount, &recvLen, 1000);
// If timed out, try again. Otherwise, if an error occur, stop the thread
if (val == LIBUSB_ERROR_TIMEOUT) {
continue;
}
else if (val) {
flog::error("USB Error: {}", val);
break;
}
// Increment sample count
if (recvLen) { sampleCount += recvLen; }
// If the buffer is full, call handler and reset sample count
if (sampleCount >= bufferSize) {
callback(buffer, sampleCount, ctx);
sampleCount = 0;
}
}
// Free buffer
delete[] buffer;
}
std::vector<DeviceInfo> list() {
// Init libusb if done yet
if (!ctx) {
libusb_init(&ctx);
libusb_set_debug(ctx, LIBUSB_LOG_LEVEL_WARNING);
}
// List devices
std::vector<DeviceInfo> devList;
libusb_device** devices;
int devCount = libusb_get_device_list(ctx, &devices);
for (int i = 0; i < devCount; i++) {
// Get device info
DeviceInfo devInfo;
devInfo.dev = devices[i];
libusb_device_descriptor desc;
libusb_get_device_descriptor(devInfo.dev, &desc);
// Check the VID/PID and give up if not the right ones
if (desc.idVendor != 0xCAFE || desc.idProduct != 0x4010) {
libusb_unref_device(devInfo.dev);
continue;
}
// Open devices
libusb_device_handle* openDev;
int err = libusb_open(devInfo.dev, &openDev);
if (err) {
libusb_unref_device(devInfo.dev);
continue;
}
// Get serial number
char serial[128];
libusb_get_string_descriptor_ascii(openDev, desc.iSerialNumber, (uint8_t*)serial, sizeof(serial));
devInfo.serialNumber = serial;
// TODO: The libusb device should be unreffed but would need to know when the list disappears
// Close device
libusb_close(openDev);
// Add to list
devList.push_back(devInfo);
}
// Return devices
return devList;
}
std::shared_ptr<Device> open(const DeviceInfo& dev) {
// Open device
libusb_device_handle* openDev;
int err = libusb_open(dev.dev, &openDev);
if (err) {
throw std::runtime_error("Failed to open device");
}
// Claim the bulk transfer interface
if (libusb_claim_interface(openDev, 0)) {
throw std::runtime_error("Failed to claim bulk interface");
}
// Create device
return std::make_shared<Device>(openDev);
}
}

View File

@@ -0,0 +1,53 @@
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <thread>
#include <libusb.h>
#include "r820t.h"
namespace BadgeSDR {
struct DeviceInfo {
std::string serialNumber;
libusb_device* dev;
bool operator==(const DeviceInfo& b) const;
};
class Device {
public:
Device(libusb_device_handle* dev);
~Device();
void setFrequency(double freq);
void setLNAGain(int gain);
void setMixerGain(int gain);
void setVGAGain(int gain);
void start(void (*callback)(const uint8_t* samples, int count, void* ctx), void* ctx = NULL, int minBufferSize = 2500);
void stop();
private:
int getI2CStatus();
int readI2C(uint8_t addr, uint8_t* data, int len);
int writeI2C(uint8_t addr, const uint8_t* data, int len);
uint8_t readR820TReg(uint8_t reg);
void writeR820TReg(uint8_t reg, uint8_t val);
int startADC();
int stopADC();
void worker();
libusb_device_handle* dev;
std::thread workerThread;
bool run = false;
int bufferSize = 0; // Must be multiple of 64 for best performance
void* ctx = NULL;
void (*callback)(const uint8_t* samples, int count, void* ctx);
static void write_reg(uint8_t reg, uint8_t value, void* ctx);
static void read_reg(uint8_t* data, int len, void* ctx);
r820t_priv_t r820t;
};
std::vector<DeviceInfo> list();
std::shared_ptr<Device> open(const DeviceInfo& dev);
}

View File

@@ -0,0 +1,272 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <utils/optionlist.h>
#include <dsp/channel/rx_vfo.h>
#include <dsp/correction/dc_blocker.h>
#include "badgesdr.h"
SDRPP_MOD_INFO{
/* Name: */ "badgesdr_source",
/* Description: */ "BadgeSDR Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
#define CONCAT(a, b) ((std::string(a) + b).c_str())
class BadgeSDRSourceModule : public ModuleManager::Instance {
public:
BadgeSDRSourceModule(std::string name) {
this->name = name;
sampleRate = 250000.0;
// Initialize DSP
dcBlock.init(&input, 0.001);
ddc.init(&dcBlock.out, 500000, 250000, 250000, 125000);
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &ddc.out;
// Refresh devices
refresh();
// Select first (TODO: Select from config)
select("");
sigpath::sourceManager.registerSource("BadgeSDR", &handler);
}
~BadgeSDRSourceModule() {
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
void refresh() {
devices.clear();
auto list = BadgeSDR::list();
for (const auto& info : list) {
// Format device name
std::string devName = "BadgeSDR ";
devName += " [";
devName += info.serialNumber;
devName += ']';
// Save device
devices.define(info.serialNumber, devName, info);
}
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Save serial number
selectedSerial = serial;
selectedDev = devices.value(devices.keyId(serial));
}
static void menuSelected(void* ctx) {
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("BadgeSDRSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
flog::info("BadgeSDRSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
if (_this->running) { return; }
// Open the device
_this->openDev = BadgeSDR::open(_this->selectedDev);
// Configure the device
_this->openDev->setFrequency(_this->freq);
_this->openDev->setLNAGain(_this->lnaGain);
_this->openDev->setMixerGain(_this->mixerGain);
_this->openDev->setVGAGain(_this->vgaGain);
// Start DSP
_this->dcBlock.start();
_this->ddc.start();
// Start device
_this->openDev->start(callback, _this, 500000/200);
_this->running = true;
flog::info("BadgeSDRSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop worker
_this->openDev->stop();
// Stop DSP
_this->dcBlock.stop();
_this->ddc.stop();
// Close device
_this->openDev.reset();
flog::info("BadgeSDRSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
if (_this->running) {
_this->openDev->setFrequency(freq);
}
_this->freq = freq;
flog::info("BadgeSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_badgesdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_badgesdr_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("LNA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_badgesdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
if (_this->running) {
_this->openDev->setLNAGain(_this->lnaGain);
}
// TODO: Save
}
SmGui::LeftLabel("Mixer Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_badgesdr_mixer_gain_", _this->name), &_this->mixerGain, 0, 15)) {
if (_this->running) {
_this->openDev->setMixerGain(_this->mixerGain);
}
// TODO: Save
}
SmGui::LeftLabel("VGA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_badgesdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
if (_this->running) {
_this->openDev->setVGAGain(_this->vgaGain);
}
// TODO: Save
}
}
static void callback(const uint8_t* samples, int count, void* ctx) {
BadgeSDRSourceModule* _this = (BadgeSDRSourceModule*)ctx;
// Convert samples to float
dsp::complex_t* out = _this->input.writeBuf;
int min = 255, max = 0;
for (int i = 0; i < count; i++) {
if (samples[i] < min) { min = samples[i]; }
if (samples[i] > max) { max = samples[i]; }
out[i].re = ((float)samples[i] - 127.5f) * (1.0f/127.0f);
out[i].im = 1.0f;
}
// Send out samples
_this->input.swap(count);
flog::debug("Amplitudes: {} -> {}", min, max);
}
std::string name;
bool enabled = true;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
OptionList<std::string, BadgeSDR::DeviceInfo> devices;
int devId = 0;
int lnaGain = 0;
int mixerGain = 0;
int vgaGain = 0;
std::string selectedSerial;
BadgeSDR::DeviceInfo selectedDev;
std::shared_ptr<BadgeSDR::Device> openDev;
dsp::stream<dsp::complex_t> input;
dsp::correction::DCBlocker<dsp::complex_t> dcBlock;
dsp::channel::RxVFO ddc;
};
MOD_EXPORT void _INIT_() {
// Nothing here
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new BadgeSDRSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (BadgeSDRSourceModule*)instance;
}
MOD_EXPORT void _END_() {
// Nothing here
}

View File

@@ -0,0 +1,622 @@
/*
* Rafael Micro R820T driver for AIRSPY
*
* Copyright 2013 Youssef Touil <youssef@airspy.com>
* Copyright 2014-2016 Benjamin Vernoux <bvernoux@airspy.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "r820t.h"
#include <thread>
static int r820t_read_cache_reg(r820t_priv_t *priv, int reg);
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
/* Tuner frequency ranges */
struct r820t_freq_range
{
uint8_t open_d;
uint8_t rf_mux_ploy;
uint8_t tf_c;
};
#define R820T_READ_MAX_DATA 32
#define R820T_INIT_NB_REGS (32-5)
uint8_t r820t_read_data[R820T_READ_MAX_DATA]; /* Buffer for data read from I2C */
uint8_t r820t_state_standby = 1; /* 1=standby/power off 0=r820t initialized/power on */
/* Tuner frequency ranges
"Copyright (C) 2013 Mauro Carvalho Chehab"
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
part of freq_ranges()
*/
const struct r820t_freq_range freq_ranges[] =
{
{
/* 0 MHz */
/* .open_d = */ 0x08, /* low */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0xdf, /* R27[7:0] band2,band0 */
}, {
/* 50 MHz */
/* .open_d = */ 0x08, /* low */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0xbe, /* R27[7:0] band4,band1 */
}, {
/* 55 MHz */
/* .open_d = */ 0x08, /* low */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x8b, /* R27[7:0] band7,band4 */
}, {
/* 60 MHz */
/* .open_d = */ 0x08, /* low */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x7b, /* R27[7:0] band8,band4 */
}, {
/* 65 MHz */
/* .open_d = */ 0x08, /* low */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x69, /* R27[7:0] band9,band6 */
}, {
/* 70 MHz */
/* .open_d = */ 0x08, /* low */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x58, /* R27[7:0] band10,band7 */
}, {
/* 75 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */
}, {
/* 80 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */
}, {
/* 90 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */
}, {
/* 100 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */
}, {
/* 110 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */
}, {
/* 120 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */
}, {
/* 140 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x14, /* R27[7:0] band14,band11 */
}, {
/* 180 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */
}, {
/* 220 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */
}, {
/* 250 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x11, /* R27[7:0] highest,highest */
}, {
/* 280 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
}, {
/* 310 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
}, {
/* 450 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
}, {
/* 588 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
}, {
/* 650 MHz */
/* .open_d = */ 0x00, /* high */
/* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */
/* .tf_c = */ 0x00, /* R27[7:0] highest,highest */
}
};
#define FREQ_TO_IDX_SIZE (600)
const uint8_t freq_to_idx[FREQ_TO_IDX_SIZE]=
{
/* 50 */ 1,/* 51 */ 1,/* 52 */ 1,/* 53 */ 1,/* 54 */ 1,
/* 55 */ 2,/* 56 */ 2,/* 57 */ 2,/* 58 */ 2,/* 59 */ 2,
/* 60 */ 3,/* 61 */ 3,/* 62 */ 3,/* 63 */ 3,/* 64 */ 3,
/* 65 */ 4,/* 66 */ 4,/* 67 */ 4,/* 68 */ 4,/* 69 */ 4,
/* 70 */ 5,/* 71 */ 5,/* 72 */ 5,/* 73 */ 5,/* 74 */ 5,
/* 75 */ 6,/* 76 */ 6,/* 77 */ 6,/* 78 */ 6,/* 79 */ 6,
/* 80 */ 7,/* 81 */ 7,/* 82 */ 7,/* 83 */ 7,/* 84 */ 7,/* 85 */ 7,/* 86 */ 7,/* 87 */ 7,/* 88 */ 7,/* 89 */ 7,
/* 90 */ 8,/* 91 */ 8,/* 92 */ 8,/* 93 */ 8,/* 94 */ 8,/* 95 */ 8,/* 96 */ 8,/* 97 */ 8,/* 98 */ 8,/* 99 */ 8,
/* 100 */ 9,/* 101 */ 9,/* 102 */ 9,/* 103 */ 9,/* 104 */ 9,/* 105 */ 9,/* 106 */ 9,/* 107 */ 9,/* 108 */ 9,/* 109 */ 9,
/* 110 */ 10,/* 111 */ 10,/* 112 */ 10,/* 113 */ 10,/* 114 */ 10,/* 115 */ 10,/* 116 */ 10,/* 117 */ 10,/* 118 */ 10,/* 119 */ 10,
/* 120 */ 11,/* 121 */ 11,/* 122 */ 11,/* 123 */ 11,/* 124 */ 11,/* 125 */ 11,/* 126 */ 11,/* 127 */ 11,/* 128 */ 11,/* 129 */ 11,
/* 130 */ 11,/* 131 */ 11,/* 132 */ 11,/* 133 */ 11,/* 134 */ 11,/* 135 */ 11,/* 136 */ 11,/* 137 */ 11,/* 138 */ 11,/* 139 */ 11,
/* 140 */ 12,/* 141 */ 12,/* 142 */ 12,/* 143 */ 12,/* 144 */ 12,/* 145 */ 12,/* 146 */ 12,/* 147 */ 12,/* 148 */ 12,/* 149 */ 12,
/* 150 */ 12,/* 151 */ 12,/* 152 */ 12,/* 153 */ 12,/* 154 */ 12,/* 155 */ 12,/* 156 */ 12,/* 157 */ 12,/* 158 */ 12,/* 159 */ 12,
/* 160 */ 12,/* 161 */ 12,/* 162 */ 12,/* 163 */ 12,/* 164 */ 12,/* 165 */ 12,/* 166 */ 12,/* 167 */ 12,/* 168 */ 12,/* 169 */ 12,
/* 170 */ 12,/* 171 */ 12,/* 172 */ 12,/* 173 */ 12,/* 174 */ 12,/* 175 */ 12,/* 176 */ 12,/* 177 */ 12,/* 178 */ 12,/* 179 */ 12,
/* 180 */ 13,/* 181 */ 13,/* 182 */ 13,/* 183 */ 13,/* 184 */ 13,/* 185 */ 13,/* 186 */ 13,/* 187 */ 13,/* 188 */ 13,/* 189 */ 13,
/* 190 */ 13,/* 191 */ 13,/* 192 */ 13,/* 193 */ 13,/* 194 */ 13,/* 195 */ 13,/* 196 */ 13,/* 197 */ 13,/* 198 */ 13,/* 199 */ 13,
/* 200 */ 13,/* 201 */ 13,/* 202 */ 13,/* 203 */ 13,/* 204 */ 13,/* 205 */ 13,/* 206 */ 13,/* 207 */ 13,/* 208 */ 13,/* 209 */ 13,
/* 210 */ 13,/* 211 */ 13,/* 212 */ 13,/* 213 */ 13,/* 214 */ 13,/* 215 */ 13,/* 216 */ 13,/* 217 */ 13,/* 218 */ 13,/* 219 */ 13,
/* 220 */ 14,/* 221 */ 14,/* 222 */ 14,/* 223 */ 14,/* 224 */ 14,/* 225 */ 14,/* 226 */ 14,/* 227 */ 14,/* 228 */ 14,/* 229 */ 14,
/* 230 */ 14,/* 231 */ 14,/* 232 */ 14,/* 233 */ 14,/* 234 */ 14,/* 235 */ 14,/* 236 */ 14,/* 237 */ 14,/* 238 */ 14,/* 239 */ 14,
/* 240 */ 14,/* 241 */ 14,/* 242 */ 14,/* 243 */ 14,/* 244 */ 14,/* 245 */ 14,/* 246 */ 14,/* 247 */ 14,/* 248 */ 14,/* 249 */ 14,
/* 250 */ 15,/* 251 */ 15,/* 252 */ 15,/* 253 */ 15,/* 254 */ 15,/* 255 */ 15,/* 256 */ 15,/* 257 */ 15,/* 258 */ 15,/* 259 */ 15,
/* 260 */ 15,/* 261 */ 15,/* 262 */ 15,/* 263 */ 15,/* 264 */ 15,/* 265 */ 15,/* 266 */ 15,/* 267 */ 15,/* 268 */ 15,/* 269 */ 15,
/* 270 */ 15,/* 271 */ 15,/* 272 */ 15,/* 273 */ 15,/* 274 */ 15,/* 275 */ 15,/* 276 */ 15,/* 277 */ 15,/* 278 */ 15,/* 279 */ 15,
/* 280 */ 16,/* 281 */ 16,/* 282 */ 16,/* 283 */ 16,/* 284 */ 16,/* 285 */ 16,/* 286 */ 16,/* 287 */ 16,/* 288 */ 16,/* 289 */ 16,
/* 290 */ 16,/* 291 */ 16,/* 292 */ 16,/* 293 */ 16,/* 294 */ 16,/* 295 */ 16,/* 296 */ 16,/* 297 */ 16,/* 298 */ 16,/* 299 */ 16,
/* 300 */ 16,/* 301 */ 16,/* 302 */ 16,/* 303 */ 16,/* 304 */ 16,/* 305 */ 16,/* 306 */ 16,/* 307 */ 16,/* 308 */ 16,/* 309 */ 16,
/* 310 */ 17,/* 311 */ 17,/* 312 */ 17,/* 313 */ 17,/* 314 */ 17,/* 315 */ 17,/* 316 */ 17,/* 317 */ 17,/* 318 */ 17,/* 319 */ 17,
/* 320 */ 17,/* 321 */ 17,/* 322 */ 17,/* 323 */ 17,/* 324 */ 17,/* 325 */ 17,/* 326 */ 17,/* 327 */ 17,/* 328 */ 17,/* 329 */ 17,
/* 330 */ 17,/* 331 */ 17,/* 332 */ 17,/* 333 */ 17,/* 334 */ 17,/* 335 */ 17,/* 336 */ 17,/* 337 */ 17,/* 338 */ 17,/* 339 */ 17,
/* 340 */ 17,/* 341 */ 17,/* 342 */ 17,/* 343 */ 17,/* 344 */ 17,/* 345 */ 17,/* 346 */ 17,/* 347 */ 17,/* 348 */ 17,/* 349 */ 17,
/* 350 */ 17,/* 351 */ 17,/* 352 */ 17,/* 353 */ 17,/* 354 */ 17,/* 355 */ 17,/* 356 */ 17,/* 357 */ 17,/* 358 */ 17,/* 359 */ 17,
/* 360 */ 17,/* 361 */ 17,/* 362 */ 17,/* 363 */ 17,/* 364 */ 17,/* 365 */ 17,/* 366 */ 17,/* 367 */ 17,/* 368 */ 17,/* 369 */ 17,
/* 370 */ 17,/* 371 */ 17,/* 372 */ 17,/* 373 */ 17,/* 374 */ 17,/* 375 */ 17,/* 376 */ 17,/* 377 */ 17,/* 378 */ 17,/* 379 */ 17,
/* 380 */ 17,/* 381 */ 17,/* 382 */ 17,/* 383 */ 17,/* 384 */ 17,/* 385 */ 17,/* 386 */ 17,/* 387 */ 17,/* 388 */ 17,/* 389 */ 17,
/* 390 */ 17,/* 391 */ 17,/* 392 */ 17,/* 393 */ 17,/* 394 */ 17,/* 395 */ 17,/* 396 */ 17,/* 397 */ 17,/* 398 */ 17,/* 399 */ 17,
/* 400 */ 17,/* 401 */ 17,/* 402 */ 17,/* 403 */ 17,/* 404 */ 17,/* 405 */ 17,/* 406 */ 17,/* 407 */ 17,/* 408 */ 17,/* 409 */ 17,
/* 410 */ 17,/* 411 */ 17,/* 412 */ 17,/* 413 */ 17,/* 414 */ 17,/* 415 */ 17,/* 416 */ 17,/* 417 */ 17,/* 418 */ 17,/* 419 */ 17,
/* 420 */ 17,/* 421 */ 17,/* 422 */ 17,/* 423 */ 17,/* 424 */ 17,/* 425 */ 17,/* 426 */ 17,/* 427 */ 17,/* 428 */ 17,/* 429 */ 17,
/* 430 */ 17,/* 431 */ 17,/* 432 */ 17,/* 433 */ 17,/* 434 */ 17,/* 435 */ 17,/* 436 */ 17,/* 437 */ 17,/* 438 */ 17,/* 439 */ 17,
/* 440 */ 17,/* 441 */ 17,/* 442 */ 17,/* 443 */ 17,/* 444 */ 17,/* 445 */ 17,/* 446 */ 17,/* 447 */ 17,/* 448 */ 17,/* 449 */ 17,
/* 450 */ 18,/* 451 */ 18,/* 452 */ 18,/* 453 */ 18,/* 454 */ 18,/* 455 */ 18,/* 456 */ 18,/* 457 */ 18,/* 458 */ 18,/* 459 */ 18,
/* 460 */ 18,/* 461 */ 18,/* 462 */ 18,/* 463 */ 18,/* 464 */ 18,/* 465 */ 18,/* 466 */ 18,/* 467 */ 18,/* 468 */ 18,/* 469 */ 18,
/* 470 */ 18,/* 471 */ 18,/* 472 */ 18,/* 473 */ 18,/* 474 */ 18,/* 475 */ 18,/* 476 */ 18,/* 477 */ 18,/* 478 */ 18,/* 479 */ 18,
/* 480 */ 18,/* 481 */ 18,/* 482 */ 18,/* 483 */ 18,/* 484 */ 18,/* 485 */ 18,/* 486 */ 18,/* 487 */ 18,/* 488 */ 18,/* 489 */ 18,
/* 490 */ 18,/* 491 */ 18,/* 492 */ 18,/* 493 */ 18,/* 494 */ 18,/* 495 */ 18,/* 496 */ 18,/* 497 */ 18,/* 498 */ 18,/* 499 */ 18,
/* 500 */ 18,/* 501 */ 18,/* 502 */ 18,/* 503 */ 18,/* 504 */ 18,/* 505 */ 18,/* 506 */ 18,/* 507 */ 18,/* 508 */ 18,/* 509 */ 18,
/* 510 */ 18,/* 511 */ 18,/* 512 */ 18,/* 513 */ 18,/* 514 */ 18,/* 515 */ 18,/* 516 */ 18,/* 517 */ 18,/* 518 */ 18,/* 519 */ 18,
/* 520 */ 18,/* 521 */ 18,/* 522 */ 18,/* 523 */ 18,/* 524 */ 18,/* 525 */ 18,/* 526 */ 18,/* 527 */ 18,/* 528 */ 18,/* 529 */ 18,
/* 530 */ 18,/* 531 */ 18,/* 532 */ 18,/* 533 */ 18,/* 534 */ 18,/* 535 */ 18,/* 536 */ 18,/* 537 */ 18,/* 538 */ 18,/* 539 */ 18,
/* 540 */ 18,/* 541 */ 18,/* 542 */ 18,/* 543 */ 18,/* 544 */ 18,/* 545 */ 18,/* 546 */ 18,/* 547 */ 18,/* 548 */ 18,/* 549 */ 18,
/* 550 */ 18,/* 551 */ 18,/* 552 */ 18,/* 553 */ 18,/* 554 */ 18,/* 555 */ 18,/* 556 */ 18,/* 557 */ 18,/* 558 */ 18,/* 559 */ 18,
/* 560 */ 18,/* 561 */ 18,/* 562 */ 18,/* 563 */ 18,/* 564 */ 18,/* 565 */ 18,/* 566 */ 18,/* 567 */ 18,/* 568 */ 18,/* 569 */ 18,
/* 570 */ 18,/* 571 */ 18,/* 572 */ 18,/* 573 */ 18,/* 574 */ 18,/* 575 */ 18,/* 576 */ 18,/* 577 */ 18,/* 578 */ 18,/* 579 */ 18,
/* 580 */ 18,/* 581 */ 18,/* 582 */ 18,/* 583 */ 18,/* 584 */ 18,/* 585 */ 18,/* 586 */ 18,/* 587 */ 18,
/* 588 */ 19,/* 589 */ 19,/* 590 */ 19,/* 591 */ 19,/* 592 */ 19,/* 593 */ 19,/* 594 */ 19,/* 595 */ 19,/* 596 */ 19,/* 597 */ 19,
/* 598 */ 19,/* 599 */ 19,/* 600 */ 19,/* 601 */ 19,/* 602 */ 19,/* 603 */ 19,/* 604 */ 19,/* 605 */ 19,/* 606 */ 19,/* 607 */ 19,
/* 608 */ 19,/* 609 */ 19,/* 610 */ 19,/* 611 */ 19,/* 612 */ 19,/* 613 */ 19,/* 614 */ 19,/* 615 */ 19,/* 616 */ 19,/* 617 */ 19,
/* 618 */ 19,/* 619 */ 19,/* 620 */ 19,/* 621 */ 19,/* 622 */ 19,/* 623 */ 19,/* 624 */ 19,/* 625 */ 19,/* 626 */ 19,/* 627 */ 19,
/* 628 */ 19,/* 629 */ 19,/* 630 */ 19,/* 631 */ 19,/* 632 */ 19,/* 633 */ 19,/* 634 */ 19,/* 635 */ 19,/* 636 */ 19,/* 637 */ 19,
/* 638 */ 19,/* 639 */ 19,/* 640 */ 19,/* 641 */ 19,/* 642 */ 19,/* 643 */ 19,/* 644 */ 19,/* 645 */ 19,/* 646 */ 19,/* 647 */ 19,
/* 648 */ 19,/* 649 */ 19
};
#define FREQ_50MHZ (50)
#define FREQ_TO_IDX_0_TO_49MHZ (0)
#define FREQ_TO_IDX_650_TO_1800MHZ (20)
int r820t_freq_get_idx(uint32_t freq_mhz)
{
uint32_t freq_mhz_fix;
if(freq_mhz < FREQ_50MHZ)
{
/* Frequency Less than 50MHz */
return FREQ_TO_IDX_0_TO_49MHZ;
}else
{
/* Frequency Between 50 to 649MHz use table */
/* Fix the frequency for the table */
freq_mhz_fix = freq_mhz - FREQ_50MHZ;
if(freq_mhz_fix < FREQ_TO_IDX_SIZE)
{
return freq_to_idx[freq_mhz_fix];
}else
{
/* Frequency Between 650 to 1800MHz */
return FREQ_TO_IDX_650_TO_1800MHZ;
}
}
}
static inline bool r820t_is_power_enabled(void)
{
return true;
}
/*
* Write regs 5 to 32 (R820T_INIT_NB_REGS values) using data parameter and write last reg to 0
*/
void airspy_r820t_write_init(r820t_priv_t *priv, const uint8_t* data)
{
for (int i = 0; i < R820T_INIT_NB_REGS; i++) {
priv->write_reg(i+REG_SHADOW_START, data[i], priv->ctx);
}
priv->write_reg(0x1F, 0, priv->ctx);
}
/*
* Read from one or more contiguous registers. data[0] should be the first
* register number, one or more values follow.
*/
const uint8_t lut[16] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf };
static uint8_t r82xx_bitrev(uint8_t byte)
{
return (lut[byte & 0xf] << 4) | lut[byte >> 4];
}
static int r820t_write_reg(r820t_priv_t *priv, uint8_t reg, uint8_t val)
{
if (r820t_read_cache_reg(priv, reg) == val)
return 0;
priv->write_reg(reg, val, priv->ctx);
priv->regs[reg - REG_SHADOW_START] = val;
return 0;
}
static int r820t_read_cache_reg(r820t_priv_t *priv, int reg)
{
reg -= REG_SHADOW_START;
if (reg >= 0 && reg < NUM_REGS)
return priv->regs[reg];
else
return -1;
}
static int r820t_write_reg_mask(r820t_priv_t *priv, uint8_t reg, uint8_t val, uint8_t bit_mask)
{
int rc = r820t_read_cache_reg(priv, reg);
if (rc < 0)
return rc;
val = (rc & ~bit_mask) | (val & bit_mask);
return r820t_write_reg(priv, reg, val);
}
static int r820t_read(r820t_priv_t *priv, uint8_t *val, int len)
{
/* reg not used and assumed to be always 0 because start from reg0 to reg0+len */
priv->read_reg(val, len, priv->ctx);
return 0;
}
/*
* r820t tuning logic
*/
#ifdef OPTIM_SET_MUX
int r820t_set_mux_freq_idx = -1; /* Default set to invalid value in order to force set_mux */
#endif
/*
"inspired by Mauro Carvalho Chehab set_mux technique"
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
part of r820t_set_mux() (set tracking filter)
*/
static int r820t_set_tf(r820t_priv_t *priv, uint32_t freq)
{
const struct r820t_freq_range *range;
int freq_idx;
int rc = 0;
/* Get the proper frequency range in MHz instead of Hz */
/* Fast divide freq by 1000000 */
freq = (uint32_t)((uint64_t)freq * 4295 >> 32);
freq_idx = r820t_freq_get_idx(freq);
range = &freq_ranges[freq_idx];
/* Only reconfigure mux freq if modified vs previous range */
#ifdef OPTIM_SET_MUX
if(freq_idx != r820t_set_mux_freq_idx)
{
#endif
/* Open Drain */
rc = r820t_write_reg_mask(priv, 0x17, range->open_d, 0x08);
if (rc < 0)
return rc;
/* RF_MUX,Polymux */
rc = r820t_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3);
if (rc < 0)
return rc;
/* TF BAND */
rc = r820t_write_reg(priv, 0x1b, range->tf_c);
if (rc < 0)
return rc;
/* XTAL CAP & Drive */
rc = r820t_write_reg_mask(priv, 0x10, 0x08, 0x0b);
if (rc < 0)
return rc;
rc = r820t_write_reg_mask(priv, 0x08, 0x00, 0x3f);
if (rc < 0)
return rc;
rc = r820t_write_reg_mask(priv, 0x09, 0x00, 0x3f);
#ifdef OPTIM_SET_MUX
}
r820t_set_mux_freq_idx = freq_idx;
#endif
return rc;
}
int r820t_set_pll(r820t_priv_t *priv, uint32_t freq)
{
const uint32_t vco_min = 1770000000;
const uint32_t vco_max = 3900000000;
uint32_t ref = priv->xtal_freq >> 1;
int rc;
uint32_t div_num;
uint32_t vco;
uint32_t rem;
uint32_t mask;
uint16_t sdm;
uint8_t nint;
uint8_t ni;
uint8_t si;
uint8_t div_found;
/* Find a suitable divider */
div_found = 0;
for (div_num = 0; div_num <= 5; div_num++)
{
vco = freq << (div_num + 1);
if (vco >= vco_min && vco <= vco_max)
{
div_found = 1;
break;
}
}
if (!div_found)
return -1;
vco += ref >> 16;
ref <<= 8;
mask = 1 << 23;
rem = 0;
while (mask > 0 && vco > 0)
{
if (vco >= ref)
{
rem |= mask;
vco -= ref;
}
ref >>= 1;
mask >>= 1;
}
nint = rem >> 16;
sdm = rem & 0xffff;
nint -= 13;
ni = nint >> 2;
si = nint & 3;
/* Set the phase splitter */
rc = r820t_write_reg_mask(priv, 0x10, (uint8_t) (div_num << 5), 0xe0);
if(rc < 0)
return rc;
/* Set the integer part of the PLL */
rc = r820t_write_reg(priv, 0x14, (uint8_t) (ni + (si << 6)));
if(rc < 0)
return rc;
if (sdm == 0)
{
/* Disable SDM */
rc = r820t_write_reg_mask(priv, 0x12, 0x08, 0x08);
if(rc < 0)
return rc;
}
else
{
/* Write SDM */
rc = r820t_write_reg(priv, 0x15, (uint8_t)(sdm & 0xff));
if (rc < 0)
return rc;
rc = r820t_write_reg(priv, 0x16, (uint8_t)(sdm >> 8));
if (rc < 0)
return rc;
/* Enable SDM */
rc = r820t_write_reg_mask(priv, 0x12, 0x00, 0x08);
if (rc < 0)
return rc;
}
return rc;
}
int r820t_set_freq(r820t_priv_t *priv, uint32_t freq)
{
int rc;
uint32_t lo_freq = freq + priv->if_freq;
rc = r820t_set_tf(priv, freq);
if (rc < 0)
return rc;
rc = r820t_set_pll(priv, lo_freq);
if (rc < 0)
return rc;
priv->freq = freq;
return 0;
}
int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index)
{
return r820t_write_reg_mask(priv, 0x05, gain_index, 0x0f);
}
int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index)
{
return r820t_write_reg_mask(priv, 0x07, gain_index, 0x0f);
}
int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index)
{
return r820t_write_reg_mask(priv, 0x0c, gain_index, 0x0f);
}
int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value)
{
value = value != 0 ? 0x00 : 0x10;
return r820t_write_reg_mask(priv, 0x05, value, 0x10);
}
int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value)
{
value = value != 0 ? 0x10 : 0x00;
return r820t_write_reg_mask(priv, 0x07, value, 0x10);
}
/*
"inspired by Mauro Carvalho Chehab calibration technique"
https://stuff.mit.edu/afs/sipb/contrib/linux/drivers/media/tuners/r820t.c
part of r820t_set_tv_standard()
*/
int r820t_calibrate(r820t_priv_t *priv)
{
int i, rc, cal_code;
uint8_t data[5];
for (i = 0; i < 5; i++)
{
/* Set filt_cap */
rc = r820t_write_reg_mask(priv, 0x0b, 0x08, 0x60);
if (rc < 0)
return rc;
/* set cali clk =on */
rc = r820t_write_reg_mask(priv, 0x0f, 0x04, 0x04);
if (rc < 0)
return rc;
/* X'tal cap 0pF for PLL */
rc = r820t_write_reg_mask(priv, 0x10, 0x00, 0x03);
if (rc < 0)
return rc;
rc = r820t_set_pll(priv, CALIBRATION_LO * 1000);
if (rc < 0)
return rc;
/* Start Trigger */
rc = r820t_write_reg_mask(priv, 0x0b, 0x10, 0x10);
if (rc < 0)
return rc;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
/* Stop Trigger */
rc = r820t_write_reg_mask(priv, 0x0b, 0x00, 0x10);
if (rc < 0)
return rc;
/* set cali clk =off */
rc = r820t_write_reg_mask(priv, 0x0f, 0x00, 0x04);
if (rc < 0)
return rc;
/* Check if calibration worked */
rc = r820t_read(priv, data, sizeof(data));
if (rc < 0)
return rc;
cal_code = data[4] & 0x0f;
if (cal_code && cal_code != 0x0f)
return 0;
}
return -1;
}
int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx)
{
int rc;
uint32_t saved_freq;
r820t_state_standby = 0;
priv->if_freq = if_freq;
priv->write_reg = write_reg;
priv->read_reg = read_reg;
priv->ctx = ctx;
/* Initialize registers */
airspy_r820t_write_init(priv, priv->regs);
r820t_set_freq(priv, priv->freq);
/* Calibrate the IF filter */
saved_freq = priv->freq;
rc = r820t_calibrate(priv);
priv->freq = saved_freq;
if (rc < 0)
{
saved_freq = priv->freq;
r820t_calibrate(priv);
priv->freq = saved_freq;
}
/* Restore freq as it has been modified by r820t_calibrate() */
rc = r820t_set_freq(priv, priv->freq);
return rc;
}
void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw)
{
const uint8_t modes[] = { 0xE0, 0x80, 0x60, 0x00 };
const uint8_t opt[] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
uint8_t a = 0xB0 | opt[bw & 0x0F];
uint8_t b = 0x0F | modes[bw >> 4];
r820t_write_reg(priv, 0x0A, a);
r820t_write_reg(priv, 0x0B, b);
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2013-2016 Benjamin Vernoux <bvernoux@airspy.com>
*
* This file is part of AirSpy.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <stdint.h>
#define REG_SHADOW_START 5
#define NUM_REGS 30
/* R820T Clock */
#define CALIBRATION_LO 88000
typedef void (*r820t_write_reg_f)(uint8_t reg, uint8_t value, void* ctx);
typedef void (*r820t_read_f)(uint8_t* data, int len, void* ctx);
typedef struct
{
uint32_t xtal_freq; /* XTAL_FREQ_HZ */
uint32_t freq;
uint32_t if_freq;
uint8_t regs[NUM_REGS];
uint16_t padding;
r820t_write_reg_f write_reg;
r820t_read_f read_reg;
void* ctx;
} r820t_priv_t;
void airspy_r820t_write_single(r820t_priv_t *priv, uint8_t reg, uint8_t val);
uint8_t airspy_r820t_read_single(r820t_priv_t *priv, uint8_t reg);
int r820t_init(r820t_priv_t *priv, const uint32_t if_freq, r820t_write_reg_f write_reg, r820t_read_f read_reg, void* ctx);
int r820t_set_freq(r820t_priv_t *priv, uint32_t freq);
int r820t_set_lna_gain(r820t_priv_t *priv, uint8_t gain_index);
int r820t_set_mixer_gain(r820t_priv_t *priv, uint8_t gain_index);
int r820t_set_vga_gain(r820t_priv_t *priv, uint8_t gain_index);
int r820t_set_lna_agc(r820t_priv_t *priv, uint8_t value);
int r820t_set_mixer_agc(r820t_priv_t *priv, uint8_t value);
void r820t_set_if_bandwidth(r820t_priv_t *priv, uint8_t bw);

View File

@@ -10,7 +10,6 @@
#include <libbladeRF.h> #include <libbladeRF.h>
#include <gui/smgui.h> #include <gui/smgui.h>
#include <algorithm> #include <algorithm>
#include <utils/optionlist.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
@@ -38,10 +37,6 @@ public:
BladeRFSourceModule(std::string name) { BladeRFSourceModule(std::string name) {
this->name = name; this->name = name;
// Define clocks
clocks.define("onboard", "On-Board", CLOCK_SELECT_ONBOARD);
clocks.define("external", "External", CLOCK_SELECT_EXTERNAL);
sampleRate = 1000000.0; sampleRate = 1000000.0;
handler.ctx = this; handler.ctx = this;
@@ -272,15 +267,6 @@ public:
} }
config.release(true); config.release(true);
// Load clock source
clkId = clocks.keyId("onboard");
if (config.conf["devices"][selectedSerial].contains("clock")) {
std::string clkStr = config.conf["devices"][selectedSerial]["clock"];
if (clocks.keyExists(clkStr)) {
clkId = clocks.keyId(clkStr);
}
}
// Load gain mode // Load gain mode
if (config.conf["devices"][selectedSerial].contains("gainMode")) { if (config.conf["devices"][selectedSerial].contains("gainMode")) {
std::string gm = config.conf["devices"][selectedSerial]["gainMode"]; std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
@@ -378,7 +364,6 @@ private:
if (_this->bufferSize < 1024) { _this->bufferSize = 1024; } if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
// Setup device parameters // Setup device parameters
_this->setClockSource(_this->clocks[_this->clkId]);
bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL); bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL);
bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq); bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL); bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
@@ -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 // General config BS
SmGui::LeftLabel("Gain control mode"); SmGui::LeftLabel("Gain control mode");
SmGui::FillWidth(); 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() { void worker() {
int16_t* buffer = new int16_t[bufferSize * 2]; int16_t* buffer = new int16_t[bufferSize * 2];
bladerf_metadata meta; bladerf_metadata meta;
@@ -602,7 +565,6 @@ private:
int devId = 0; int devId = 0;
int srId = 0; int srId = 0;
int bwId = 0; int bwId = 0;
int clkId = 0;
int chanId = 0; int chanId = 0;
int gainMode = 0; int gainMode = 0;
bool streamingEnabled = false; bool streamingEnabled = false;
@@ -618,8 +580,8 @@ private:
std::string sampleRatesTxt; std::string sampleRatesTxt;
std::vector<uint64_t> bandwidths; std::vector<uint64_t> bandwidths;
std::string bandwidthsTxt; std::string bandwidthsTxt;
std::string channelNamesTxt; std::string channelNamesTxt;
OptionList<std::string, bladerf_clock_select> clocks;
int bufferSize; int bufferSize;
struct bladerf_stream* rxStream; struct bladerf_stream* rxStream;

View File

@@ -1,28 +0,0 @@
cmake_minimum_required(VERSION 3.13)
project(dragonlabs_source)
file(GLOB SRC "src/*.cpp")
include(${SDRPP_MODULE_CMAKE})
if (MSVC)
# Debugging only
find_package(PkgConfig REQUIRED)
pkg_check_modules(libusb REQUIRED IMPORTED_TARGET libusb-1.0)
target_link_libraries(dragonlabs_source PRIVATE PkgConfig::libusb)
target_include_directories(dragonlabs_source PRIVATE "C:/Users/ryzerth/Documents/DragonLabs/products/KrakenSlayer/host/src")
target_include_directories(dragonlabs_source PRIVATE "C:/Program Files/DragonLabs/CR/include")
target_link_directories(dragonlabs_source PRIVATE "C:/Program Files/DragonLabs/CR/lib")
target_link_libraries(dragonlabs_source PRIVATE dlcr)
elseif (ANDROID)
# Not supported yet...
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBDLCR REQUIRED libdlcr)
target_include_directories(dragonlabs_source PRIVATE ${LIBDLCR_INCLUDE_DIRS})
target_link_directories(dragonlabs_source PRIVATE ${LIBDLCR_LIBRARY_DIRS})
target_link_libraries(dragonlabs_source PRIVATE ${LIBDLCR_LIBRARIES})
endif ()

View File

@@ -1,453 +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 <fstream>
#include <dlcr.h>
#ifdef DRAGONLABS_SOURCE_ENABLE_DEBUG
#include <dlcr_internal.h>
#include <lmx2572.h>
#endif
SDRPP_MOD_INFO{
/* Name: */ "dragonlabs_source",
/* Description: */ "Dragon Labs Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
#define CONCAT(a, b) ((std::string(a) + b).c_str())
class DragonLabsSourceModule : public ModuleManager::Instance {
public:
DragonLabsSourceModule(std::string name) {
this->name = name;
// Load the register debugging values
strcpy(clkRegStr, "00");
strcpy(clkValStr, "--");
strcpy(synRegStr, "00");
strcpy(synValStr, "--");
strcpy(adcRegStr, "00");
strcpy(adcValStr, "--");
strcpy(tunRegStr, "00");
strcpy(tunValStr, "--");
// Define the clock sources
clockSources.define("internal", "Internal", DLCR_CLOCK_INTERNAL);
clockSources.define("external", "External", DLCR_CLOCK_EXTERNAL);
// Define the channels
channels.define("chan1", "Channel 1", 0);
channels.define("chan2", "Channel 2", 1);
channels.define("chan3", "Channel 3", 2);
channels.define("chan4", "Channel 4", 3);
channels.define("chan5", "Channel 5", 4);
channels.define("chan6", "Channel 6", 5);
channels.define("chan7", "Channel 7", 6);
channels.define("chan8", "Channel 8", 7);
// Hardcode the samplerate
sampleRate = 12.5e6;
// Fill out the source handler
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &stream;
// Refresh devices
refresh();
// Select first (TODO: Select from config)
select("");
sigpath::sourceManager.registerSource("Dragon Labs", &handler);
}
~DragonLabsSourceModule() {
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
void refresh() {
devices.clear();
// Get the list of devices
dlcr_info_t* list;
int count = dlcr_list_devices(&list);
if (count < 0) {
flog::error("Failed to list devices");
return;
}
// Generate the menu list
for (int i = 0; i < count; i++) {
// Format device name
std::string devName = "CR8-1725 ";
devName += " [";
devName += list[i].serial;
devName += ']';
// Save device
devices.define(list[i].serial, devName, list[i].serial);
}
// Free the device list
dlcr_free_device_list(list);
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Save serial number
selectedSerial = serial;
}
static void menuSelected(void* ctx) {
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("DragonLabsSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
flog::info("DragonLabsSourceModule '{0}': Menu Deselect!", _this->name);
}
std::chrono::high_resolution_clock::time_point last;
static void start(void* ctx) {
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
if (_this->running) { return; }
// Open the device
int err = dlcr_open(&_this->openDev, _this->selectedSerial.c_str());
if (err) {
flog::error("Failed to open device '{}': {}", _this->selectedSerial, err);
return;
}
flog::debug("Device open");
// Configure the device
dlcr_set_freq(_this->openDev, DLCR_CHAN_ALL, _this->freq, true);
dlcr_set_lna_gain(_this->openDev, DLCR_CHAN_ALL, _this->lnaGain);
dlcr_set_mixer_gain(_this->openDev, DLCR_CHAN_ALL, _this->mixerGain);
dlcr_set_vga_gain(_this->openDev, DLCR_CHAN_ALL, _this->vgaGain);
// Start device
dlcr_start(_this->openDev, 0/*TODO*/, callback, _this);
_this->running = true;
flog::info("DragonLabsSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop device
dlcr_stop(_this->openDev);
// Close device
dlcr_close(_this->openDev);
flog::info("DragonLabsSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
if (_this->running) {
dlcr_set_freq(_this->openDev, DLCR_CHAN_ALL, freq, _this->docal);
}
_this->calibrated = false;
_this->freq = freq;
flog::info("DragonLabsSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
double lmxFreq = 100e6;
static void menuHandler(void* ctx) {
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_dlcr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_dlcr_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("Clock Source");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_dlcr_clock_", _this->name), &_this->clockSourceId, _this->clockSources.txt)) {
if (_this->running) {
dlcr_set_clock_source(_this->openDev, _this->clockSources[_this->clockSourceId]);
}
// TODO: Save
}
SmGui::LeftLabel("Channel");
SmGui::FillWidth();
SmGui::Combo(CONCAT("##_dlcr_chan_", _this->name), &_this->channelId, _this->channels.txt);
SmGui::LeftLabel("LNA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_dlcr_lna_gain_", _this->name), &_this->lnaGain, 0, 14)) {
if (_this->running) {
dlcr_set_lna_gain(_this->openDev, DLCR_CHAN_ALL, _this->lnaGain);
}
// TODO: Save
}
SmGui::LeftLabel("Mixer Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_dlcr_mixer_gain_", _this->name), &_this->mixerGain, 0, 15)) {
if (_this->running) {
dlcr_set_mixer_gain(_this->openDev, DLCR_CHAN_ALL, _this->mixerGain);
}
// TODO: Save
}
SmGui::LeftLabel("VGA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_dlcr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
if (_this->running) {
dlcr_set_vga_gain(_this->openDev, DLCR_CHAN_ALL, _this->vgaGain);
}
// TODO: Save
}
#ifdef DRAGONLABS_SOURCE_ENABLE_DEBUG
SmGui::Checkbox(CONCAT("Debug##_dlcr_debug_", _this->name), &_this->debug);
if (_this->debug) {
ImGui::Separator();
SmGui::LeftLabel("Clockgen Reg");
SmGui::FillWidth();
SmGui::InputText(CONCAT("##_dlcr_clk_reg_", _this->name), _this->clkRegStr, 256);
SmGui::LeftLabel("Clockgen Value");
SmGui::FillWidth();
SmGui::InputText(CONCAT("##_dlcr_clk_val_", _this->name), _this->clkValStr, 256);
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Read##_dlcr_clk_rd_", _this->name))) {
if (_this->running) {
uint8_t val;
dlcr_si5351c_read_reg(_this->openDev, std::stoi(_this->clkRegStr, NULL, 16), &val);
sprintf(_this->clkValStr, "%02X", val);
}
}
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Write##_dlcr_clk_wr_", _this->name))) {
if (_this->running) {
dlcr_si5351c_write_reg(_this->openDev, std::stoi(_this->clkRegStr, NULL, 16), std::stoi(_this->clkValStr, NULL, 16));
}
}
ImGui::Separator();
SmGui::LeftLabel("Synth Reg");
SmGui::FillWidth();
SmGui::InputText(CONCAT("##_dlcr_syn_reg_", _this->name), _this->synRegStr, 256);
SmGui::LeftLabel("Synth Value");
SmGui::FillWidth();
SmGui::InputText(CONCAT("##_dlcr_syn_val_", _this->name), _this->synValStr, 256);
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Read##_dlcr_syn_rd_", _this->name))) {
if (_this->running) {
uint16_t val;
dlcr_lmx2572_read_reg(_this->openDev, std::stoi(_this->synRegStr, NULL, 16), &val);
sprintf(_this->synValStr, "%02X", val);
}
}
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Write##_dlcr_syn_wr_", _this->name))) {
if (_this->running) {
dlcr_lmx2572_write_reg(_this->openDev, std::stoi(_this->synRegStr, NULL, 16), std::stoi(_this->synValStr, NULL, 16));
}
}
ImGui::Separator();
SmGui::LeftLabel("ADC Reg");
SmGui::FillWidth();
SmGui::InputText(CONCAT("##_dlcr_adc_reg_", _this->name), _this->adcRegStr, 256);
SmGui::LeftLabel("ADC Value");
SmGui::FillWidth();
SmGui::InputText(CONCAT("##_dlcr_adc_val_", _this->name), _this->adcValStr, 256);
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Read##_dlcr_adc_rd_", _this->name))) {
if (_this->running) {
uint8_t val;
dlcr_mcp37211_read_reg(_this->openDev, std::stoi(_this->adcRegStr, NULL, 16), &val);
sprintf(_this->adcValStr, "%02X", val);
}
}
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Write##_dlcr_adc_wr_", _this->name))) {
if (_this->running) {
dlcr_mcp37211_write_reg(_this->openDev, std::stoi(_this->adcRegStr, NULL, 16), std::stoi(_this->adcValStr, NULL, 16));
}
}
ImGui::Separator();
SmGui::LeftLabel("Tuner Reg");
SmGui::FillWidth();
SmGui::InputText(CONCAT("##_dlcr_tun_reg_", _this->name), _this->tunRegStr, 256);
SmGui::LeftLabel("Tuner Value");
SmGui::FillWidth();
SmGui::InputText(CONCAT("##_dlcr_tun_val_", _this->name), _this->tunValStr, 256);
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Read##_dlcr_tun_rd_", _this->name))) {
if (_this->running) {
uint8_t val[8];
dlcr_r860_read_reg(_this->openDev, DLCR_TUNER_ALL, std::stoi(_this->tunRegStr, NULL, 16), val);
sprintf(_this->tunValStr, "%02X", val[0]);
for (int i = 0; i < 8; i++) {
printf("TUNER[%d][0x%02X] = 0x%02X\n", i, std::stoi(_this->tunRegStr, NULL, 16), val[i]);
}
}
}
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Write##_dlcr_tun_wr_", _this->name))) {
if (_this->running) {
dlcr_r860_write_reg(_this->openDev, DLCR_TUNER_ALL, std::stoi(_this->tunRegStr, NULL, 16), std::stoi(_this->tunValStr, NULL, 16));
}
}
ImGui::Separator();
SmGui::LeftLabel("Synth Freq");
SmGui::FillWidth();
ImGui::InputDouble("##_dlcr_synth_freq", &_this->synthFreq);
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Tune##_dlcr_synth_freq", _this->name))) {
if (_this->running) {
lmx2572_tune(_this->openDev, _this->synthFreq);
}
}
SmGui::FillWidth();
if (SmGui::Button(CONCAT("Shutdown##_dlcr_synth_freq", _this->name))) {
if (_this->running) {
lmx2572_shutdown(_this->openDev);
}
}
ImGui::Separator();
SmGui::Checkbox("Calibrate##_dlcr_cal", &_this->docal);
ImGui::Separator();
}
#endif
}
static void callback(dlcr_complex_t* samples[DLCR_CHANNEL_COUNT], size_t count, size_t drops, void* ctx) {
DragonLabsSourceModule* _this = (DragonLabsSourceModule*)ctx;
// Copy the data to the stream
memcpy(_this->stream.writeBuf, samples[_this->channelId], count * sizeof(dsp::complex_t));
// Send the samples
_this->stream.swap(count);
}
std::string name;
bool enabled = true;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq = 100e6;
OptionList<std::string, std::string> devices;
bool debug = false;
char clkRegStr[256];
char clkValStr[256];
char synRegStr[256];
char synValStr[256];
char adcRegStr[256];
char adcValStr[256];
char tunRegStr[256];
char tunValStr[256];
double synthFreq = 100e6;
bool docal = false;
int devId = 0;
int clockSourceId = 0;
int channelId = 0;
int lnaGain = 0;
int mixerGain = 0;
int vgaGain = 0;
std::string selectedSerial;
dlcr_t* openDev = NULL;
bool calibrated = false;
dsp::stream<dsp::complex_t> stream;
OptionList<std::string, dlcr_clock_t> clockSources;
OptionList<std::string, int> channels;
};
MOD_EXPORT void _INIT_() {
// Nothing here
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new DragonLabsSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (DragonLabsSourceModule*)instance;
}
MOD_EXPORT void _END_() {
// Nothing here
}

View File

@@ -1,36 +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-host/lib/")
target_include_directories(hydrasdr_source PUBLIC "C:/Program Files/hydrasdr-host/include/libhydrasdr")
target_link_libraries(hydrasdr_source PRIVATE hydrasdr)
elseif (ANDROID)
target_include_directories(hydrasdr_source PUBLIC
/sdr-kit/${ANDROID_ABI}/include/libhydrasdr
)
target_link_libraries(hydrasdr_source PUBLIC
/sdr-kit/${ANDROID_ABI}/lib/libusb1.0.so
/sdr-kit/${ANDROID_ABI}/lib/libhydrasdr.so
)
else (MSVC)
find_package(PkgConfig)
pkg_check_modules(LIBHYDRASDR REQUIRED libhydrasdr)
target_include_directories(hydrasdr_source PRIVATE ${LIBHYDRASDR_INCLUDE_DIRS})
target_link_directories(hydrasdr_source PRIVATE ${LIBHYDRASDR_LIBRARY_DIRS})
target_link_libraries(hydrasdr_source PRIVATE ${LIBHYDRASDR_LIBRARIES})
# Include it because for some reason pkgconfig doesn't look here?
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
target_include_directories(hydrasdr_source PRIVATE "/usr/local/include")
endif()
endif ()

View File

@@ -1,639 +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", HYDRASDR_RF_PORT_RX0);
ports.define("rx1", "RX1", HYDRASDR_RF_PORT_RX1);
ports.define("rx2", "RX2", HYDRASDR_RF_PORT_RX2);
regStr[0] = 0;
valStr[0] = 0;
sampleRate = 10000000.0;
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &stream;
refresh();
// Select device from config
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
selectByString(devSerial);
sigpath::sourceManager.registerSource("HydraSDR", &handler);
}
~HydraSDRSourceModule() {
stop(this);
sigpath::sourceManager.unregisterSource("HydraSDR");;
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
void refresh() {
#ifndef __ANDROID__
devices.clear();
uint64_t serials[256];
int n = hydrasdr_list_devices(serials, 256);
char buf[1024];
for (int i = 0; i < n; i++) {
sprintf(buf, "%016" PRIX64, serials[i]);
devices.define(buf, buf, serials[i]);
}
#else
// Check for device presence
int vid, pid;
devFd = backend::getDeviceFD(vid, pid, backend::HYDRASDR_VIDPIDS);
if (devFd < 0) { return; }
// Get device info
std::string fakeName = "HydraSDR USB";
devices.define(fakeName, 0);
#endif
}
void selectFirst() {
if (!devices.empty()) {
selectBySerial(devices.value(0));
}
}
void selectByString(std::string serial) {
if (devices.keyExists(serial)) {
selectBySerial(devices.value(devices.keyId(serial)));
return;
}
selectFirst();
}
void selectBySerial(uint64_t serial) {
hydrasdr_device* dev;
try {
#ifndef __ANDROID__
int err = hydrasdr_open_sn(&dev, serial);
#else
int err = hydrasdr_open_fd(&dev, devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, serial);
flog::error("Could not open HydraSDR {0}", buf);
selectedSerial = 0;
return;
}
}
catch (const std::exception& e) {
char buf[1024];
sprintf(buf, "%016" PRIX64, serial);
flog::error("Could not open HydraSDR {}", buf);
}
devId = devices.valueId(serial);
selectedSerial = serial;
selectedSerStr = devices.key(devId);
uint32_t sampleRates[256];
hydrasdr_get_samplerates(dev, sampleRates, 0);
int n = sampleRates[0];
hydrasdr_get_samplerates(dev, sampleRates, n);
samplerates.clear();
for (int i = 0; i < n; i++) {
samplerates.define(sampleRates[i], getBandwdithScaled(sampleRates[i]), sampleRates[i]);
}
// Load config here
config.acquire();
bool created = false;
if (!config.conf["devices"].contains(selectedSerStr)) {
created = true;
config.conf["devices"][selectedSerStr]["sampleRate"] = 10000000;
config.conf["devices"][selectedSerStr]["gainMode"] = 0;
config.conf["devices"][selectedSerStr]["sensitiveGain"] = 0;
config.conf["devices"][selectedSerStr]["linearGain"] = 0;
config.conf["devices"][selectedSerStr]["lnaGain"] = 0;
config.conf["devices"][selectedSerStr]["mixerGain"] = 0;
config.conf["devices"][selectedSerStr]["vgaGain"] = 0;
config.conf["devices"][selectedSerStr]["lnaAgc"] = false;
config.conf["devices"][selectedSerStr]["mixerAgc"] = false;
config.conf["devices"][selectedSerStr]["biasT"] = false;
}
// Load sample rate
srId = 0;
sampleRate = samplerates.value(0);
if (config.conf["devices"][selectedSerStr].contains("sampleRate")) {
int selectedSr = config.conf["devices"][selectedSerStr]["sampleRate"];
if (samplerates.keyExists(selectedSr)) {
srId = samplerates.keyId(selectedSr);
sampleRate = samplerates[srId];
}
}
// Load port
if (config.conf["devices"][selectedSerStr].contains("port")) {
std::string portStr = config.conf["devices"][selectedSerStr]["port"];
if (ports.keyExists(portStr)) {
portId = ports.keyId(portStr);
}
}
// Load gains
if (config.conf["devices"][selectedSerStr].contains("gainMode")) {
gainMode = config.conf["devices"][selectedSerStr]["gainMode"];
}
if (config.conf["devices"][selectedSerStr].contains("sensitiveGain")) {
sensitiveGain = config.conf["devices"][selectedSerStr]["sensitiveGain"];
}
if (config.conf["devices"][selectedSerStr].contains("linearGain")) {
linearGain = config.conf["devices"][selectedSerStr]["linearGain"];
}
if (config.conf["devices"][selectedSerStr].contains("lnaGain")) {
lnaGain = config.conf["devices"][selectedSerStr]["lnaGain"];
}
if (config.conf["devices"][selectedSerStr].contains("mixerGain")) {
mixerGain = config.conf["devices"][selectedSerStr]["mixerGain"];
}
if (config.conf["devices"][selectedSerStr].contains("vgaGain")) {
vgaGain = config.conf["devices"][selectedSerStr]["vgaGain"];
}
if (config.conf["devices"][selectedSerStr].contains("lnaAgc")) {
lnaAgc = config.conf["devices"][selectedSerStr]["lnaAgc"];
}
if (config.conf["devices"][selectedSerStr].contains("mixerAgc")) {
mixerAgc = config.conf["devices"][selectedSerStr]["mixerAgc"];
}
// Load Bias-T
if (config.conf["devices"][selectedSerStr].contains("biasT")) {
biasT = config.conf["devices"][selectedSerStr]["biasT"];
}
config.release(created);
hydrasdr_close(dev);
}
private:
std::string getBandwdithScaled(double bw) {
char buf[1024];
if (bw >= 1000000.0) {
sprintf(buf, "%.1lfMHz", bw / 1000000.0);
}
else if (bw >= 1000.0) {
sprintf(buf, "%.1lfKHz", bw / 1000.0);
}
else {
sprintf(buf, "%.1lfHz", bw);
}
return std::string(buf);
}
static void menuSelected(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("HydraSDRSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
flog::info("HydraSDRSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
if (_this->running) { return; }
if (_this->selectedSerial == 0) {
flog::error("Tried to start HydraSDR source with null serial");
return;
}
#ifndef __ANDROID__
int err = hydrasdr_open_sn(&_this->openDev, _this->selectedSerial);
#else
int err = hydrasdr_open_fd(&_this->openDev, _this->devFd);
#endif
if (err != 0) {
char buf[1024];
sprintf(buf, "%016" PRIX64, _this->selectedSerial);
flog::error("Could not open HydraSDR {0}", buf);
return;
}
hydrasdr_set_samplerate(_this->openDev, _this->samplerates[_this->srId]);
hydrasdr_set_freq(_this->openDev, _this->freq);
hydrasdr_set_rf_port(_this->openDev, _this->ports[_this->portId]);
if (_this->gainMode == 0) {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
}
else if (_this->gainMode == 1) {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain);
}
else if (_this->gainMode == 2) {
if (_this->lnaAgc) {
hydrasdr_set_lna_agc(_this->openDev, 1);
}
else {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (_this->mixerAgc) {
hydrasdr_set_mixer_agc(_this->openDev, 1);
}
else {
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
}
hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain);
}
hydrasdr_set_rf_bias(_this->openDev, _this->biasT);
hydrasdr_start_rx(_this->openDev, callback, _this);
_this->running = true;
flog::info("HydraSDRSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
_this->stream.stopWriter();
hydrasdr_close(_this->openDev);
_this->stream.clearWriteStop();
flog::info("HydraSDRSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
if (_this->running) {
hydrasdr_set_freq(_this->openDev, freq);
}
_this->freq = freq;
flog::info("HydraSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_hydrasdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->selectBySerial(_this->devices[_this->devId]);
core::setInputSampleRate(_this->sampleRate);
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["device"] = _this->selectedSerStr;
config.release(true);
}
}
if (SmGui::Combo(CONCAT("##_hydrasdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates[_this->srId];
core::setInputSampleRate(_this->sampleRate);
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->samplerates.key(_this->srId);
config.release(true);
}
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_hydrasdr_refr_", _this->name))) {
_this->refresh();
config.acquire();
std::string devSerial = config.conf["device"];
config.release();
_this->selectByString(devSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("Antenna Port");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_hydrasdr_port_", _this->name), &_this->portId, _this->ports.txt)) {
if (_this->running) {
hydrasdr_set_rf_port(_this->openDev, _this->ports[_this->portId]);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["port"] = _this->ports.key(_this->portId);
config.release(true);
}
}
SmGui::BeginGroup();
SmGui::Columns(3, CONCAT("HydraSDRGainModeColumns##_", _this->name), false);
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Sensitive##_hydrasdr_gm_", _this->name), _this->gainMode == 0)) {
_this->gainMode = 0;
if (_this->running) {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0;
config.release(true);
}
}
SmGui::NextColumn();
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Linear##_hydrasdr_gm_", _this->name), _this->gainMode == 1)) {
_this->gainMode = 1;
if (_this->running) {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1;
config.release(true);
}
}
SmGui::NextColumn();
SmGui::ForceSync();
if (SmGui::RadioButton(CONCAT("Free##_hydrasdr_gm_", _this->name), _this->gainMode == 2)) {
_this->gainMode = 2;
if (_this->running) {
if (_this->lnaAgc) {
hydrasdr_set_lna_agc(_this->openDev, 1);
}
else {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (_this->mixerAgc) {
hydrasdr_set_mixer_agc(_this->openDev, 1);
}
else {
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
}
hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2;
config.release(true);
}
}
SmGui::Columns(1, CONCAT("EndHydraSDRGainModeColumns##_", _this->name), false);
SmGui::EndGroup();
// Gain menus
if (_this->gainMode == 0) {
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) {
if (_this->running) {
hydrasdr_set_sensitivity_gain(_this->openDev, _this->sensitiveGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain;
config.release(true);
}
}
}
else if (_this->gainMode == 1) {
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_lin_gain_", _this->name), &_this->linearGain, 0, 21)) {
if (_this->running) {
hydrasdr_set_linearity_gain(_this->openDev, _this->linearGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain;
config.release(true);
}
}
}
else if (_this->gainMode == 2) {
// TODO: Switch to a table for alignment
if (_this->lnaAgc) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("LNA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) {
if (_this->running) {
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain;
config.release(true);
}
}
if (_this->lnaAgc) { SmGui::EndDisabled(); }
if (_this->mixerAgc) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("Mixer Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) {
if (_this->running) {
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain;
config.release(true);
}
}
if (_this->mixerAgc) { SmGui::EndDisabled(); }
SmGui::LeftLabel("VGA Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_hydrasdr_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) {
if (_this->running) {
hydrasdr_set_vga_gain(_this->openDev, _this->vgaGain);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain;
config.release(true);
}
}
// AGC Control
SmGui::ForceSync();
if (SmGui::Checkbox(CONCAT("LNA AGC##_hydrasdr_", _this->name), &_this->lnaAgc)) {
if (_this->running) {
if (_this->lnaAgc) {
hydrasdr_set_lna_agc(_this->openDev, 1);
}
else {
hydrasdr_set_lna_agc(_this->openDev, 0);
hydrasdr_set_lna_gain(_this->openDev, _this->lnaGain);
}
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc;
config.release(true);
}
}
SmGui::ForceSync();
if (SmGui::Checkbox(CONCAT("Mixer AGC##_hydrasdr_", _this->name), &_this->mixerAgc)) {
if (_this->running) {
if (_this->mixerAgc) {
hydrasdr_set_mixer_agc(_this->openDev, 1);
}
else {
hydrasdr_set_mixer_agc(_this->openDev, 0);
hydrasdr_set_mixer_gain(_this->openDev, _this->mixerGain);
}
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc;
config.release(true);
}
}
}
// Bias T
if (SmGui::Checkbox(CONCAT("Bias T##_hydrasdr_", _this->name), &_this->biasT)) {
if (_this->running) {
hydrasdr_set_rf_bias(_this->openDev, _this->biasT);
}
if (_this->selectedSerStr != "") {
config.acquire();
config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT;
config.release(true);
}
}
}
char valStr[256];
char regStr[256];
static int callback(hydrasdr_transfer_t* transfer) {
HydraSDRSourceModule* _this = (HydraSDRSourceModule*)transfer->ctx;
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
if (!_this->stream.swap(transfer->sample_count)) { return -1; }
return 0;
}
std::string name;
hydrasdr_device* openDev;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
uint64_t selectedSerial = 0;
std::string selectedSerStr = "";
int devId = 0;
int srId = 0;
int portId = 0;
bool biasT = false;
int lnaGain = 0;
int vgaGain = 0;
int mixerGain = 0;
int linearGain = 0;
int sensitiveGain = 0;
int gainMode = 0;
bool lnaAgc = false;
bool mixerAgc = false;
#ifdef __ANDROID__
int devFd = 0;
#endif
OptionList<std::string, uint64_t> devices;
OptionList<uint32_t, uint32_t> samplerates;
OptionList<std::string, hydrasdr_rf_port_t> ports;
};
MOD_EXPORT void _INIT_() {
json def = json({});
def["devices"] = json({});
def["device"] = "";
config.setPath(core::args["root"].s() + "/hydrasdr_config.json");
config.load(def);
config.enableAutoSave();
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new HydraSDRSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
delete (HydraSDRSourceModule*)instance;
}
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}

View File

@@ -1 +0,0 @@
vendor/*

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -36,10 +36,10 @@ enum SampleType {
}; };
const size_t SAMPLE_TYPE_SIZE[] { const size_t SAMPLE_TYPE_SIZE[] {
2*sizeof(int8_t), sizeof(int8_t)*2,
2*sizeof(int16_t), sizeof(int16_t)*2,
2*sizeof(int32_t), sizeof(int32_t)*2,
2*sizeof(float), sizeof(float)*2,
}; };
class NetworkSourceModule : public ModuleManager::Instance { class NetworkSourceModule : public ModuleManager::Instance {
@@ -58,6 +58,20 @@ public:
handler.tuneHandler = tune; handler.tuneHandler = tune;
handler.stream = &stream; handler.stream = &stream;
// Define samplerates
for (int i = 3000; i <= 192000; i <<= 1) {
samplerates.define(i, getSrScaled(i), i);
}
for (int i = 250000; i < 1000000; i += 250000) {
samplerates.define(i, getSrScaled(i), i);
}
for (int i = 1000000; i < 10000000; i += 500000) {
samplerates.define(i, getSrScaled(i), i);
}
for (int i = 10000000; i <= 100000000; i += 5000000) {
samplerates.define(i, getSrScaled(i), i);
}
// Define protocols // Define protocols
// protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER); // protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT); protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
@@ -72,8 +86,8 @@ public:
// Load config // Load config
config.acquire(); config.acquire();
if (config.conf[name].contains("samplerate")) { if (config.conf[name].contains("samplerate")) {
samplerate = config.conf[name]["samplerate"]; int sr = config.conf[name]["samplerate"];
tempSamplerate = samplerate; if (samplerates.keyExists(sr)) { samplerate = samplerates.value(samplerates.keyId(sr)); }
} }
if (config.conf[name].contains("protocol")) { if (config.conf[name].contains("protocol")) {
std::string protoStr = config.conf[name]["protocol"]; std::string protoStr = config.conf[name]["protocol"];
@@ -94,6 +108,7 @@ public:
config.release(); config.release();
// Set menu IDs // Set menu IDs
srId = samplerates.valueId(samplerate);
protoId = protocols.valueId(proto); protoId = protocols.valueId(proto);
sampTypeId = sampleTypes.valueId(sampType); sampTypeId = sampleTypes.valueId(sampType);
@@ -213,24 +228,35 @@ private:
if (_this->running) { SmGui::BeginDisabled(); } if (_this->running) { SmGui::BeginDisabled(); }
// Hostname and port field // 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.acquire();
config.conf[_this->name]["host"] = _this->hostname; config.conf[_this->name]["host"] = _this->hostname;
config.release(true); config.release(true);
} }
SmGui::SameLine(); ImGui::SameLine();
SmGui::FillWidth(); ImGui::FillWidth();
if (SmGui::InputInt(("##network_source_port_" + _this->name).c_str(), &_this->port, 0, 0)) { if (ImGui::InputInt(("##iq_exporter_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
_this->port = std::clamp<int>(_this->port, 1, 65535); _this->port = std::clamp<int>(_this->port, 1, 65535);
config.acquire(); config.acquire();
config.conf[_this->name]["port"] = _this->port; config.conf[_this->name]["port"] = _this->port;
config.release(true); config.release(true);
} }
// Samplerate selector
ImGui::LeftLabel("Samplerate");
ImGui::FillWidth();
if (ImGui::Combo(("##iq_exporter_sr_" + _this->name).c_str(), &_this->srId, _this->samplerates.txt)) {
_this->samplerate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->samplerate);
config.acquire();
config.conf[_this->name]["samplerate"] = _this->samplerates.key(_this->srId);
config.release(true);
}
// Mode protocol selector // Mode protocol selector
SmGui::LeftLabel("Protocol"); ImGui::LeftLabel("Protocol");
SmGui::FillWidth(); ImGui::FillWidth();
if (SmGui::Combo(("##network_source_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) { if (ImGui::Combo(("##iq_exporter_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
_this->proto = _this->protocols.value(_this->protoId); _this->proto = _this->protocols.value(_this->protoId);
config.acquire(); config.acquire();
config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId); config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
@@ -238,38 +264,15 @@ private:
} }
// Sample type selector // Sample type selector
SmGui::LeftLabel("Sample type"); ImGui::LeftLabel("Sample type");
SmGui::FillWidth(); ImGui::FillWidth();
if (SmGui::Combo(("##network_source_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) { if (ImGui::Combo(("##iq_exporter_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
_this->sampType = _this->sampleTypes.value(_this->sampTypeId); _this->sampType = _this->sampleTypes.value(_this->sampTypeId);
config.acquire(); config.acquire();
config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId); config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
config.release(true); config.release(true);
} }
// Samplerate selector
SmGui::LeftLabel("Samplerate");
SmGui::FillWidth();
if (SmGui::InputInt(("##network_source_sr_" + _this->name).c_str(), &_this->tempSamplerate)) {
// Prevent silly values from silly users
_this->tempSamplerate = std::max<int>(_this->tempSamplerate, 1000);
}
bool applyEn = (!_this->running && _this->tempSamplerate != _this->samplerate);
if (!applyEn) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
if (SmGui::Button(("Apply##network_source_apply_" + _this->name).c_str())) {
_this->samplerate = _this->tempSamplerate;
core::setInputSampleRate(_this->samplerate);
config.acquire();
config.conf[_this->name]["samplerate"] = _this->samplerate;
config.release(true);
}
if (!applyEn) { SmGui::EndDisabled(); }
if (_this->tempSamplerate != _this->samplerate) {
SmGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Warning: Samplerate not applied yet");
}
if (_this->running) { SmGui::EndDisabled(); } if (_this->running) { SmGui::EndDisabled(); }
} }
@@ -277,17 +280,14 @@ private:
// Compute sizes // Compute sizes
int blockSize = samplerate / 200; int blockSize = samplerate / 200;
int sampleSize = SAMPLE_TYPE_SIZE[sampType]; int sampleSize = SAMPLE_TYPE_SIZE[sampType];
int frameSize = blockSize*sampleSize;
// Chose amount of bytes to attempt to read
bool forceSize = (proto != PROTOCOL_UDP);
int frameSize = sampleSize * (forceSize ? blockSize : STREAM_BUFFER_SIZE);
// Allocate receive buffer // Allocate receive buffer
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize); uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
while (true) { while (true) {
// Read samples from socket // Read samples from socket
int bytes = sock->recv(buffer, frameSize, forceSize); int bytes = sock->recv(buffer, frameSize, true);
if (bytes <= 0) { break; } if (bytes <= 0) { break; }
// Convert to CF32 (note: problem if partial sample) // Convert to CF32 (note: problem if partial sample)
@@ -325,7 +325,7 @@ private:
double freq; double freq;
int samplerate = 1000000; int samplerate = 1000000;
int tempSamplerate = 1000000; int srId;
Protocol proto = PROTOCOL_UDP; Protocol proto = PROTOCOL_UDP;
int protoId; int protoId;
SampleType sampType = SAMPLE_TYPE_INT16; SampleType sampType = SAMPLE_TYPE_INT16;
@@ -333,6 +333,7 @@ private:
char hostname[1024] = "localhost"; char hostname[1024] = "localhost";
int port = 1234; int port = 1234;
OptionList<int, int> samplerates;
OptionList<std::string, Protocol> protocols; OptionList<std::string, Protocol> protocols;
OptionList<std::string, SampleType> sampleTypes; OptionList<std::string, SampleType> sampleTypes;

View File

@@ -23,12 +23,6 @@ SDRPP_MOD_INFO{
ConfigManager config; ConfigManager config;
const std::vector<const char*> deviceWhiteList = {
"PlutoSDR",
"ANTSDR",
"LibreSDR"
};
class PlutoSDRSourceModule : public ModuleManager::Instance { class PlutoSDRSourceModule : public ModuleManager::Instance {
public: public:
PlutoSDRSourceModule(std::string name) { PlutoSDRSourceModule(std::string name) {
@@ -136,14 +130,7 @@ private:
std::string duri = iio_context_info_get_uri(info); std::string duri = iio_context_info_get_uri(info);
// If the device is not a plutosdr, don't include it // If the device is not a plutosdr, don't include it
bool isPluto = false; if (desc.find("PlutoSDR") == std::string::npos) {
for (const auto type : deviceWhiteList) {
if (desc.find(type) != std::string::npos) {
isPluto = true;
break;
}
}
if (!isPluto) {
flog::warn("Ignored IIO device: [{}] {}", duri, desc); flog::warn("Ignored IIO device: [{}] {}", duri, desc);
continue; continue;
} }
@@ -176,9 +163,6 @@ private:
// Construct the device name // Construct the device name
std::string devName = '(' + backend + ") " + model + " [" + serial + ']'; std::string devName = '(' + backend + ") " + model + " [" + serial + ']';
// Skip duplicate devices
if (devices.keyExists(desc) || devices.nameExists(devName) || devices.valueExists(duri)) { continue; }
// Save device // Save device
devices.define(desc, devName, duri); devices.define(desc, devName, duri);
} }

View File

@@ -5,7 +5,6 @@
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <mutex> #include <mutex>
#include <chrono>
#define RFSPACE_MAX_SIZE 8192 #define RFSPACE_MAX_SIZE 8192
#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000 #define RFSPACE_HEARTBEAT_INTERVAL_MS 1000

View File

@@ -121,23 +121,14 @@ public:
#ifndef __ANDROID__ #ifndef __ANDROID__
devCount = rtlsdr_get_device_count(); devCount = rtlsdr_get_device_count();
char buf[1024]; char buf[1024];
char venBuf[256]; char snBuf[1024];
char prodBuf[256];
char snBuf[256];
for (int i = 0; i < devCount; i++) { for (int i = 0; i < devCount; i++) {
// Gather device info // Gather device info
const char* devName = rtlsdr_get_device_name(i); const char* devName = rtlsdr_get_device_name(i);
int snErr = rtlsdr_get_device_usb_strings(i, venBuf, prodBuf, snBuf); int snErr = rtlsdr_get_device_usb_strings(i, NULL, NULL, snBuf);
// Build name // Build name
if (venBuf[0] && prodBuf[0]) { sprintf(buf, "[%s] %s##%d", (!snErr && snBuf[0]) ? snBuf : "No Serial", devName, i);
sprintf(buf, "%s %s [%s]##%d", venBuf, prodBuf, (!snErr && snBuf[0]) ? snBuf : "No Serial", i);
}
else {
sprintf(buf, "%s [%s]##%d", devName, (!snErr && snBuf[0]) ? snBuf : "No Serial", i);
}
// Add device to list
devNames.push_back(buf); devNames.push_back(buf);
devListTxt += buf; devListTxt += buf;
devListTxt += '\0'; devListTxt += '\0';
@@ -208,6 +199,8 @@ public:
config.conf["devices"][selectedDevName]["tunerAgc"] = tunerAgc; config.conf["devices"][selectedDevName]["tunerAgc"] = tunerAgc;
config.conf["devices"][selectedDevName]["gain"] = gainId; config.conf["devices"][selectedDevName]["gain"] = gainId;
} }
if (gainId >= gainList.size()) { gainId = gainList.size() - 1; }
updateGainTxt();
// Load config // Load config
if (config.conf["devices"][selectedDevName].contains("sampleRate")) { if (config.conf["devices"][selectedDevName].contains("sampleRate")) {
@@ -247,11 +240,9 @@ public:
if (config.conf["devices"][selectedDevName].contains("gain")) { if (config.conf["devices"][selectedDevName].contains("gain")) {
gainId = config.conf["devices"][selectedDevName]["gain"]; gainId = config.conf["devices"][selectedDevName]["gain"];
updateGainTxt();
} }
if (gainId >= gainList.size()) { gainId = gainList.size() - 1; }
updateGainTxt();
config.release(created); config.release(created);
rtlsdr_close(openDev); rtlsdr_close(openDev);
@@ -604,4 +595,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
MOD_EXPORT void _END_() { MOD_EXPORT void _END_() {
config.disableAutoSave(); config.disableAutoSave();
config.save(); config.save();
} }

View File

@@ -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)

View File

@@ -1,2 +0,0 @@
build/
.vscode/

View File

@@ -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)

View File

@@ -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)

View File

@@ -1,177 +0,0 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
// Handle inclusion from C++ code
#ifdef __cplusplus
extern "C" {
#endif
#define SDDC_SERIAL_MAX_LEN 256
enum sddc_model {
SDDC_MODEL_UNKNOWN = 0x00,
SDDC_MODEL_BBRF103 = 0x01,
SDDC_MODEL_HF103 = 0x02,
SDDC_MODEL_RX888 = 0x03,
SDDC_MODEL_RX888_MK2 = 0x04,
SDDC_MODEL_RX999 = 0x05,
SDDC_MODEL_RXLUCY = 0x06,
SDDC_MODEL_RX888_MK3 = 0x07
};
typedef enum sddc_model sddc_model_t;
enum sddc_error {
SDDC_ERROR_UNKNOWN = -99,
SDDC_ERROR_NOT_IMPLEMENTED = -98,
SDDC_ERROR_FIRMWARE_UPLOAD_FAILED = -4,
SDDC_ERROR_NOT_FOUND = -3,
SDDC_ERROR_USB_ERROR = -2,
SDDC_ERROR_TIMEOUT = -1,
SDDC_SUCCESS = 0
};
typedef enum sddc_error sddc_error_t;
/**
* Device instance.
*/
struct sddc_dev;
typedef struct sddc_dev sddc_dev_t;
/**
* Device information.
*/
struct sddc_devinfo {
const char serial[SDDC_SERIAL_MAX_LEN];
sddc_model_t model;
int firmwareMajor;
int firmwareMinor;
};
typedef struct sddc_devinfo sddc_devinfo_t;
/**
* Parameter range. A step size of zero means infinitely variable.
*/
struct sddc_range {
double start;
double end;
double step;
};
typedef struct sddc_range sddc_range_t;
/**
* Get the string representation of a device model.
* @param model Model to get the string representation of.
* @return String representation of the model.
*/
const char* sddc_model_to_string(sddc_model_t model);
/**
* Get the string representation of an error.
* @param model Error to get the string representation of.
* @return String representation of the error.
*/
const char* sddc_error_to_string(sddc_error_t error);
/**
* Set the path to the firmware image.
* @param path Path to the firmware image.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_firmware_path(const char* path);
/**
* Get a list of connected devices. The returned list has to be freed using `sddc_free_device_list()` if it isn't empty.
* @param dev_list Pointer to a list of devices.
* @return Number of devices in the list or an error code if an error occured.
*/
int sddc_get_device_list(sddc_devinfo_t** dev_list);
/**
* Free a device list returned by `sddc_get_device_list()`. Attempting to free a list returned empty has no effect.
* @param dev_list Device list to free.
*/
void sddc_free_device_list(sddc_devinfo_t* dev_list);
/**
* Open a device by its serial number.
* @param serial Serial number of the device to open.
* @param dev Pointer to a SDDC device pointer to populate once open.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_open(const char* serial, sddc_dev_t** dev);
/**
* Close an opened SDDC device.
* @param dev SDDC Device to close.
*/
void sddc_close(sddc_dev_t* dev);
/**
* Get the range of samplerate supported by a device.
* @param dev SDDC device.
* @return Range of supported samplerates.
*/
sddc_range_t sddc_get_samplerate_range(sddc_dev_t* dev);
/**
* Set the device's sampling rate.
* @param dev SDDC device.
* @param samplerate Sampling rate.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_samplerate(sddc_dev_t* dev, uint32_t samplerate);
/**
* Enable the ADC's dithering feature.
* @param dev SDDC device.
* @param enabled True to enable dithering, false to disable it.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_dithering(sddc_dev_t* dev, bool enabled);
/**
* Enable the ADC's randomizer feature.
* @param dev SDDC device.
* @param enabled True to enable randomization, false to disable it.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_randomizer(sddc_dev_t* dev, bool enabled);
/**
* Set the LO of the tuner.
* @param dev SDDC device.
* @param frequency Frequency of the LO.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_set_tuner_frequency(sddc_dev_t* dev, uint64_t frequency);
/**
* Start the device.
* @param dev SDDC device.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_start(sddc_dev_t* dev);
/**
* Stop the device.
* @param dev SDDC device.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_stop(sddc_dev_t* dev);
/**
* Receive samples.
* @param dev SDDC device.
* @param samples Buffer to write the samples to.
* @param count Number of samples to read.
* @return SDDC_SUCCESS on success or an error code otherwise.
*/
sddc_error_t sddc_rx(sddc_dev_t* dev, int16_t* samples, int count);
// Handle inclusion from C++ code
#ifdef __cplusplus
}
#endif

Some files were not shown because too many files have changed in this diff Show More