Merge pull request #1302 from AlexandreRouma/master

keep new_rds branch updated
This commit is contained in:
AlexandreRouma 2024-01-29 01:45:32 +01:00 committed by GitHub
commit 9501371c6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 1338 additions and 600 deletions

View File

@ -7,6 +7,8 @@ assignees: ''
---
# WARNING: Filling out the template below is NOT optional. Issues not filling out this template will be closed without review.
FIRST: Before reporting any bug, make sure that the bug you are reporting has not been reported before. Also, try to use the [nightly version](https://www.sdrpp.org/nightly) if possible in case I've already fixed the bug.
**Hardware**

View File

@ -1,7 +1,4 @@
# Important
Only minor bug fixes and bandplans are accepted.
Pull requests adding features or any bug fix that requires significant code changes will be automatically rejected.
Only bandplan, colormaps and themes are accepted. Code pull requests are **NOT welcome**.
Open an issue requesting a feature or discussing a possible bugfix instead.

View File

@ -18,7 +18,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
@ -34,7 +34,7 @@ jobs:
- name: Patch Pothos with earlier libusb version
working-directory: ${{runner.workspace}}
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/"
run: 7z x libusb.7z -olibusb_old ; rm "C:/Program Files/PothosSDR/bin/libusb-1.0.dll" ; cp "libusb_old/MS64/dll/libusb-1.0.dll" "C:/Program Files/PothosSDR/bin/" ; rm "C:/Program Files/PothosSDR/lib/libusb-1.0.lib" ; cp "libusb_old/MS64/dll/libusb-1.0.lib" "C:/Program Files/PothosSDR/lib/"
- name: Download SDRPlay API
run: Invoke-WebRequest -Uri "https://drive.google.com/uc?id=12UHPMwkfa67A11QZDmpCT4iwHnyJHWuu&confirm=t" -OutFile ${{runner.workspace}}/SDRPlay.zip
@ -64,7 +64,7 @@ jobs:
run: git clone https://github.com/thestk/rtaudio ; cd rtaudio ; git checkout 2f2fca4502d506abc50f6d4473b2836d24cfb1e3 ; mkdir build ; cd build ; cmake .. ; cmake --build . --config Release ; cmake --install .
- name: Install libperseus-sdr
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
run: git clone https://github.com/AlexandreRouma/libperseus-sdr ; cd libperseus-sdr ; mkdir build ; cd build ; cmake "-DLIBUSB_LIBRARIES=C:/Program Files/PothosSDR/lib/libusb-1.0.lib" "-DLIBUSB_INCLUDE_DIRS=C:/Program Files/PothosSDR/include/libusb-1.0" .. "-DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake" ; cmake --build . --config Release ; mkdir "C:/Program Files/PothosSDR/include/perseus-sdr" ; cp Release/perseus-sdr.dll "C:/Program Files/PothosSDR/bin" ; cp Release/perseus-sdr.lib "C:/Program Files/PothosSDR/bin" ; cd .. ; xcopy "src" "C:/Program Files/PothosSDR/include/perseus-sdr"
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
@ -79,16 +79,16 @@ jobs:
run: '&($Env:GITHUB_WORKSPACE + "/make_windows_package.ps1") ./build ($Env:GITHUB_WORKSPACE + "/root")'
- name: Save Archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_windows_x64
path: ${{runner.workspace}}/sdrpp_windows_x64.zip
build_macos:
runs-on: macos-11
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Build Environment
run: cmake -E make_directory ${{runner.workspace}}/build
@ -100,29 +100,29 @@ jobs:
run: brew install pkg-config libusb fftw glfw airspy airspyhf portaudio hackrf libbladerf codec2 zstd autoconf automake libtool && pip3 install mako
- name: Install volk
run: git clone --recursive https://github.com/gnuradio/volk && cd volk && mkdir build && cd build && cmake -DCMAKE_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 ../../
- name: Install SDRplay API
run: wget https://www.sdrplay.com/software/SDRplay_RSP_API-MacOSX-3.07.3.pkg && sudo installer -pkg SDRplay_RSP_API-MacOSX-3.07.3.pkg -target /
run: wget https://www.sdrplay.com/software/SDRplayAPI-macos-installer-universal-3.12.1.pkg && sudo installer -pkg SDRplayAPI-macos-installer-universal-3.12.1.pkg -target /
- name: Install libiio
run: wget https://github.com/analogdevicesinc/libiio/archive/refs/tags/v0.25.zip && 7z x v0.25.zip && cd libiio-0.25 && mkdir build && cd build && cmake -DCMAKE_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
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
run: git clone https://github.com/analogdevicesinc/libad9361-iio && cd libad9361-iio && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install LimeSuite
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
run: git clone https://github.com/myriadrf/LimeSuite && cd LimeSuite && mkdir builddir && cd builddir && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 && sudo make install && cd ../../
- name: Install libperseus
run: git clone https://github.com/Microtelecom/libperseus-sdr && cd libperseus-sdr && autoreconf -i && ./configure --prefix=/usr/local && make && make install && cd ..
- name: Install more recent librtlsdr
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
run: git clone https://github.com/osmocom/rtl-sdr && cd rtl-sdr && mkdir build && cd build && cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=Release .. && make -j3 LIBRARY_PATH=$(pkg-config --libs-only-L libusb-1.0 | sed 's/\-L//') && sudo make install && cd ../../
- name: Prepare CMake
working-directory: ${{runner.workspace}}/build
run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
run: cmake -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release
- name: Build
working-directory: ${{runner.workspace}}/build
@ -133,7 +133,7 @@ jobs:
run: cd $GITHUB_WORKSPACE && sh make_macos_bundle.sh ${{runner.workspace}}/build ./SDR++.app && zip -r ${{runner.workspace}}/sdrpp_macos_intel.zip SDR++.app
- name: Save Archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_macos_intel
path: ${{runner.workspace}}/sdrpp_macos_intel.zip
@ -142,7 +142,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_buster && docker build . --tag sdrpp_build
@ -155,7 +155,7 @@ jobs:
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_buster_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
@ -164,7 +164,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bullseye && docker build . --tag sdrpp_build
@ -177,7 +177,7 @@ jobs:
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_bullseye_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
@ -186,7 +186,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_bookworm && docker build . --tag sdrpp_build
@ -199,7 +199,7 @@ jobs:
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_bookworm_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
@ -208,7 +208,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/debian_sid && docker build . --tag sdrpp_build
@ -221,7 +221,7 @@ jobs:
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_debian_sid_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
@ -230,7 +230,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_focal && docker build . --tag sdrpp_build
@ -243,7 +243,7 @@ jobs:
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_ubuntu_focal_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
@ -252,7 +252,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_jammy && docker build . --tag sdrpp_build
@ -265,16 +265,38 @@ jobs:
run: docker cp build:/root/SDRPlusPlus/sdrpp_debian_amd64.deb ./
- name: Save Deb Archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_ubuntu_jammy_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_ubuntu_mantic:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create Docker Image
run: cd $GITHUB_WORKSPACE/docker_builds/ubuntu_mantic && docker build . --tag sdrpp_build
- name: Run Container
run: docker run --name build -v $GITHUB_WORKSPACE:/root/SDRPlusPlus --env BUILD_NO="-$GITHUB_RUN_NUMBER" sdrpp_build /root/do_build.sh
- 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_mantic_amd64
path: ${{runner.workspace}}/sdrpp_debian_amd64.deb
build_raspios_bullseye_armhf:
runs-on: ARM
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Create Build Environment
run: rm -rf ${{runner.workspace}}/build && cmake -E make_directory ${{runner.workspace}}/build
@ -292,7 +314,7 @@ jobs:
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
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_raspios_bullseye_armhf
path: ${{runner.workspace}}/sdrpp_debian_armhf.deb
@ -301,7 +323,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Fetch container
working-directory: ${{runner.workspace}}
@ -319,18 +341,18 @@ jobs:
run: docker cp build:/root/SDRPlusPlus/android/app/build/outputs/apk/debug/app-debug.apk ./ && mv app-debug.apk sdrpp.apk
- name: Save APK
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: sdrpp_android
path: ${{runner.workspace}}/sdrpp.apk
create_full_archive:
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_raspios_bullseye_armhf', 'build_android']
needs: ['build_windows', 'build_macos', 'build_debian_buster', 'build_debian_bullseye', 'build_debian_bookworm', 'build_debian_sid', 'build_ubuntu_focal', 'build_ubuntu_jammy', 'build_ubuntu_mantic', 'build_raspios_bullseye_armhf', 'build_android']
runs-on: ubuntu-latest
steps:
- name: Download All Builds
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
- name: Create Archive
run: >
@ -343,10 +365,11 @@ jobs:
mv sdrpp_debian_sid_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_debian_sid_amd64.deb &&
mv sdrpp_ubuntu_focal_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_focal_amd64.deb &&
mv sdrpp_ubuntu_jammy_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_jammy_amd64.deb &&
mv sdrpp_ubuntu_mantic_amd64/sdrpp_debian_amd64.deb sdrpp_all/sdrpp_ubuntu_mantic_amd64.deb &&
mv sdrpp_raspios_bullseye_armhf/sdrpp_debian_armhf.deb sdrpp_all/sdrpp_raspios_bullseye_armhf.deb &&
mv sdrpp_android/sdrpp.apk sdrpp_all/sdrpp.apk
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: sdrpp_all
path: sdrpp_all/
@ -358,7 +381,7 @@ jobs:
steps:
- name: Download All Builds
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
- name: Update Nightly
run: gh release upload nightly sdrpp_all/* -R ${{github.repository}} --clobber
@ -367,7 +390,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install codespell
run: sudo apt update -y && sudo apt install -y codespell
@ -379,7 +402,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Run check_clang_format
run: cd $GITHUB_WORKSPACE && chmod +x ./check_clang_format.sh && ./check_clang_format.sh || true

View File

@ -1,65 +1,6 @@
# Pull Requests
**I DO NOT ACCEPT PULL-REQUEST FOR FEATURES OR BUGFIXES REQUIRING SIGNIFICANT CODE/STRUCTURE CHANGES.**
**SUCH PULL REQUESTS WILL BE CLOSED AUTOMATICALLY. OPEN AN ISSUE DETAILING FEATURE REQUESTS OR POTENTIAL BUGFIX INSTEAD.**
# Code Style
## Naming Convention
- Files: `snake_case.h` `snake_case.cpp`
- Namespaces: `CamelCase`
- Classes: `CamelCase`
- Structs: `CamelCase_t`
- Members: `camelCase`
- Enum: `SNAKE_CASE`
- Macros: `SNAKE_CASE`
## Brace Style
```c++
int myFunction() {
if (shortIf) { shortFunctionName(); }
if (longIf) {
longFunction();
otherStuff();
myLongFunction();
}
}
```
Note: If it makes the code cleaner, remember to use the `?` keyword instead of a `if else` statement.
## Pointers
Please use `type* name` for pointers.
## Structure
Headers and their associated C++ files shall be in the same directory. All headers must use `#pragma once` instead of other include guards. Only include files in a header that are being used in that header. Include the rest in the associated C++ file.
# Modules
## Module Naming Convention
All modules names must be `snake_case`. If the module is a source, it must end with `_source`. If it is a sink, it must end with `_sink`.
For example, lets take the module named `cool_source`:
- Directory: `cool_source`
- Class: `CoolSourceModule`
- Binary: `cool_source.<os dynlib extension>`
## Integration into main repository
If the module meets the code quality requirements, it may be added to the official repository. A module that doesn't require any external dependencies that the core doesn't already use may be enabled for build by default. Otherwise, they must be disabled for build by default with a `OPT_BUILD_MODULE_NAME` variable set to `OFF`.
# JSON Formatting
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**
Code pull requests are **NOT welcome**. Please open an issue discussing potential bugfixes or feature requests instead.
## Band Frequency Allocation
@ -119,8 +60,8 @@ Please follow this guide to properly format the JSON files for custom color maps
}
```
# Best Practices
# JSON Formatting
* All additions and/or bug fixes to the core must not add additional dependencies.
* Use VSCode for development, VS seems to cause issues.
* DO NOT use libboost for any code meant for this repository
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**

View File

@ -45,7 +45,7 @@ uint8_t *history_buffer_get_slice(history_buffer *buf) { return buf->history[buf
shift_register_t history_buffer_search(history_buffer *buf, const distance_t *distances,
unsigned int search_every) {
shift_register_t bestpath;
shift_register_t bestpath = 0;
distance_t leasterror = USHRT_MAX;
// search for a state with the least error
for (shift_register_t state = 0; state < buf->num_states; state += search_every) {

View File

@ -88,7 +88,7 @@ int CommandArgsParser::parse(int argc, char* argv[]) {
try {
carg.ival = std::stoi(arg);
}
catch (std::exception e) {
catch (const std::exception& e) {
printf("Invalid argument, failed to parse integer\n");
showHelp();
return -1;
@ -98,7 +98,7 @@ int CommandArgsParser::parse(int argc, char* argv[]) {
try {
carg.fval = std::stod(arg);
}
catch (std::exception e) {
catch (const std::exception& e) {
printf("Invalid argument, failed to parse float\n");
showHelp();
return -1;

View File

@ -36,8 +36,8 @@ void ConfigManager::load(json def, bool lock) {
file >> conf;
file.close();
}
catch (std::exception e) {
flog::error("Config file '{0}' is corrupted, resetting it", path);
catch (const std::exception& e) {
flog::error("Config file '{}' is corrupted, resetting it: {}", path, e.what());
conf = def;
save(false);
}

View File

@ -93,7 +93,7 @@ namespace dsp {
void disableBlock(Processor<T, T>* block, Func onOutputChange) {
// Check that the block is part of the chain
if (!blockExists(block)) {
throw std::runtime_error("[chain] Tried to enable a block that isn't part of the chain");
throw std::runtime_error("[chain] Tried to disable a block that isn't part of the chain");
}
// If already disabled, don't do anything
@ -163,10 +163,12 @@ namespace dsp {
private:
Processor<T, T>* blockBefore(Processor<T, T>* block) {
// TODO: This is wrong and must be fixed when I get more time
for (auto& ln : links) {
if (ln == block) { return NULL; }
if (states[ln]) { return ln; }
}
return NULL;
}
Processor<T, T>* blockAfter(Processor<T, T>* block) {

View File

@ -110,7 +110,7 @@ namespace dsp::demod {
else if (_mode == Mode::LSB) {
return -_bandwidth / 2.0;
}
else if (_mode == Mode::DSB) {
else {
return 0.0;
}
}

View File

@ -62,6 +62,33 @@ inline void printAndScale(double freq, char* buf) {
}
}
inline void doZoom(int offset, int width, int inSize, int outSize, float* in, float* out) {
// NOTE: REMOVE THAT SHIT, IT'S JUST A HACKY FIX
if (offset < 0) {
offset = 0;
}
if (width > 524288) {
width = 524288;
}
float factor = (float)width / (float)outSize;
float sFactor = ceilf(factor);
float uFactor;
float id = offset;
float maxVal;
int sId;
for (int i = 0; i < outSize; i++) {
maxVal = -INFINITY;
sId = (int)id;
uFactor = (sId + sFactor > inSize) ? sFactor - ((sId + sFactor) - inSize) : sFactor;
for (int j = 0; j < uFactor; j++) {
if (in[sId + j] > maxVal) { maxVal = in[sId + j]; }
}
out[i] = maxVal;
id += factor;
}
}
namespace ImGui {
WaterFall::WaterFall() {
fftMin = -70.0;
@ -586,7 +613,7 @@ namespace ImGui {
for (int i = 0; i < count; i++) {
drawDataSize = (viewBandwidth / wholeBandwidth) * rawFFTSize;
drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2);
doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[((i + currentFFTLine) % waterfallHeight) * rawFFTSize], tempData);
doZoom(drawDataStart, drawDataSize, rawFFTSize, dataWidth, &rawFFTs[((i + currentFFTLine) % waterfallHeight) * rawFFTSize], tempData);
for (int j = 0; j < dataWidth; j++) {
pixel = (std::clamp<float>(tempData[j], waterfallMin, waterfallMax) - waterfallMin) / dataRange;
waterfallFb[(i * dataWidth) + j] = waterfallPallet[(int)(pixel * (WATERFALL_RESOLUTION - 1))];
@ -867,7 +894,7 @@ namespace ImGui {
int drawDataStart = (((double)rawFFTSize / 2.0) * (offsetRatio + 1)) - (drawDataSize / 2);
if (waterfallVisible) {
doZoom(drawDataStart, drawDataSize, dataWidth, &rawFFTs[currentFFTLine * rawFFTSize], latestFFT);
doZoom(drawDataStart, drawDataSize, rawFFTSize, dataWidth, &rawFFTs[currentFFTLine * rawFFTSize], latestFFT);
memmove(&waterfallFb[dataWidth], waterfallFb, dataWidth * (waterfallHeight - 1) * sizeof(uint32_t));
float pixel;
float dataRange = waterfallMax - waterfallMin;
@ -879,7 +906,7 @@ namespace ImGui {
waterfallUpdate = true;
}
else {
doZoom(drawDataStart, drawDataSize, dataWidth, rawFFTs, latestFFT);
doZoom(drawDataStart, drawDataSize, rawFFTSize, dataWidth, rawFFTs, latestFFT);
fftLines = 1;
}

View File

@ -90,33 +90,6 @@ namespace ImGui {
float* getFFTBuffer();
void pushFFT();
inline void doZoom(int offset, int width, int outWidth, float* data, float* out) {
// NOTE: REMOVE THAT SHIT, IT'S JUST A HACKY FIX
if (offset < 0) {
offset = 0;
}
if (width > 524288) {
width = 524288;
}
float factor = (float)width / (float)outWidth;
float sFactor = ceilf(factor);
float uFactor;
float id = offset;
float maxVal;
int sId;
for (int i = 0; i < outWidth; i++) {
maxVal = -INFINITY;
sId = (int)id;
uFactor = (sId + sFactor > rawFFTSize) ? sFactor - ((sId + sFactor) - rawFFTSize) : sFactor;
for (int j = 0; j < uFactor; j++) {
if (data[sId + j] > maxVal) { maxVal = data[sId + j]; }
}
out[i] = maxVal;
id += factor;
}
}
void updatePallette(float colors[][3], int colorCount);
void updatePalletteFromArray(float* colors, int colorCount);

View File

@ -86,14 +86,14 @@ namespace net {
addr.sin_port = htons(port);
}
std::string Address::getIPStr() {
std::string Address::getIPStr() const {
char buf[128];
IP_t ip = getIP();
sprintf(buf, "%d.%d.%d.%d", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
return buf;
}
IP_t Address::getIP() {
IP_t Address::getIP() const {
return htonl(addr.sin_addr.s_addr);
}
@ -101,7 +101,7 @@ namespace net {
addr.sin_addr.s_addr = htonl(ip);
}
int Address::getPort() {
int Address::getPort() const {
return htons(addr.sin_port);
}
@ -160,8 +160,8 @@ namespace net {
// Set timeout
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = timeout * 1000;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout - tv.tv_sec*1000) * 1000;
// Wait for data
int err = select(sock+1, &set, NULL, &set, (timeout > 0) ? &tv : NULL);
@ -225,8 +225,8 @@ namespace net {
// Define timeout
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = timeout * 1000;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout - tv.tv_sec*1000) * 1000;
// Wait for data or error
if (timeout != NONBLOCKING) {
@ -375,13 +375,25 @@ namespace net {
return connect(Address(host, port));
}
std::shared_ptr<Socket> openudp(const Address& raddr, const Address& laddr) {
std::shared_ptr<Socket> openudp(const Address& raddr, const Address& laddr, bool allowBroadcast) {
// Init library if needed
init();
// Create socket
SockHandle_t s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// If the remote address is multicast, allow multicast connections
#ifdef _WIN32
const char enable = allowBroadcast;
#else
int enable = allowBroadcast;
#endif
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(int)) < 0) {
closeSocket(s);
throw std::runtime_error("Could not enable broadcast on socket");
return NULL;
}
// Bind socket to local port
if (bind(s, (sockaddr*)&laddr.addr, sizeof(sockaddr_in))) {
closeSocket(s);
@ -393,15 +405,15 @@ namespace net {
return std::make_shared<Socket>(s, &raddr);
}
std::shared_ptr<Socket> openudp(std::string rhost, int rport, const Address& laddr) {
return openudp(Address(rhost, rport), laddr);
std::shared_ptr<Socket> openudp(std::string rhost, int rport, const Address& laddr, bool allowBroadcast) {
return openudp(Address(rhost, rport), laddr, allowBroadcast);
}
std::shared_ptr<Socket> openudp(const Address& raddr, std::string lhost, int lport) {
return openudp(raddr, Address(lhost, lport));
std::shared_ptr<Socket> openudp(const Address& raddr, std::string lhost, int lport, bool allowBroadcast) {
return openudp(raddr, Address(lhost, lport), allowBroadcast);
}
std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost, int lport) {
return openudp(Address(rhost, rport), Address(lhost, lport));
std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost, int lport, bool allowBroadcast) {
return openudp(Address(rhost, rport), Address(lhost, lport), allowBroadcast);
}
}

View File

@ -67,13 +67,13 @@ namespace net {
* Get the IP address.
* @return IP address in standard string format.
*/
std::string getIPStr();
std::string getIPStr() const;
/**
* Get the IP address.
* @return IP address in host byte order.
*/
IP_t getIP();
IP_t getIP() const;
/**
* Set the IP address.
@ -85,7 +85,7 @@ namespace net {
* Get the TCP/UDP port.
* @return TCP/UDP port number.
*/
int getPort();
int getPort() const;
/**
* Set the TCP/UDP port.
@ -246,37 +246,37 @@ namespace net {
/**
* Create UDP socket.
* @param raddr Remote address.
* @param raddr Remote address. Set to a multicast address to allow multicast.
* @param laddr Local address to bind the socket to.
* @return Socket instance on success, Throws runtime_error otherwise.
*/
std::shared_ptr<Socket> openudp(const Address& raddr, const Address& laddr);
std::shared_ptr<Socket> openudp(const Address& raddr, const Address& laddr, bool allowBroadcast = false);
/**
* Create UDP socket.
* @param rhost Remote hostname or IP address.
* @param rhost Remote hostname or IP address. Set to a multicast address to allow multicast.
* @param rport Remote port.
* @param laddr Local address to bind the socket to.
* @return Socket instance on success, Throws runtime_error otherwise.
*/
std::shared_ptr<Socket> openudp(std::string rhost, int rport, const Address& laddr);
std::shared_ptr<Socket> openudp(std::string rhost, int rport, const Address& laddr, bool allowBroadcast = false);
/**
* Create UDP socket.
* @param raddr Remote address.
* @param raddr Remote address. Set to a multicast or broadcast address to allow multicast.
* @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any).
* @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically).
* @return Socket instance on success, Throws runtime_error otherwise.
*/
std::shared_ptr<Socket> openudp(const Address& raddr, std::string lhost = "0.0.0.0", int lport = 0);
std::shared_ptr<Socket> openudp(const Address& raddr, std::string lhost = "0.0.0.0", int lport = 0, bool allowBroadcast = false);
/**
* Create UDP socket.
* @param rhost Remote hostname or IP address.
* @param rhost Remote hostname or IP address. Set to a multicast or broadcast address to allow multicast.
* @param rport Remote port.
* @param lhost Local hostname or IP used to bind the socket (optional, "0.0.0.0" for Any).
* @param lpost Local port used to bind the socket to (optional, 0 to allocate automatically).
* @return Socket instance on success, Throws runtime_error otherwise.
*/
std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost = "0.0.0.0", int lport = 0);
std::shared_ptr<Socket> openudp(std::string rhost, int rport, std::string lhost = "0.0.0.0", int lport = 0, bool allowBroadcast = false);
}

View File

@ -320,7 +320,7 @@ namespace net {
}
entry.handler(std::move(client), entry.ctx);
}
catch (std::exception e) {
catch (const std::exception& e) {
listening = false;
return;
}

View File

@ -257,6 +257,7 @@ namespace net::http {
// Deserialize
req.deserialize(respData);
return 0; // Might wanna return size instead
}
int Client::sendResponseHeader(ResponseHeader& resp) {
@ -274,6 +275,7 @@ namespace net::http {
// Deserialize
resp.deserialize(respData);
return 0; // Might wanna return size instead
}
int Client::sendChunkHeader(ChunkHeader& chdr) {

View File

@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1
cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus

View File

@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1
cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus

View File

@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev
libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1
cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus

View File

@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev l
libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1
cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus

View File

@ -15,10 +15,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk1-dev
libcodec2-dev libudev-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1
cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install a more recent libusb version

View File

@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1
cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus

View File

@ -9,10 +9,10 @@ apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk2-dev
libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1.run
7z x ./SDRplay_RSP_API-Linux-3.07.1
cp x86_64/libsdrplay_api.so.3.07 /usr/lib/libsdrplay_api.so
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1
cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus

View File

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

View File

@ -0,0 +1,35 @@
#!/bin/bash
set -e
cd /root
# Install dependencies and tools
apt update
apt install -y build-essential cmake git libfftw3-dev libglfw3-dev libvolk-dev libzstd-dev libsoapysdr-dev libairspyhf-dev libairspy-dev \
libiio-dev libad9361-dev librtaudio-dev libhackrf-dev librtlsdr-dev libbladerf-dev liblimesuite-dev p7zip-full wget portaudio19-dev \
libcodec2-dev autoconf libtool xxd
# Install SDRPlay libraries
wget https://www.sdrplay.com/software/SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1.run
7z x ./SDRplay_RSP_API-Linux-3.12.1
cp x86_64/libsdrplay_api.so.3.12 /usr/lib/libsdrplay_api.so
cp inc/* /usr/include/
# Install libperseus
git clone https://github.com/Microtelecom/libperseus-sdr
cd libperseus-sdr
autoreconf -i
./configure
make
make install
ldconfig
cd ..
cd SDRPlusPlus
mkdir build
cd build
cmake .. -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_PERSEUS_SOURCE=ON
make VERBOSE=1 -j2
cd ..
sh make_debian_package.sh ./build 'libfftw3-dev, libglfw3-dev, libvolk-dev, librtaudio-dev, libzstd-dev'

View File

@ -531,7 +531,7 @@ private:
ImGui::TableSetColumnIndex(0);
if (ImGui::Button(("Import##_freq_mgr_imp_" + _this->name).c_str(), ImVec2(ImGui::GetContentRegionAvail().x, 0)) && !_this->importOpen) {
_this->importOpen = true;
_this->importDialog = new pfd::open_file("Import bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" }, true);
_this->importDialog = new pfd::open_file("Import bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" }, pfd::opt::multiselect);
}
ImGui::TableSetColumnIndex(1);
@ -544,7 +544,7 @@ private:
}
config.release();
_this->exportOpen = true;
_this->exportDialog = new pfd::save_file("Export bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" }, true);
_this->exportDialog = new pfd::save_file("Export bookmarks", "", { "JSON Files (*.json)", "*.json", "All Files", "*" });
}
if (selectedNames.size() == 0 && _this->selectedListName != "") { style::endDisabled(); }
ImGui::EndTable();
@ -787,7 +787,7 @@ private:
void exportBookmarks(std::string path) {
std::ofstream fs(path);
exportedBookmarks >> fs;
fs << exportedBookmarks;
fs.close();
}

View File

@ -80,8 +80,8 @@ public:
try {
client = net::rigctl::connect(host, port);
}
catch (std::exception e) {
flog::error("Could not connect: {0}", e.what());
catch (const std::exception& e) {
flog::error("Could not connect: {}", e.what());
return;
}

View File

@ -200,8 +200,8 @@ private:
listener = net::listen(hostname, port);
listener->acceptAsync(clientHandler, this);
}
catch (std::exception e) {
flog::error("Could not start rigctl server: {0}", e.what());
catch (const std::exception& e) {
flog::error("Could not start rigctl server: {}", e.what());
}
}

View File

@ -15,7 +15,7 @@ SDR++ is a cross-platform and open source SDR software with the aim of being blo
* Multi VFO
* Wide hardware support (both through SoapySDR and dedicated modules)
* SIMD accelerated DSP
* Cross-platform (Windows, Linux, OSX and BSD)
* Cross-platform (Windows, Linux, MacOS and BSD)
* Full waterfall update when possible. Makes browsing signals easier and more pleasant
* Modular design (easily write your own plugins)

View File

@ -103,6 +103,24 @@
},
{
"start": 283500,
"end": 472000,
"type": "aviation",
"name": "Aeronautical Radionavigation / Maritime"
},
{
"start": 472000,
"end": 475000,
"type": "amateur",
"name": "635m Ham Band CW"
},
{
"start": 475000,
"end": 479000,
"type": "amateur1",
"name": "635m Ham Band CW, Digimodes"
},
{
"start": 479000,
"end": 526500,
"type": "aviation",
"name": "Aeronautical Radionavigation / Maritime"
@ -284,7 +302,7 @@
{
"start": 3754500,
"end": 3757500,
"type": "amateur1",
"type": "utility",
"name": "The Pip (Night)"
},
{
@ -729,7 +747,7 @@
"start": 5330500,
"end": 5333400,
"type": "amateur",
"name": "|60m Ham Band Ch. 1 (Not NL)"
"name": "60m Ham Band Ch. 1 (Not NL)"
},
{
"start": 5333400,
@ -744,19 +762,31 @@
"name": "Ch. 2 (60m) (Not NL)"
},
{
"start": 5351500,
"end": 5366500,
"start": 5349400,
"end": 5351500,
"type": "amateur1",
"name": "60m Ham Band NL"
"name": "60m Ham Band (Not NL)"
},
{
"start": 5357000,
"end": 5359900,
"start": 5351500,
"end": 5354000,
"type": "amateur",
"name": "Ch. 3 (60m) (Not NL)"
"name": "60m Ham Band CW"
},
{
"start": 5359900,
"start": 5354000,
"end": 5366000,
"type": "amateur1",
"name": "60m HAM Alle modes (USB)"
},
{
"start": 5366000,
"end": 5366500,
"type": "amateur",
"name": "60m HAM Weak NB modes"
},
{
"start": 5366500,
"end": 5371500,
"type": "amateur1",
"name": "60m Ham Band (Not NL)"
@ -777,7 +807,7 @@
"start": 5403500,
"end": 5406400,
"type": "amateur",
"name": "Ch. 5 60m Ham Band (Not NL)|"
"name": "Ch. 5 60m Ham Band (Not NL)"
},
{
"start": 5406400,
@ -3047,17 +3077,11 @@
"type": "utility",
"name": "Radio Astronomy"
},
{
"start": 2300000000,
"end": 2310000000,
"type": "amateur",
"name": "|13cm Ham Band"
},
{
"start": 2320000000,
"end": 2332500000,
"type": "satellite",
"name": "Satellite Digital Audio Radio Service (SDARS)"
"type": "amateur",
"name": "|13cm Ham Band Satellite Digital Audio Radio Service (SDARS)"
},
{
"start": 2332500000,
@ -3067,21 +3091,9 @@
},
{
"start": 2345000000,
"end": 2360000000,
"type": "aviation",
"name": "Aviation Service and the Wireless Communications Service (WCS)"
},
{
"start": 2360000000,
"end": 2390000000,
"type": "aviation",
"name": "Aviation"
},
{
"start": 2390000000,
"end": 2393750000,
"type": "amateur",
"name": "13cm Ham Band (Upper)| Analog and Digital"
"name": "13cm Ham Band Analog and Digital"
},
{
"start": 2393750000,
@ -3093,25 +3105,25 @@
"start": 2394750000,
"end": 2400000000,
"type": "amateur",
"name": "Analog and Digital 13 cm HAM Band|"
"name": "Analog and Digital 13cm HAM Band"
},
{
"start": 2400000000,
"end": 2401000000,
"type": "utility",
"name": "Shared Satellite and 13cm ISM"
"name": "Shared Satellite, 13cm ISM and Ham"
},
{
"start": 2401000000,
"end": 2410000000,
"type": "utility",
"name": "WiFi shared with Satellite and 13cm ISM"
"name": "WiFi shared with Satellite, 13cm ISM and Ham"
},
{
"start": 2410000000,
"end": 2411900000,
"type": "utility",
"name": "WiFi shared with Broadband Modes and 13cm ISM"
"name": "WiFi shared with Broadband Modes, 13cm ISM and Ham"
},
{
"start": 2411900000,
@ -3122,8 +3134,8 @@
{
"start": 2412100000,
"end": 2450000000,
"type": "utility",
"name": "WiFi shared with Broadband Modes 13cm ISM |13cm Ham Band"
"type": "amateur",
"name": "WiFi shared with Broadband Modes 13cm ISM, 13cm Ham Band|"
},
{
"start": 2450000000,
@ -3172,6 +3184,366 @@
"end": 2500000000,
"type": "utility",
"name": "ISM Band (13cm)"
},
{
"name": "IMT",
"type": "cellular",
"start": 2500000000,
"end": 2544500000
},
{
"name": "Radioastronomy",
"type": "utility",
"start": 2690000000,
"end": 2700000000
},
{
"name": "Radar meteo",
"type": "military",
"start": 2700000000,
"end": 2900000000
},
{
"name": "Maritime Radar",
"type": "marine",
"start": 2900000000,
"end": 3400000000
},
{
"name": "Radio Ham 9 cm band",
"type": "amateur",
"start": 3400000000,
"end": 3475000000
},
{
"name": "Digital Networks",
"type": "utility",
"start": 3475000000,
"end": 4200000000
},
{
"name": "Altimeters",
"type": "aviation",
"start": 4200000000,
"end": 4400000000
},
{
"name": "Feeder link",
"type": "satellite",
"start": 5150000000,
"end": 5250000000
},
{
"name": "Digital networks and 802.11",
"type": "utility",
"start": 5250000000,
"end": 5650000000
},
{
"name": "Radio Ham 6 cm band",
"type": "amateur",
"start": 5650000000,
"end": 5850000000
},
{
"name": "Digital Networks and LPR",
"type": "utility",
"start": 5925000000,
"end": 7750000000
},
{
"name": "LPR",
"type": "utility",
"start": 7750000000,
"end": 7975000000
},
{
"name": "Remote sensing",
"type": "utility",
"start": 7975000000,
"end": 8215000000
},
{
"name": "TLPR and SRD",
"type": "utility",
"start": 8215000000,
"end": 8650000000
},
{
"name": "Radar Doppler",
"type": "aviation",
"start": 8650000000,
"end": 8850000000
},
{
"name": "Maritime Radar",
"type": "marine",
"start": 8850000000,
"end": 9000000000
},
{
"name": "Radar and transponder SART",
"type": "marine",
"start": 9000000000,
"end": 9500000000
},
{
"name": "TLPR and SRD",
"type": "utility",
"start": 9500000000,
"end": 10000000000
},
{
"name": "Radio Ham 3 cm band",
"type": "amateur",
"start": 10000000000,
"end": 10500000000
},
{
"name": "Point to point TV networks",
"type": "utility",
"start": 10500000000,
"end": 10680000000
},
{
"name": "Digital network (fixed)",
"type": "utility",
"start": 10680000000,
"end": 11700000000
},
{
"name": "TV satellite",
"type": "satellite",
"start": 11700000000,
"end": 12500000000
},
{
"name": "Digital network (fixed)",
"type": "utility",
"start": 12500000000,
"end": 13250000000
},
{
"name": "Satellite Uplink",
"type": "satellite",
"start": 14000000000,
"end": 14500000000
},
{
"name": "Digital network (fixed)",
"type": "utility",
"start": 14500000000,
"end": 14620000000
},
{
"name": "Digital network (fixed)",
"type": "utility",
"start": 15230000000,
"end": 15350000000
},
{
"name": "Digital network (poit to point)",
"type": "utility",
"start": 17100000000,
"end": 19300000000
},
{
"name": "Feeder link",
"type": "satellite",
"start": 19300000000,
"end": 19700000000
},
{
"name": "HEST, LEST, ESIM, ESOMP",
"type": "satellite",
"start": 19700000000,
"end": 20200000000
},
{
"name": "Digital network (fixed)",
"type": "utility",
"start": 22000000000,
"end": 22330000000
},
{
"name": "Digital network (fixed)",
"type": "utility",
"start": 22674750000,
"end": 2283350000
},
{
"name": "Digital network (fixed), SAP/SAB",
"type": "utility",
"start": 22926750000,
"end": 23150000000
},
{
"name": "Digital network (fixed)",
"type": "utility",
"start": 23150000000,
"end": 23338000000
},
{
"name": "ISM, SRD and LPR",
"type": "utility",
"start": 24000000000,
"end": 24450000000
},
{
"name": "Digital network (point to point, multipoint)",
"type": "utility",
"start": 24450000000,
"end": 25109000000
},
{
"name": "LPR, SRD and SRR",
"type": "utility",
"start": 25109000000,
"end": 2544500000
},
{
"name": "Network (point to point, multipoint)",
"type": "utility",
"start": 2544500000,
"end": 2611700000
},
{
"name": "LPR, SRD and SRR",
"type": "utility",
"start": 2611700000,
"end": 2650000000
},
{
"name": "Terrestrial electric utility",
"type": "utility",
"start": 26500000000,
"end": 27500000000
},
{
"name": "Network (point to point, multipoint)",
"type": "utility",
"start": 27500000000,
"end": 29100000000
},
{
"name": "Network (point to point, multipoint)",
"type": "utility",
"start": 29100000000,
"end": 29500000000
},
{
"name": "Network (point to point, multipoint)",
"type": "utility",
"start": 31000000000,
"end": 31300000000
},
{
"name": "Network (high density, fixed)",
"type": "utility",
"start": 31983000000,
"end": 32599000000
},
{
"name": "Network (high density, fixed)",
"type": "utility",
"start": 32795000000,
"end": 33400000000
},
{
"name": "Network (high density, fixed)",
"type": "utility",
"start": 37338000000,
"end": 38300000000
},
{
"name": "Network (high density, fixed)",
"type": "utility",
"start": 38590000000,
"end": 39500000000
},
{
"name": "FWS systems (fixed)",
"type": "utility",
"start": 40500000000,
"end": 43500000000
},
{
"name": "Radio Ham 6 mm band",
"type": "amateur",
"start": 47000000000,
"end": 47200000000
},
{
"name": "Network (high density, fixed)",
"type": "utility",
"start": 51400000000,
"end": 52600000000
},
{
"name": "Network (high density, fixed)",
"type": "utility",
"start": 55780000000,
"end": 61000000000
},
{
"name": "ISM",
"type": "utility",
"start": 61000000000,
"end": 64000000000
},
{
"name": "Network (high density, fixed)",
"type": "utility",
"start": 64000000000,
"end": 66000000000
},
{
"name": "Links (high density, fixed)",
"type": "utility",
"start": 71000000000,
"end": 74000000000
},
{
"name": "LPR, SRD, SRR, TLPR, vehichle radar",
"type": "utility",
"start": 74000000000,
"end": 76500000000
},
{
"name": "Radio Ham 4 mm band",
"type": "amateur",
"start": 76500000000,
"end": 81500000000
},
{
"name": "Links (high density, fixed)",
"type": "utility",
"start": 84000000000,
"end": 86000000000
},
{
"name": "ISM",
"type": "utility",
"start": 120200000000,
"end": 122250000000
},
{
"name": "Radio Ham 2.5 mm band",
"type": "amateur",
"start": 122250000000,
"end": 123000000000
},
{
"name": "Radio Ham 2 mm band",
"type": "amateur",
"start": 134000000000,
"end": 141000000000
},
{
"name": "Radio Ham 1 mm band",
"type": "amateur",
"start": 241000000000,
"end": 250000000000
}
]
}

View File

@ -31,6 +31,10 @@ public:
monoPacker.init(&s2m.out, 512);
stereoPacker.init(_stream->sinkOut, 512);
#if RTAUDIO_VERSION_MAJOR >= 6
audio.setErrorCallback(&errorCallback);
#endif
bool created = false;
std::string device = "";
config.acquire();
@ -42,12 +46,18 @@ public:
device = config.conf[_streamName]["device"];
config.release(created);
int count = audio.getDeviceCount();
RtAudio::DeviceInfo info;
#if RTAUDIO_VERSION_MAJOR >= 6
for (int i : audio.getDeviceIds()) {
#else
int count = audio.getDeviceCount();
for (int i = 0; i < count; i++) {
#endif
try {
info = audio.getDeviceInfo(i);
#if !defined(RTAUDIO_VERSION_MAJOR) || RTAUDIO_VERSION_MAJOR < 6
if (!info.probed) { continue; }
#endif
if (info.outputChannels == 0) { continue; }
if (info.isDefaultOutput) { defaultDevId = devList.size(); }
devList.push_back(info);
@ -55,8 +65,8 @@ public:
txtDevList += info.name;
txtDevList += '\0';
}
catch (std::exception e) {
flog::error("AudioSinkModule Error getting audio device info: {0}", e.what());
catch (const std::exception& e) {
flog::error("AudioSinkModule Error getting audio device ({}) info: {}", i, e.what());
}
}
selectByName(device);
@ -156,6 +166,22 @@ public:
}
}
#if RTAUDIO_VERSION_MAJOR >= 6
static void errorCallback(RtAudioErrorType type, const std::string& errorText) {
switch (type) {
case RtAudioErrorType::RTAUDIO_NO_ERROR:
return;
case RtAudioErrorType::RTAUDIO_WARNING:
case RtAudioErrorType::RTAUDIO_NO_DEVICES_FOUND:
case RtAudioErrorType::RTAUDIO_DEVICE_DISCONNECT:
flog::warn("AudioSinkModule Warning: {} ({})", errorText, (int)type);
break;
default:
throw std::runtime_error(errorText);
}
}
#endif
private:
bool doStart() {
RtAudio::StreamParameters parameters;
@ -172,8 +198,8 @@ private:
audio.startStream();
stereoPacker.start();
}
catch (RtAudioError& e) {
flog::error("Could not open audio device");
catch (const std::exception& e) {
flog::error("Could not open audio device {0}", e.what());
return false;
}
@ -198,14 +224,6 @@ private:
int count = _this->stereoPacker.out.read();
if (count < 0) { return 0; }
// For debug purposes only...
// if (nBufferFrames != count) { flog::warn("Buffer size mismatch, wanted {0}, was asked for {1}", count, nBufferFrames); }
// for (int i = 0; i < count; i++) {
// if (_this->stereoPacker.out.readBuf[i].l == NAN || _this->stereoPacker.out.readBuf[i].r == NAN) { flog::error("NAN in audio data"); }
// if (_this->stereoPacker.out.readBuf[i].l == INFINITY || _this->stereoPacker.out.readBuf[i].r == INFINITY) { flog::error("INFINITY in audio data"); }
// if (_this->stereoPacker.out.readBuf[i].l == -INFINITY || _this->stereoPacker.out.readBuf[i].r == -INFINITY) { flog::error("-INFINITY in audio data"); }
// }
memcpy(outputBuffer, _this->stereoPacker.out.readBuf, nBufferFrames * sizeof(dsp::stereo_t));
_this->stereoPacker.out.flush();
return 0;

View File

@ -141,10 +141,10 @@ public:
return;
}
}
catch (std::exception e) {
catch (const std::exception& e) {
char buf[1024];
sprintf(buf, "%016" PRIX64, serial);
flog::error("Could not open Airspy {0}", buf);
flog::error("Could not open Airspy {}", buf);
}
selectedSerial = serial;

View File

@ -144,10 +144,10 @@ public:
return;
}
}
catch (std::exception e) {
catch (const std::exception& e) {
char buf[1024];
sprintf(buf, "%016" PRIX64, serial);
flog::error("Could not open Airspy HF+ {0}", buf);
flog::error("Could not open Airspy HF+ {}", buf);
}
selectedSerial = serial;

View File

@ -35,6 +35,10 @@ public:
AudioSourceModule(std::string name) {
this->name = name;
#if RTAUDIO_VERSION_MAJOR >= 6
audio.setErrorCallback(&errorCallback);
#endif
sampleRate = 48000.0;
handler.ctx = this;
@ -83,21 +87,28 @@ public:
void refresh() {
devices.clear();
#if RTAUDIO_VERSION_MAJOR >= 6
for (int i : audio.getDeviceIds()) {
#else
int count = audio.getDeviceCount();
for (int i = 0; i < count; i++) {
#endif
try {
// Get info
auto info = audio.getDeviceInfo(i);
#if !defined(RTAUDIO_VERSION_MAJOR) || RTAUDIO_VERSION_MAJOR < 6
if (!info.probed) { continue; }
#endif
// Check that it has a stereo input
if (info.probed && info.inputChannels < 2) { continue; }
if (info.inputChannels < 2) { continue; }
// Save info
DeviceInfo dinfo = { info, i };
devices.define(info.name, info.name, dinfo);
}
catch (std::exception e) {
flog::error("Error getting audio device info: {0}", e.what());
catch (const std::exception& e) {
flog::error("Error getting audio device ({}) info: {}", i, e.what());
}
}
}
@ -189,11 +200,11 @@ private:
_this->audio.startStream();
_this->running = true;
}
catch (std::exception e) {
flog::error("Error opening audio device: {0}", e.what());
catch (const std::exception& e) {
flog::error("Error opening audio device: {}", e.what());
}
flog::info("AudioSourceModule '{0}': Start!", _this->name);
flog::info("AudioSourceModule '{}': Start!", _this->name);
}
static void stop(void* ctx) {
@ -254,6 +265,22 @@ private:
return 0;
}
#if RTAUDIO_VERSION_MAJOR >= 6
static void errorCallback(RtAudioErrorType type, const std::string& errorText) {
switch (type) {
case RtAudioErrorType::RTAUDIO_NO_ERROR:
return;
case RtAudioErrorType::RTAUDIO_WARNING:
case RtAudioErrorType::RTAUDIO_NO_DEVICES_FOUND:
case RtAudioErrorType::RTAUDIO_DEVICE_DISCONNECT:
flog::warn("AudioSourceModule Warning: {} ({})", errorText, (int)type);
break;
default:
throw std::runtime_error(errorText);
}
}
#endif
std::string name;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;

View File

@ -139,8 +139,8 @@ private:
//gui::freqSelect.maxFreq = _this->centerFreq + (_this->sampleRate/2);
//gui::freqSelect.limitFreq = true;
}
catch (std::exception& e) {
flog::error("Error: {0}", e.what());
catch (const std::exception& e) {
flog::error("Error: {}", e.what());
}
config.acquire();
config.conf["path"] = _this->fileSelect.path;

View File

@ -2,6 +2,13 @@
#include <utils/flog.h>
namespace hermes {
const int SAMPLERATE_LIST[] = {
48000,
96000,
192000,
384000
};
Client::Client(std::shared_ptr<net::Socket> sock) {
this->sock = sock;
@ -33,6 +40,7 @@ namespace hermes {
void Client::setSamplerate(HermesLiteSamplerate samplerate) {
writeReg(0, (uint32_t)samplerate << 24);
blockSize = SAMPLERATE_LIST[samplerate] / 200;
}
void Client::setFrequency(double freq) {
@ -157,12 +165,15 @@ namespace hermes {
void Client::worker() {
uint8_t rbuf[2048];
MetisUSBPacket* pkt = (MetisUSBPacket*)rbuf;
int sampleCount = 0;
while (true) {
// Wait for a packet or exit if connection closed
int len = sock->recv(rbuf, 2048);
if (len <= 0) { break; }
// Ignore anything that's not a USB packet
// TODO: Gotta check the endpoint
if (htons(pkt->hdr.signature) != HERMES_METIS_SIGNATURE || pkt->hdr.type != METIS_PKT_USB) {
continue;
}
@ -183,9 +194,10 @@ namespace hermes {
flog::warn("Got response! Reg={0}, Seq={1}", reg, (uint32_t)htonl(pkt->seq));
}
// Decode and send IQ to stream
// Decode and save IQ to buffer
uint8_t* iq = &frame[8];
for (int i = 0; i < 63; i++) {
dsp::complex_t* writeBuf = &out.writeBuf[sampleCount];
for (int i = 0; i < HERMES_SAMPLES_PER_FRAME; i++) {
// Convert to 32bit
int32_t si = ((uint32_t)iq[(i*8) + 0] << 16) | ((uint32_t)iq[(i*8) + 1] << 8) | (uint32_t)iq[(i*8) + 2];
int32_t sq = ((uint32_t)iq[(i*8) + 3] << 16) | ((uint32_t)iq[(i*8) + 4] << 8) | (uint32_t)iq[(i*8) + 5];
@ -195,18 +207,23 @@ namespace hermes {
sq = (sq << 8) >> 8;
// Convert to float (IQ swapped for some reason)
out.writeBuf[i].im = (float)si / (float)0x1000000;
out.writeBuf[i].re = (float)sq / (float)0x1000000;
writeBuf[i].im = (float)si / (float)0x1000000;
writeBuf[i].re = (float)sq / (float)0x1000000;
}
sampleCount += HERMES_SAMPLES_PER_FRAME;
// If enough samples are in the buffer, send to stream
if (sampleCount >= blockSize) {
out.swap(sampleCount);
sampleCount = 0;
}
out.swap(63);
// TODO: Buffer the data to avoid having a very high DSP frame rate
}
}
}
std::vector<Info> discover() {
// TODO: Maybe try to instead detect on each interface as a work around for 0.0.0.0 not receiving anything?
auto sock = net::openudp("0.0.0.0", 1024);
// Open a UDP broadcast socket (TODO: Figure out why 255.255.255.255 doesn't work on windows with local = 0.0.0.0)
auto sock = net::openudp("255.255.255.255", 1024, "0.0.0.0", 0, true);
// Build discovery packet
uint8_t discoveryPkt[64];
@ -225,6 +242,7 @@ namespace hermes {
}
}
// Await all responses
std::vector<Info> devices;
while (true) {
// Wait for a response
@ -259,6 +277,8 @@ namespace hermes {
devices.push_back(info);
}
// Close broadcast socket
sock->close();
return devices;
}

View File

@ -7,11 +7,12 @@
#include <string>
#include <thread>
#define HERMES_METIS_REPEAT 5
#define HERMES_METIS_TIMEOUT 1000
#define HERMES_METIS_SIGNATURE 0xEFFE
#define HERMES_HPSDR_USB_SYNC 0x7F
#define HERMES_I2C_DELAY 50
#define HERMES_METIS_REPEAT 5
#define HERMES_METIS_TIMEOUT 1000
#define HERMES_METIS_SIGNATURE 0xEFFE
#define HERMES_HPSDR_USB_SYNC 0x7F
#define HERMES_I2C_DELAY 50
#define HERMES_SAMPLES_PER_FRAME 63
namespace hermes {
enum MetisPacketType {
@ -140,7 +141,7 @@ namespace hermes {
dsp::stream<dsp::complex_t> out;
//private:
private:
void sendMetisUSB(uint8_t endpoint, void* frame0, void* frame1 = NULL);
void sendMetisControl(MetisControl ctrl);
@ -149,12 +150,12 @@ namespace hermes {
void writeI2C(I2CPort port, uint8_t addr, uint8_t reg, uint8_t data);
void worker();
double freq = 0;
int blockSize = 63;
std::thread workerThread;
std::shared_ptr<net::Socket> sock;
uint32_t usbSeq = 0;

View File

@ -17,7 +17,7 @@ SDRPP_MOD_INFO{
/* Name: */ "hermes_source",
/* Description: */ "Hermes Lite 2 source module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Version: */ 0, 1, 1,
/* Max instances */ 1
};

View File

@ -7,7 +7,9 @@
#include <gui/smgui.h>
#include <iio.h>
#include <ad9361.h>
#include <utils/optionlist.h>
#include <algorithm>
#include <regex>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -15,16 +17,10 @@ SDRPP_MOD_INFO{
/* Name: */ "plutosdr_source",
/* Description: */ "PlutoSDR source module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Version: */ 0, 2, 0,
/* Max instances */ 1
};
const char* gainModes[] = {
"manual", "fast_attack", "slow_attack", "hybrid"
};
const char* gainModesTxt = "Manual\0Fast Attack\0Slow Attack\0Hybrid\0";
ConfigManager config;
class PlutoSDRSourceModule : public ModuleManager::Instance {
@ -32,34 +28,34 @@ public:
PlutoSDRSourceModule(std::string name) {
this->name = name;
// Define valid samplerates
for (int sr = 1000000; sr <= 61440000; sr += 500000) {
samplerates.define(sr, getBandwdithScaled(sr), sr);
}
samplerates.define(61440000, getBandwdithScaled(61440000.0), 61440000.0);
// Define valid bandwidths
bandwidths.define(0, "Auto", 0);
for (int bw = 1000000.0; bw <= 52000000; bw += 500000) {
bandwidths.define(bw, getBandwdithScaled(bw), bw);
}
// Define gain modes
gainModes.define("manual", "Manual", "manual");
gainModes.define("fast_attack", "Fast Attack", "fast_attack");
gainModes.define("slow_attack", "Slow Attack", "slow_attack");
gainModes.define("hybrid", "Hybrid", "hybrid");
// Enumerate devices
refresh();
// Select device
config.acquire();
std::string _ip = config.conf["IP"];
strcpy(&ip[3], _ip.c_str());
sampleRate = config.conf["sampleRate"];
gainMode = config.conf["gainMode"];
gain = config.conf["gain"];
devDesc = config.conf["device"];
config.release();
select(devDesc);
// Generate the samplerate list and find srId
bool found = false;
int id = 0;
for (double sr = 1000000; sr <= 20000000; sr += 500000) {
sampleRates.push_back(sr);
sampleRatesTxt += getBandwdithScaled(sr);
sampleRatesTxt += '\0';
if (sr == sampleRate) {
found = true;
srId = id;
}
id++;
}
if (!found) {
srId = 0;
sampleRate = sampleRates[0];
}
// Register source
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
@ -105,9 +101,157 @@ private:
return std::string(buf);
}
void refresh() {
// Clear device list
devices.clear();
// Create scan context
iio_scan_context* sctx = iio_create_scan_context(NULL, 0);
if (!sctx) {
flog::error("Failed get scan context");
return;
}
// Create parsing regexes
std::regex backendRgx(".+(?=:)", std::regex::ECMAScript);
std::regex modelRgx("\\(.+(?=\\),)", std::regex::ECMAScript);
std::regex serialRgx("serial=[0-9A-Za-z]+", std::regex::ECMAScript);
// Enumerate devices
iio_context_info** ctxInfoList;
ssize_t count = iio_scan_context_get_info_list(sctx, &ctxInfoList);
if (count < 0) {
flog::error("Failed to enumerate contexts");
return;
}
for (ssize_t i = 0; i < count; i++) {
iio_context_info* info = ctxInfoList[i];
std::string desc = iio_context_info_get_description(info);
std::string duri = iio_context_info_get_uri(info);
// If the device is not a plutosdr, don't include it
if (desc.find("PlutoSDR") == std::string::npos) {
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
continue;
}
// Extract the backend
std::string backend = "unknown";
std::smatch backendMatch;
if (std::regex_search(duri, backendMatch, backendRgx)) {
backend = backendMatch[0];
}
// Extract the model
std::string model = "Unknown";
std::smatch modelMatch;
if (std::regex_search(desc, modelMatch, modelRgx)) {
model = modelMatch[0];
int parenthPos = model.find('(');
if (parenthPos != std::string::npos) {
model = model.substr(parenthPos+1);
}
}
// Extract the serial
std::string serial = "unknown";
std::smatch serialMatch;
if (std::regex_search(desc, serialMatch, serialRgx)) {
serial = serialMatch[0].str().substr(7);
}
// Construct the device name
std::string devName = '(' + backend + ") " + model + " [" + serial + ']';
// Save device
devices.define(desc, devName, duri);
}
iio_context_info_list_free(ctxInfoList);
// Destroy scan context
iio_scan_context_destroy(sctx);
#ifdef __ANDROID__
// On Android, a default IP entry must be made (TODO: This is not ideal since the IP cannot be changed)
const char* androidURI = "ip:192.168.2.1";
const char* androidName = "Default (192.168.2.1)";
devices.define(androidName, androidName, androidURI);
#endif
}
void select(const std::string& desc) {
// If no device is available, give up
if (devices.empty()) {
devDesc.clear();
return;
}
// If the device is not available, select the first one
if (!devices.keyExists(desc)) {
select(devices.key(0));
}
// Update URI
devDesc = desc;
uri = devices.value(devices.keyId(desc));
// TODO: Enumerate capabilities
// Load defaults
samplerate = 4000000;
bandwidth = 0;
gmId = 0;
gain = -1.0f;
// Load device config
config.acquire();
if (config.conf["devices"][devDesc].contains("samplerate")) {
samplerate = config.conf["devices"][devDesc]["samplerate"];
}
if (config.conf["devices"][devDesc].contains("bandwidth")) {
bandwidth = config.conf["devices"][devDesc]["bandwidth"];
}
if (config.conf["devices"][devDesc].contains("gainMode")) {
// Select given gain mode or default if invalid
std::string gm = config.conf["devices"][devDesc]["gainMode"];
if (gainModes.keyExists(gm)) {
gmId = gainModes.keyId(gm);
}
else {
gmId = 0;
}
}
if (config.conf["devices"][devDesc].contains("gain")) {
gain = config.conf["devices"][devDesc]["gain"];
gain = std::clamp<int>(gain, -1.0f, 73.0f);
}
config.release();
// Update samplerate ID
if (samplerates.keyExists(samplerate)) {
srId = samplerates.keyId(samplerate);
}
else {
srId = 0;
samplerate = samplerates.value(srId);
}
// Update bandwidth ID
if (bandwidths.keyExists(bandwidth)) {
bwId = bandwidths.keyId(bandwidth);
}
else {
bwId = 0;
bandwidth = bandwidths.value(bwId);
}
// Update core samplerate
core::setInputSampleRate(samplerate);
}
static void menuSelected(void* ctx) {
PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
core::setInputSampleRate(_this->samplerate);
flog::info("PlutoSDRSourceModule '{0}': Menu Select!", _this->name);
}
@ -120,12 +264,17 @@ private:
PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx;
if (_this->running) { return; }
// TODO: INIT CONTEXT HERE
_this->ctx = iio_create_context_from_uri(_this->ip);
// If no device is selected, give up
if (_this->devDesc.empty() || _this->uri.empty()) { return; }
// Open context
_this->ctx = iio_create_context_from_uri(_this->uri.c_str());
if (_this->ctx == NULL) {
flog::error("Could not open pluto");
flog::error("Could not open pluto ({})", _this->uri);
return;
}
// Get phy and device handle
_this->phy = iio_context_find_device(_this->ctx, "ad9361-phy");
if (_this->phy == NULL) {
flog::error("Could not connect to pluto phy");
@ -139,17 +288,27 @@ private:
return;
}
// Configure pluto
// Get RX channels
_this->rxChan = iio_device_find_channel(_this->phy, "voltage0", false);
_this->rxLO = iio_device_find_channel(_this->phy, "altvoltage0", true);
// Enable RX LO and disable TX
iio_channel_attr_write_bool(iio_device_find_channel(_this->phy, "altvoltage1", true), "powerdown", true);
iio_channel_attr_write_bool(iio_device_find_channel(_this->phy, "altvoltage0", true), "powerdown", false);
iio_channel_attr_write_bool(_this->rxLO, "powerdown", false);
iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "rf_port_select", "A_BALANCED");
iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(_this->freq)); // Freq
iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "sampling_frequency", round(_this->sampleRate)); // Sample rate
iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", gainModes[_this->gainMode]); // manual gain
iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain)); // gain
ad9361_set_bb_rate(_this->phy, round(_this->sampleRate));
// Configure RX channel
iio_channel_attr_write(_this->rxChan, "rf_port_select", "A_BALANCED");
iio_channel_attr_write_longlong(_this->rxLO, "frequency", round(_this->freq)); // Freq
iio_channel_attr_write_bool(_this->rxChan, "filter_fir_en", true); // Digital filter
iio_channel_attr_write_longlong(_this->rxChan, "sampling_frequency", round(_this->samplerate)); // Sample rate
iio_channel_attr_write_double(_this->rxChan, "hardwaregain", _this->gain); // Gain
iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gmId).c_str()); // Gain mode
_this->setBandwidth(_this->bandwidth);
// Configure the ADC filters
ad9361_set_bb_rate(_this->phy, round(_this->samplerate));
// Start worker thread
_this->running = true;
_this->workerThread = std::thread(worker, _this);
flog::info("PlutoSDRSourceModule '{0}': Start!", _this->name);
@ -158,12 +317,14 @@ private:
static void stop(void* ctx) {
PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx;
if (!_this->running) { return; }
// Stop worker thread
_this->running = false;
_this->stream.stopWriter();
_this->workerThread.join();
_this->stream.clearWriteStop();
// DESTROY CONTEXT HERE
// Close device
if (_this->ctx != NULL) {
iio_context_destroy(_this->ctx);
_this->ctx = NULL;
@ -176,8 +337,8 @@ private:
PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx;
_this->freq = freq;
if (_this->running) {
// SET PLUTO FREQ HERE
iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "altvoltage0", true), "frequency", round(freq));
// Tune device
iio_channel_attr_write_longlong(_this->rxLO, "frequency", round(freq));
}
flog::info("PlutoSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
@ -186,120 +347,184 @@ private:
PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("IP");
SmGui::FillWidth();
if (SmGui::InputText(CONCAT("##_pluto_ip_", _this->name), &_this->ip[3], 16)) {
SmGui::ForceSync();
if (SmGui::Combo("##plutosdr_dev_sel", &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
config.acquire();
config.conf["IP"] = &_this->ip[3];
config.conf["device"] = _this->devices.key(_this->devId);
config.release(true);
}
SmGui::LeftLabel("Samplerate");
if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->samplerate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->samplerate);
if (!_this->devDesc.empty()) {
config.acquire();
config.conf["devices"][_this->devDesc]["samplerate"] = _this->samplerate;
config.release(true);
}
}
// Refresh button
SmGui::SameLine();
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_pluto_sr_", _this->name), &_this->srId, _this->sampleRatesTxt.c_str())) {
_this->sampleRate = _this->sampleRates[_this->srId];
core::setInputSampleRate(_this->sampleRate);
config.acquire();
config.conf["sampleRate"] = _this->sampleRate;
config.release(true);
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_pluto_refr_", _this->name))) {
_this->refresh();
_this->select(_this->devDesc);
}
if (_this->running) { SmGui::EndDisabled(); }
SmGui::LeftLabel("Bandwidth");
SmGui::FillWidth();
if (SmGui::Combo(CONCAT("##_pluto_bw_", _this->name), &_this->bwId, _this->bandwidths.txt)) {
_this->bandwidth = _this->bandwidths.value(_this->bwId);
if (_this->running) {
_this->setBandwidth(_this->bandwidth);
}
if (!_this->devDesc.empty()) {
config.acquire();
config.conf["devices"][_this->devDesc]["bandwidth"] = _this->bandwidth;
config.release(true);
}
}
SmGui::LeftLabel("Gain Mode");
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_gainmode_select_", _this->name), &_this->gainMode, gainModesTxt)) {
if (SmGui::Combo(CONCAT("##_pluto_gainmode_select_", _this->name), &_this->gmId, _this->gainModes.txt)) {
if (_this->running) {
iio_channel_attr_write(iio_device_find_channel(_this->phy, "voltage0", false), "gain_control_mode", gainModes[_this->gainMode]);
iio_channel_attr_write(_this->rxChan, "gain_control_mode", _this->gainModes.value(_this->gmId).c_str());
}
if (!_this->devDesc.empty()) {
config.acquire();
config.conf["devices"][_this->devDesc]["gainMode"] = _this->gainModes.key(_this->gmId);
config.release(true);
}
config.acquire();
config.conf["gainMode"] = _this->gainMode;
config.release(true);
}
SmGui::LeftLabel("PGA Gain");
if (_this->gainMode) { SmGui::BeginDisabled(); }
SmGui::LeftLabel("Gain");
if (_this->gmId) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
if (SmGui::SliderFloat(CONCAT("##_gain_select_", _this->name), &_this->gain, 0, 76)) {
if (SmGui::SliderFloatWithSteps(CONCAT("##_pluto_gain__", _this->name), &_this->gain, -1.0f, 73.0f, 1.0f, SmGui::FMT_STR_FLOAT_DB_NO_DECIMAL)) {
if (_this->running) {
iio_channel_attr_write_longlong(iio_device_find_channel(_this->phy, "voltage0", false), "hardwaregain", round(_this->gain));
iio_channel_attr_write_double(_this->rxChan, "hardwaregain", _this->gain);
}
if (!_this->devDesc.empty()) {
config.acquire();
config.conf["devices"][_this->devDesc]["gain"] = _this->gain;
config.release(true);
}
config.acquire();
config.conf["gain"] = _this->gain;
config.release(true);
}
if (_this->gainMode) { SmGui::EndDisabled(); }
if (_this->gmId) { SmGui::EndDisabled(); }
}
void setBandwidth(int bw) {
if (bw > 0) {
iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", bw);
}
else {
iio_channel_attr_write_longlong(rxChan, "rf_bandwidth", std::min<int>(samplerate, 52000000));
}
}
static void worker(void* ctx) {
PlutoSDRSourceModule* _this = (PlutoSDRSourceModule*)ctx;
int blockSize = _this->sampleRate / 200.0f;
int blockSize = _this->samplerate / 200.0f;
struct iio_channel *rx0_i, *rx0_q;
struct iio_buffer* rxbuf;
rx0_i = iio_device_find_channel(_this->dev, "voltage0", 0);
rx0_q = iio_device_find_channel(_this->dev, "voltage1", 0);
// Acquire channels
iio_channel* rx0_i = iio_device_find_channel(_this->dev, "voltage0", 0);
iio_channel* rx0_q = iio_device_find_channel(_this->dev, "voltage1", 0);
if (!rx0_i || !rx0_q) {
flog::error("Failed to acquire RX channels");
return;
}
// Start streaming
iio_channel_enable(rx0_i);
iio_channel_enable(rx0_q);
rxbuf = iio_device_create_buffer(_this->dev, blockSize, false);
// Allocate buffer
iio_buffer* rxbuf = iio_device_create_buffer(_this->dev, blockSize, false);
if (!rxbuf) {
flog::error("Could not create RX buffer");
return;
}
// Receive loop
while (true) {
// Read samples here
// TODO: RECEIVE HERE
// Read samples
iio_buffer_refill(rxbuf);
// Get buffer pointer
int16_t* buf = (int16_t*)iio_buffer_first(rxbuf, rx0_i);
if (!buf) { break; }
for (int i = 0; i < blockSize; i++) {
_this->stream.writeBuf[i].re = (float)buf[i * 2] / 32768.0f;
_this->stream.writeBuf[i].im = (float)buf[(i * 2) + 1] / 32768.0f;
}
// Convert samples to CF32
volk_16i_s32f_convert_32f((float*)_this->stream.writeBuf, buf, 32768.0f, blockSize * 2);
// Send out the samples
if (!_this->stream.swap(blockSize)) { break; };
}
// Stop streaming
iio_channel_disable(rx0_i);
iio_channel_disable(rx0_q);
// Free buffer
iio_buffer_destroy(rxbuf);
}
std::string name;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;
float sampleRate;
SourceManager::SourceHandler handler;
std::thread workerThread;
struct iio_context* ctx = NULL;
struct iio_device* phy = NULL;
struct iio_device* dev = NULL;
iio_context* ctx = NULL;
iio_device* phy = NULL;
iio_device* dev = NULL;
iio_channel* rxLO = NULL;
iio_channel* rxChan = NULL;
bool running = false;
bool ipMode = true;
double freq;
char ip[1024] = "ip:192.168.2.1";
int gainMode = 0;
float gain = 0;
int srId = 0;
std::vector<double> sampleRates;
std::string sampleRatesTxt;
std::string devDesc = "";
std::string uri = "";
double freq;
int samplerate = 4000000;
int bandwidth = 0;
float gain = -1;
int devId = 0;
int srId = 0;
int bwId = 0;
int gmId = 0;
OptionList<std::string, std::string> devices;
OptionList<int, double> samplerates;
OptionList<int, double> bandwidths;
OptionList<std::string, std::string> gainModes;
};
MOD_EXPORT void _INIT_() {
json defConf;
defConf["IP"] = "192.168.2.1";
defConf["sampleRate"] = 4000000.0f;
defConf["gainMode"] = 0;
defConf["gain"] = 0.0f;
json defConf = {};
defConf["device"] = "";
defConf["devices"] = {};
config.setPath(core::args["root"].s() + "/plutosdr_source_config.json");
config.load(defConf);
config.enableAutoSave();
// Reset the configuration if the old format is still used
config.acquire();
if (!config.conf.contains("device") || !config.conf.contains("devices")) {
config.conf = defConf;
config.release(true);
}
else {
config.release();
}
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {

View File

@ -17,7 +17,7 @@ SDRPP_MOD_INFO{
/* Name: */ "rfspace_source",
/* Description: */ "RFspace source module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Version: */ 0, 1, 1,
/* Max instances */ 1
};
@ -154,8 +154,8 @@ private:
_this->client = rfspace::connect(_this->hostname, _this->port, &_this->stream);
_this->deviceInit();
}
catch (std::exception e) {
flog::error("Could not connect to SDR: {0}", e.what());
catch (const std::exception& e) {
flog::error("Could not connect to SDR: {}", e.what());
}
}
else if (connected && SmGui::Button("Disconnect##rfspace_source")) {
@ -231,7 +231,7 @@ private:
}
// Create samplerate list
auto srs = client->getValidSampleRates();
auto srs = client->getSamplerates();
sampleRates.clear();
for (auto& sr : srs) {
sampleRates.define(sr, getBandwdithScaled(sr), sr);
@ -317,7 +317,7 @@ private:
dsp::stream<dsp::complex_t> stream;
SourceManager::SourceHandler handler;
rfspace::RFspaceClient client;
std::shared_ptr<rfspace::Client> client;
};
MOD_EXPORT void _INIT_() {

View File

@ -6,15 +6,13 @@
using namespace std::chrono_literals;
namespace rfspace {
RFspaceClientClass::RFspaceClientClass(net::Conn conn, net::Conn udpConn, dsp::stream<dsp::complex_t>* out) {
client = std::move(conn);
udpClient = std::move(udpConn);
Client::Client(std::shared_ptr<net::Socket> tcp, std::shared_ptr<net::Socket> udp, dsp::stream<dsp::complex_t>* out) {
this->tcp = tcp;
this->udp = udp;
output = out;
// Allocate buffers
rbuffer = new uint8_t[RFSPACE_MAX_SIZE];
sbuffer = new uint8_t[RFSPACE_MAX_SIZE];
ubuffer = new uint8_t[RFSPACE_MAX_SIZE];
// Clear write stop of stream just in case
output->clearWriteStop();
@ -22,9 +20,9 @@ namespace rfspace {
// Send UDP packet so that a router opens the port
sendDummyUDP();
// Start readers
client->readAsync(sizeof(tcpHeader), (uint8_t*)&tcpHeader, tcpHandler, this);
udpClient->readAsync(RFSPACE_MAX_SIZE, ubuffer, udpHandler, this);
// Start workers
tcpWorkerThread = std::thread(&Client::tcpWorker, this);
udpWorkerThread = std::thread(&Client::udpWorker, this);
// Get device ID and wait for response
getControlItem(RFSPACE_CTRL_ITEM_PROD_ID, NULL, 0);
@ -43,22 +41,20 @@ namespace rfspace {
setPort(RFSPACE_RF_PORT_1);
// Start heartbeat
heartBeatThread = std::thread(&RFspaceClientClass::heartBeatWorker, this);
heartBeatThread = std::thread(&Client::heartBeatWorker, this);
}
RFspaceClientClass::~RFspaceClientClass() {
Client::~Client() {
close();
delete[] rbuffer;
delete[] sbuffer;
delete[] ubuffer;
}
void RFspaceClientClass::sendDummyUDP() {
void Client::sendDummyUDP() {
uint8_t dummy = 0x5A;
udpClient->write(1, &dummy);
udp->send(&dummy, 1);
}
int RFspaceClientClass::getControlItem(ControlItem item, void* param, int len) {
int Client::getControlItem(ControlItem item, void* param, int len) {
// Build packet
uint16_t* header = (uint16_t*)&sbuffer[0];
uint16_t* item_val = (uint16_t*)&sbuffer[2];
@ -66,12 +62,12 @@ namespace rfspace {
*item_val = item;
// Send packet
client->write(4, sbuffer);
tcp->send(sbuffer, 4);
return -1;
}
void RFspaceClientClass::setControlItem(ControlItem item, void* param, int len) {
void Client::setControlItem(ControlItem item, void* param, int len) {
// Build packet
uint16_t* header = (uint16_t*)&sbuffer[0];
uint16_t* item_val = (uint16_t*)&sbuffer[2];
@ -80,10 +76,10 @@ namespace rfspace {
memcpy(&sbuffer[4], param, len);
// Send packet
client->write(len + 4, sbuffer);
tcp->send(sbuffer, len + 4);
}
void RFspaceClientClass::setControlItemWithChanID(ControlItem item, uint8_t chanId, void* param, int len) {
void Client::setControlItemWithChanID(ControlItem item, uint8_t chanId, void* param, int len) {
// Build packet
uint16_t* header = (uint16_t*)&sbuffer[0];
uint16_t* item_val = (uint16_t*)&sbuffer[2];
@ -94,10 +90,10 @@ namespace rfspace {
memcpy(&sbuffer[5], param, len);
// Send packet
client->write(len + 5, sbuffer);
tcp->send(sbuffer, len + 5);
}
std::vector<uint32_t> RFspaceClientClass::getValidSampleRates() {
std::vector<uint32_t> Client::getSamplerates() {
std::vector<uint32_t> sr;
switch (deviceId) {
@ -119,92 +115,145 @@ namespace rfspace {
return sr;
}
void RFspaceClientClass::setFrequency(uint64_t freq) {
void Client::setFrequency(uint64_t freq) {
setControlItemWithChanID(RFSPACE_CTRL_ITEM_NCO_FREQUENCY, 0, &freq, 5);
}
void RFspaceClientClass::setPort(RFPort port) {
void Client::setPort(RFPort port) {
uint8_t value = port;
setControlItemWithChanID(RFSPACE_CTRL_ITEM_RF_PORT, 0, &value, sizeof(value));
}
void RFspaceClientClass::setGain(int8_t gain) {
void Client::setGain(int8_t gain) {
setControlItemWithChanID(RFSPACE_CTRL_ITEM_RF_GAIN, 0, &gain, sizeof(gain));
}
void RFspaceClientClass::setSampleRate(uint32_t sampleRate) {
void Client::setSampleRate(uint32_t sampleRate) {
// Acquire the buffer variables
std::lock_guard<std::mutex> lck(bufferMtx);
// Update block size
blockSize = sampleRate / 200;
// Send samplerate to device
setControlItemWithChanID(RFSPACE_CTRL_ITEM_IQ_SAMP_RATE, 0, &sampleRate, sizeof(sampleRate));
}
void RFspaceClientClass::start(SampleFormat sampleFormat, SampleDepth sampleDepth) {
void Client::start(SampleFormat sampleFormat, SampleDepth sampleDepth) {
// Acquire the buffer variables
std::lock_guard<std::mutex> lck(bufferMtx);
// Reset buffer
inBuffer = 0;
// Start device
uint8_t args[4] = { (uint8_t)sampleFormat, (uint8_t)RFSPACE_STATE_RUN, (uint8_t)sampleDepth, 0 };
setControlItem(RFSPACE_CTRL_ITEM_STATE, args, sizeof(args));
}
void RFspaceClientClass::stop() {
void Client::stop() {
uint8_t args[4] = { 0, RFSPACE_STATE_IDLE, 0, 0 };
setControlItem(RFSPACE_CTRL_ITEM_STATE, args, sizeof(args));
}
void RFspaceClientClass::close() {
void Client::close() {
// Stop UDP worker
output->stopWriter();
udp->close();
if (udpWorkerThread.joinable()) { udpWorkerThread.join(); }
output->clearWriteStop();
// Stop heartbeat worker
stopHeartBeat = true;
heartBeatCnd.notify_all();
if (heartBeatThread.joinable()) { heartBeatThread.join(); }
client->close();
udpClient->close();
output->clearWriteStop();
// Stop TCP worker
tcp->close();
if (tcpWorkerThread.joinable()) { tcpWorkerThread.join(); }
}
bool RFspaceClientClass::isOpen() {
return client->isOpen();
bool Client::isOpen() {
return tcp->isOpen() || udp->isOpen();
}
void RFspaceClientClass::tcpHandler(int count, uint8_t* buf, void* ctx) {
RFspaceClientClass* _this = (RFspaceClientClass*)ctx;
uint8_t type = _this->tcpHeader >> 13;
uint16_t size = _this->tcpHeader & 0b1111111111111;
void Client::tcpWorker() {
// Allocate receive buffer
uint8_t* buffer = new uint8_t[RFSPACE_MAX_SIZE];
// Read the rest of the data
if (size > 2) {
_this->client->read(size - 2, &_this->rbuffer[2]);
}
// Receive loop
while (true) {
// Receive header
uint16_t header;
if (tcp->recv((uint8_t*)&header, sizeof(uint16_t), true) <= 0) { break; }
// flog::warn("TCP received: {0} {1}", type, size);
// Decode header
uint8_t type = header >> 13;
uint16_t size = header & 0b1111111111111;
// Check for a device ID
uint16_t* controlItem = (uint16_t*)&_this->rbuffer[2];
if (type == RFSPACE_MSG_TYPE_T2H_SET_CTRL_ITEM_RESP && *controlItem == RFSPACE_CTRL_ITEM_PROD_ID) {
{
std::lock_guard<std::mutex> lck(_this->devIdMtx);
_this->deviceId = (DeviceID)*(uint32_t*)&_this->rbuffer[4];
_this->devIdAvailable = true;
// Receive data
if (tcp->recv(buffer, size - 2, true, RFSPACE_TIMEOUT_MS) <= 0) { break; }
// Check for a device ID
uint16_t* controlItem = (uint16_t*)&buffer[0];
if (type == RFSPACE_MSG_TYPE_T2H_SET_CTRL_ITEM_RESP && *controlItem == RFSPACE_CTRL_ITEM_PROD_ID) {
{
std::lock_guard<std::mutex> lck(devIdMtx);
deviceId = (DeviceID)*(uint32_t*)&buffer[2];
devIdAvailable = true;
}
devIdCnd.notify_all();
}
_this->devIdCnd.notify_all();
}
// Restart an async read
_this->client->readAsync(sizeof(_this->tcpHeader), (uint8_t*)&_this->tcpHeader, tcpHandler, _this);
// Free receive buffer
delete[] buffer;
}
void RFspaceClientClass::udpHandler(int count, uint8_t* buf, void* ctx) {
RFspaceClientClass* _this = (RFspaceClientClass*)ctx;
uint16_t hdr = (uint16_t)buf[0] | ((uint16_t)buf[1] << 8);
uint8_t type = hdr >> 13;
uint16_t size = hdr & 0b1111111111111;
void Client::udpWorker() {
// Allocate receive buffer
uint8_t* buffer = new uint8_t[RFSPACE_MAX_SIZE];
uint16_t* header = (uint16_t*)&buffer[0];
if (type == RFSPACE_MSG_TYPE_T2H_DATA_ITEM_0) {
int16_t* samples = (int16_t*)&buf[4];
int sampCount = (size - 4) / (2 * sizeof(int16_t));
volk_16i_s32f_convert_32f((float*)_this->output->writeBuf, samples, 32768.0f, sampCount * 2);
_this->output->swap(sampCount);
// Receive loop
while (true) {
// Receive datagram
int rsize = udp->recv(buffer, RFSPACE_MAX_SIZE);
if (rsize <= 0) { break; }
// Decode header
uint8_t type = (*header) >> 13;
uint16_t size = (*header) & 0b1111111111111;
if (rsize != size) {
flog::error("Datagram size mismatch: {} vs {}", rsize, size);
continue;
}
// Check for a sample packet
if (type == RFSPACE_MSG_TYPE_T2H_DATA_ITEM_0) {
// Acquire the buffer variables
std::lock_guard<std::mutex> lck(bufferMtx);
// Convert samples to complex float
int16_t* samples = (int16_t*)&buffer[4];
int sampCount = (size - 4) / (2 * sizeof(int16_t));
volk_16i_s32f_convert_32f((float*)&output->writeBuf[inBuffer], samples, 32768.0f, sampCount * 2);
inBuffer += sampCount;
// Send out samples if enough are buffered
if (inBuffer >= blockSize) {
if (!output->swap(inBuffer)) { break; };
inBuffer = 0;
}
}
}
// Restart an async read
_this->udpClient->readAsync(RFSPACE_MAX_SIZE, _this->ubuffer, udpHandler, _this);
// Free receive buffer
delete[] buffer;
}
void RFspaceClientClass::heartBeatWorker() {
void Client::heartBeatWorker() {
uint8_t dummy[4];
while (true) {
getControlItem(RFSPACE_CTRL_ITEM_STATE, dummy, sizeof(dummy));
@ -216,11 +265,9 @@ namespace rfspace {
}
}
RFspaceClient connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out) {
net::Conn conn = net::connect(host, port);
if (!conn) { return NULL; }
net::Conn udpConn = net::openUDP("0.0.0.0", port, host, port, true);
if (!udpConn) { return NULL; }
return RFspaceClient(new RFspaceClientClass(std::move(conn), std::move(udpConn), out));
std::shared_ptr<Client> connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out) {
auto tcp = net::connect(host, port);
auto udp = net::openudp(host, port, "0.0.0.0", port);
return std::make_shared<Client>(tcp, udp, out);
}
}

View File

@ -1,9 +1,10 @@
#pragma once
#include <utils/networking.h>
#include <utils/net.h>
#include <dsp/stream.h>
#include <dsp/types.h>
#include <atomic>
#include <queue>
#include <thread>
#include <vector>
#include <mutex>
#define RFSPACE_MAX_SIZE 8192
#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000
@ -96,10 +97,10 @@ namespace rfspace {
RFSPACE_CTRL_ITEM_ERROR_LOG = 0x0410
};
class RFspaceClientClass {
class Client {
public:
RFspaceClientClass(net::Conn conn, net::Conn udpConn, dsp::stream<dsp::complex_t>* out);
~RFspaceClientClass();
Client(std::shared_ptr<net::Socket> tcp, std::shared_ptr<net::Socket> udp, dsp::stream<dsp::complex_t>* out);
~Client();
void sendDummyUDP();
@ -107,7 +108,7 @@ namespace rfspace {
void setControlItem(ControlItem item, void* param, int len);
void setControlItemWithChanID(ControlItem item, uint8_t chanId, void* param, int len);
std::vector<uint32_t> getValidSampleRates();
std::vector<uint32_t> getSamplerates();
void setFrequency(uint64_t freq);
void setPort(RFPort port);
@ -123,21 +124,22 @@ namespace rfspace {
DeviceID deviceId;
private:
static void tcpHandler(int count, uint8_t* buf, void* ctx);
static void udpHandler(int count, uint8_t* buf, void* ctx);
void tcpWorker();
void udpWorker();
void heartBeatWorker();
net::Conn client;
net::Conn udpClient;
std::shared_ptr<net::Socket> tcp;
std::shared_ptr<net::Socket> udp;
dsp::stream<dsp::complex_t>* output;
uint16_t tcpHeader;
uint16_t udpHeader;
uint8_t* rbuffer = NULL;
uint8_t* sbuffer = NULL;
uint8_t* ubuffer = NULL;
std::thread tcpWorkerThread;
std::thread udpWorkerThread;
std::thread heartBeatThread;
std::mutex heartBeatMtx;
@ -147,10 +149,12 @@ namespace rfspace {
bool devIdAvailable = false;
std::condition_variable devIdCnd;
std::mutex devIdMtx;
std::mutex bufferMtx;
int blockSize = 256;
int inBuffer = 0;
};
typedef std::unique_ptr<RFspaceClientClass> RFspaceClient;
RFspaceClient connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out);
std::shared_ptr<Client> connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out);
}

View File

@ -171,7 +171,7 @@ public:
#ifndef __ANDROID__
int oret = rtlsdr_open(&openDev, id);
#else
int oret = rtlsdr_open_fd(&openDev, devFd);
int oret = rtlsdr_open_sys_dev(&openDev, devFd);
#endif
if (oret < 0) {
@ -285,7 +285,7 @@ private:
#ifndef __ANDROID__
int oret = rtlsdr_open(&_this->openDev, _this->devId);
#else
int oret = rtlsdr_open_fd(&_this->openDev, _this->devFd);
int oret = rtlsdr_open_sys_dev(&_this->openDev, _this->devFd);
#endif
if (oret < 0) {

View File

@ -132,8 +132,8 @@ private:
try {
_this->client = rtltcp::connect(&_this->stream, _this->ip, _this->port);
}
catch (std::exception e) {
flog::error("Could connect to RTL-TCP server: {0}", e.what());
catch (const std::exception& e) {
flog::error("Could connect to RTL-TCP server: {}", e.what());
return;
}

View File

@ -17,7 +17,7 @@ SDRPP_MOD_INFO{
/* Name: */ "sdrpp_server_source",
/* Description: */ "SDR++ Server source module for SDR++",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Version: */ 0, 2, 0,
/* Max instances */ 1
};
@ -109,10 +109,10 @@ private:
SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx;
if (_this->running) { return; }
// Try to connect if not already connected
if (!_this->client) {
// Try to connect if not already connected (Play button is locked anyway so not sure why I put this here)
if (!_this->connected()) {
_this->tryConnect();
if (!_this->client) { return; }
if (!_this->connected()) { return; }
}
// Set configuration
@ -127,7 +127,7 @@ private:
SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx;
if (!_this->running) { return; }
if (_this->client) { _this->client->stop(); }
if (_this->connected()) { _this->client->stop(); }
_this->running = false;
flog::info("SDRPPServerSourceModule '{0}': Stop!", _this->name);
@ -135,7 +135,7 @@ private:
static void tune(double freq, void* ctx) {
SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx;
if (_this->running && _this->client) {
if (_this->running && _this->connected()) {
_this->client->setFrequency(freq);
}
_this->freq = freq;
@ -146,7 +146,7 @@ private:
SDRPPServerSourceModule* _this = (SDRPPServerSourceModule*)ctx;
float menuWidth = ImGui::GetContentRegionAvail().x;
bool connected = (_this->client && _this->client->isOpen());
bool connected = _this->connected();
gui::mainWindow.playButtonLocked = !connected;
ImGui::GenericDialog("##sdrpp_srv_src_err_dialog", _this->serverBusy, GENERIC_DIALOG_BUTTONS_OK, [=](){
@ -227,14 +227,18 @@ private:
}
}
bool connected() {
return client && client->isOpen();
}
void tryConnect() {
try {
if (client) { client.reset(); }
client = server::connect(hostname, port, &stream);
deviceInit();
}
catch (std::exception e) {
flog::error("Could not connect to SDR: {0}", e.what());
catch (const std::exception& e) {
flog::error("Could not connect to SDR: {}", e.what());
if (!strcmp(e.what(), "Server busy")) { serverBusy = true; }
}
}
@ -281,7 +285,7 @@ private:
int sampleTypeId;
bool compression = false;
server::Client client;
std::shared_ptr<server::Client> client;
};
MOD_EXPORT void _INIT_() {

View File

@ -7,8 +7,8 @@
using namespace std::chrono_literals;
namespace server {
ClientClass::ClientClass(net::Conn conn, dsp::stream<dsp::complex_t>* out) {
client = std::move(conn);
Client::Client(std::shared_ptr<net::Socket> sock, dsp::stream<dsp::complex_t>* out) {
this->sock = sock;
output = out;
// Allocate buffers
@ -37,8 +37,8 @@ namespace server {
decomp.start();
link.start();
// Start readers
client->readAsync(sizeof(PacketHeader), rbuffer, tcpHandler, this);
// Start worker thread
workerThread = std::thread(&Client::worker, this);
// Ask for a UI
int res = getUI();
@ -46,14 +46,14 @@ namespace server {
else if (res == -2) { throw std::runtime_error("Server busy"); }
}
ClientClass::~ClientClass() {
Client::~Client() {
close();
ZSTD_freeDCtx(dctx);
delete[] rbuffer;
delete[] sbuffer;
}
void ClientClass::showMenu() {
void Client::showMenu() {
std::string diffId = "";
SmGui::DrawListElem diffValue;
bool syncRequired = false;
@ -96,8 +96,8 @@ namespace server {
}
}
void ClientClass::setFrequency(double freq) {
if (!client || !client->isOpen()) { return; }
void Client::setFrequency(double freq) {
if (!isOpen()) { return; }
*(double*)s_cmd_data = freq;
sendCommand(COMMAND_SET_FREQUENCY, sizeof(double));
auto waiter = awaitCommandAck(COMMAND_SET_FREQUENCY);
@ -105,119 +105,126 @@ namespace server {
waiter->handled();
}
double ClientClass::getSampleRate() {
double Client::getSampleRate() {
return currentSampleRate;
}
void ClientClass::setSampleType(dsp::compression::PCMType type) {
void Client::setSampleType(dsp::compression::PCMType type) {
if (!isOpen()) { return; }
s_cmd_data[0] = type;
sendCommand(COMMAND_SET_SAMPLE_TYPE, 1);
}
void ClientClass::setCompression(bool enabled) {
void Client::setCompression(bool enabled) {
if (!isOpen()) { return; }
s_cmd_data[0] = enabled;
sendCommand(COMMAND_SET_COMPRESSION, 1);
}
void ClientClass::start() {
if (!client || !client->isOpen()) { return; }
void Client::start() {
if (!isOpen()) { return; }
sendCommand(COMMAND_START, 0);
getUI();
}
void ClientClass::stop() {
if (!client || !client->isOpen()) { return; }
void Client::stop() {
if (!isOpen()) { return; }
sendCommand(COMMAND_STOP, 0);
getUI();
}
void ClientClass::close() {
void Client::close() {
// Stop worker
decompIn.stopWriter();
if (sock) { sock->close(); }
if (workerThread.joinable()) { workerThread.join(); }
decompIn.clearWriteStop();
// Stop DSP
decomp.stop();
link.stop();
decompIn.stopWriter();
client->close();
decompIn.clearWriteStop();
}
bool ClientClass::isOpen() {
return client->isOpen();
bool Client::isOpen() {
return sock && sock->isOpen();
}
void ClientClass::tcpHandler(int count, uint8_t* buf, void* ctx) {
ClientClass* _this = (ClientClass*)ctx;
// Read the rest of the data (TODO: CHECK SIZE OR SHIT WILL BE FUCKED)
int len = 0;
int read = 0;
int goal = _this->r_pkt_hdr->size - sizeof(PacketHeader);
while (len < goal) {
read = _this->client->read(goal - len, &buf[sizeof(PacketHeader) + len]);
if (read < 0) {
return;
};
len += read;
}
_this->bytes += _this->r_pkt_hdr->size;
if (_this->r_pkt_hdr->type == PACKET_TYPE_COMMAND) {
// TODO: Move to command handler
if (_this->r_cmd_hdr->cmd == COMMAND_SET_SAMPLERATE && _this->r_pkt_hdr->size == sizeof(PacketHeader) + sizeof(CommandHeader) + sizeof(double)) {
_this->currentSampleRate = *(double*)_this->r_cmd_data;
core::setInputSampleRate(_this->currentSampleRate);
void Client::worker() {
while (true) {
// Receive header
if (sock->recv(rbuffer, sizeof(PacketHeader), true) <= 0) {
break;
}
else if (_this->r_cmd_hdr->cmd == COMMAND_DISCONNECT) {
flog::error("Asked to disconnect by the server");
_this->serverBusy = true;
// Cancel waiters
// Receive remaining data
if (sock->recv(&rbuffer[sizeof(PacketHeader)], r_pkt_hdr->size - sizeof(PacketHeader), true, PROTOCOL_TIMEOUT_MS) <= 0) {
break;
}
// Increment data counter
bytes += r_pkt_hdr->size;
// Decode packet
if (r_pkt_hdr->type == PACKET_TYPE_COMMAND) {
// TODO: Move to command handler
if (r_cmd_hdr->cmd == COMMAND_SET_SAMPLERATE && r_pkt_hdr->size == sizeof(PacketHeader) + sizeof(CommandHeader) + sizeof(double)) {
currentSampleRate = *(double*)r_cmd_data;
core::setInputSampleRate(currentSampleRate);
}
else if (r_cmd_hdr->cmd == COMMAND_DISCONNECT) {
flog::error("Asked to disconnect by the server");
serverBusy = true;
// Cancel waiters
std::vector<PacketWaiter*> toBeRemoved;
for (auto& [waiter, cmd] : commandAckWaiters) {
waiter->cancel();
toBeRemoved.push_back(waiter);
}
// Remove handled waiters
for (auto& waiter : toBeRemoved) {
commandAckWaiters.erase(waiter);
delete waiter;
}
}
}
else if (r_pkt_hdr->type == PACKET_TYPE_COMMAND_ACK) {
// Notify waiters
std::vector<PacketWaiter*> toBeRemoved;
for (auto& [waiter, cmd] : _this->commandAckWaiters) {
waiter->cancel();
for (auto& [waiter, cmd] : commandAckWaiters) {
if (cmd != r_cmd_hdr->cmd) { continue; }
waiter->notify();
toBeRemoved.push_back(waiter);
}
// Remove handled waiters
for (auto& waiter : toBeRemoved) {
_this->commandAckWaiters.erase(waiter);
commandAckWaiters.erase(waiter);
delete waiter;
}
}
}
else if (_this->r_pkt_hdr->type == PACKET_TYPE_COMMAND_ACK) {
// Notify waiters
std::vector<PacketWaiter*> toBeRemoved;
for (auto& [waiter, cmd] : _this->commandAckWaiters) {
if (cmd != _this->r_cmd_hdr->cmd) { continue; }
waiter->notify();
toBeRemoved.push_back(waiter);
else if (r_pkt_hdr->type == PACKET_TYPE_BASEBAND) {
memcpy(decompIn.writeBuf, &rbuffer[sizeof(PacketHeader)], r_pkt_hdr->size - sizeof(PacketHeader));
if (!decompIn.swap(r_pkt_hdr->size - sizeof(PacketHeader))) { break; }
}
// Remove handled waiters
for (auto& waiter : toBeRemoved) {
_this->commandAckWaiters.erase(waiter);
delete waiter;
else if (r_pkt_hdr->type == PACKET_TYPE_BASEBAND_COMPRESSED) {
size_t outCount = ZSTD_decompressDCtx(dctx, decompIn.writeBuf, STREAM_BUFFER_SIZE, r_pkt_data, r_pkt_hdr->size - sizeof(PacketHeader));
if (outCount) {
if (!decompIn.swap(outCount)) { break; }
};
}
else if (r_pkt_hdr->type == PACKET_TYPE_ERROR) {
flog::error("SDR++ Server Error: {0}", rbuffer[sizeof(PacketHeader)]);
}
else {
flog::error("Invalid packet type: {0}", r_pkt_hdr->type);
}
}
else if (_this->r_pkt_hdr->type == PACKET_TYPE_BASEBAND) {
memcpy(_this->decompIn.writeBuf, &buf[sizeof(PacketHeader)], _this->r_pkt_hdr->size - sizeof(PacketHeader));
_this->decompIn.swap(_this->r_pkt_hdr->size - sizeof(PacketHeader));
}
else if (_this->r_pkt_hdr->type == PACKET_TYPE_BASEBAND_COMPRESSED) {
size_t outCount = ZSTD_decompressDCtx(_this->dctx, _this->decompIn.writeBuf, STREAM_BUFFER_SIZE, _this->r_pkt_data, _this->r_pkt_hdr->size - sizeof(PacketHeader));
if (outCount) { _this->decompIn.swap(outCount); };
}
else if (_this->r_pkt_hdr->type == PACKET_TYPE_ERROR) {
flog::error("SDR++ Server Error: {0}", buf[sizeof(PacketHeader)]);
}
else {
flog::error("Invalid packet type: {0}", _this->r_pkt_hdr->type);
}
// Restart an async read
_this->client->readAsync(sizeof(PacketHeader), _this->rbuffer, tcpHandler, _this);
}
int ClientClass::getUI() {
int Client::getUI() {
if (!isOpen()) { return -1; }
auto waiter = awaitCommandAck(COMMAND_GET_UI);
sendCommand(COMMAND_GET_UI, 0);
if (waiter->await(PROTOCOL_TIMEOUT_MS)) {
@ -233,37 +240,35 @@ namespace server {
return 0;
}
void ClientClass::sendPacket(PacketType type, int len) {
void Client::sendPacket(PacketType type, int len) {
s_pkt_hdr->type = type;
s_pkt_hdr->size = sizeof(PacketHeader) + len;
client->write(s_pkt_hdr->size, sbuffer);
sock->send(sbuffer, s_pkt_hdr->size);
}
void ClientClass::sendCommand(Command cmd, int len) {
void Client::sendCommand(Command cmd, int len) {
s_cmd_hdr->cmd = cmd;
sendPacket(PACKET_TYPE_COMMAND, sizeof(CommandHeader) + len);
}
void ClientClass::sendCommandAck(Command cmd, int len) {
void Client::sendCommandAck(Command cmd, int len) {
s_cmd_hdr->cmd = cmd;
sendPacket(PACKET_TYPE_COMMAND_ACK, sizeof(CommandHeader) + len);
}
PacketWaiter* ClientClass::awaitCommandAck(Command cmd) {
PacketWaiter* Client::awaitCommandAck(Command cmd) {
PacketWaiter* waiter = new PacketWaiter;
commandAckWaiters[waiter] = cmd;
return waiter;
}
void ClientClass::dHandler(dsp::complex_t *data, int count, void *ctx) {
ClientClass* _this = (ClientClass*)ctx;
void Client::dHandler(dsp::complex_t *data, int count, void *ctx) {
Client* _this = (Client*)ctx;
memcpy(_this->output->writeBuf, data, count * sizeof(dsp::complex_t));
_this->output->swap(count);
}
Client connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out) {
net::Conn conn = net::connect(host, port);
if (!conn) { return NULL; }
return Client(new ClientClass(std::move(conn), out));
std::shared_ptr<Client> connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out) {
return std::make_shared<Client>(net::connect(host, port), out);
}
}

View File

@ -1,5 +1,5 @@
#pragma once
#include <utils/networking.h>
#include <utils/net.h>
#include <dsp/stream.h>
#include <dsp/types.h>
#include <atomic>
@ -13,10 +13,6 @@
#include <dsp/routing/stream_link.h>
#include <zstd.h>
#define RFSPACE_MAX_SIZE 8192
#define RFSPACE_HEARTBEAT_INTERVAL_MS 1000
#define RFSPACE_TIMEOUT_MS 3000
#define PROTOCOL_TIMEOUT_MS 10000
namespace server {
@ -75,10 +71,10 @@ namespace server {
std::mutex handledMtx;
};
class ClientClass {
class Client {
public:
ClientClass(net::Conn conn, dsp::stream<dsp::complex_t>* out);
~ClientClass();
Client(std::shared_ptr<net::Socket> sock, dsp::stream<dsp::complex_t>* out);
~Client();
void showMenu();
@ -98,7 +94,7 @@ namespace server {
bool serverBusy = false;
private:
static void tcpHandler(int count, uint8_t* buf, void* ctx);
void worker();
int getUI();
@ -112,7 +108,7 @@ namespace server {
static void dHandler(dsp::complex_t *data, int count, void *ctx);
net::Conn client;
std::shared_ptr<net::Socket> sock;
dsp::stream<uint8_t> decompIn;
dsp::compression::SampleStreamDecompressor decomp;
@ -137,10 +133,10 @@ namespace server {
ZSTD_DCtx* dctx;
std::thread workerThread;
double currentSampleRate = 1000000.0;
};
typedef std::unique_ptr<ClientClass> Client;
Client connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out);
std::shared_ptr<Client> connect(std::string host, uint16_t port, dsp::stream<dsp::complex_t>* out);
}

View File

@ -1,5 +1,6 @@
#include "spectran_http_client.h"
#include <utils/flog.h>
#include <inttypes.h>
SpectranHTTPClient::SpectranHTTPClient(std::string host, int port, dsp::stream<dsp::complex_t>* stream) {
this->stream = stream;
@ -50,10 +51,10 @@ void SpectranHTTPClient::setCenterFrequency(uint64_t freq) {
// Make request
net::http::RequestHeader rqhdr(net::http::METHOD_PUT, "/control", host);
char buf[1024];
sprintf(buf, "{\"frequencyCenter\":%d,\"frequencySpan\":%d,\"type\":\"capture\"}", freq, _samplerate);
sprintf(buf, "{\"frequencyCenter\":%" PRIu64 ",\"frequencySpan\":%" PRIu64 ",\"type\":\"capture\"}", freq, _samplerate);
std::string data = buf;
char lenBuf[16];
sprintf(lenBuf, "%d", data.size());
sprintf(lenBuf, "%" PRIu64, (uint64_t)data.size());
rqhdr.setField("Content-Length", lenBuf);
controlHttp.sendRequestHeader(rqhdr);
controlSock->sendstr(data);

View File

@ -283,8 +283,8 @@ private:
flog::info("Connected to server");
}
}
catch (std::exception e) {
flog::error("Could not connect to spyserver {0}", e.what());
catch (const std::exception& e) {
flog::error("Could not connect to spyserver {}", e.what());
}
}