28 Commits

Author SHA1 Message Date
5ab3428b90 update readme
Some checks failed
Build Binaries / build_windows (push) Waiting to run
Build Binaries / build_macos_intel (push) Waiting to run
Build Binaries / build_macos_arm (push) Waiting to run
Build Binaries / create_full_archive (push) Blocked by required conditions
Build Binaries / build_raspios_bullseye_armhf (push) Waiting to run
Build Binaries / update_nightly_release (push) Blocked by required conditions
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 6s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
2024-11-06 21:27:45 +01:00
7f002f6276 add lower limit to network source samplerate 2024-11-06 20:54:01 +01:00
a728403a3f Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2024-11-06 20:29:58 +01:00
0f1d2da3b7 finish and enable the network source 2024-11-06 20:29:50 +01:00
6d0b65c27f Merge pull request #1515 from Oskar-Dudek/patch-1
Some checks failed
Build Binaries / create_full_archive (push) Blocked by required conditions
Build Binaries / update_nightly_release (push) Blocked by required conditions
Build Binaries / build_windows (push) Waiting to run
Build Binaries / build_macos_intel (push) Waiting to run
Build Binaries / build_macos_arm (push) Waiting to run
Build Binaries / build_raspios_bullseye_armhf (push) Waiting to run
Build Binaries / build_debian_buster (push) Failing after 6s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_debian_sid (push) Failing after 5s
Build Binaries / build_ubuntu_focal (push) Failing after 5s
Build Binaries / build_ubuntu_jammy (push) Failing after 5s
Build Binaries / build_ubuntu_mantic (push) Failing after 5s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Create ireland.json
2024-11-06 17:51:31 +01:00
f640cdcb6a fix formatting 2024-11-06 17:47:14 +01:00
80a90e13d9 Create ireland.json 2024-11-06 16:37:49 +00:00
3982db73d3 Merge pull request #1501 from bsy0317/master
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 3s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 6s
Build Binaries / build_ubuntu_mantic (push) Failing after 8s
Build Binaries / build_ubuntu_noble (push) Failing after 5s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 9s
Build Binaries / check_formatting (push) Successful in 5s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Add Republic of Korea BandPlan
2024-10-22 11:03:52 +02:00
bd64f07a20 Update republic-of-korea.json 2024-10-22 17:57:18 +09:00
c9950d9331 fix band plan name 2024-10-22 10:48:39 +02:00
9bc609f4e4 Update republic-of-korea.json
Modify indentation
2024-10-22 17:39:15 +09:00
bcc8e20e66 Update republic-of-korea.json
Corrected typos
2024-10-22 17:34:02 +09:00
b07e828fed Update republic-of-korea.json
Correcting typos
2024-10-22 12:55:10 +09:00
bd24a4a5eb Add Republic of Korea.json
Add Republic of Korea BandPlan.
It's not the Democratic People's Republic of Korea!
2024-10-22 12:39:06 +09:00
fe4a7b32a7 Merge pull request #1496 from AlexandreRouma/bladerf_clock_sel
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 3s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
add clock selection got bladerf devices
2024-10-17 20:35:52 +02:00
0e1ab29b5d ingore commas in pasted frequencies
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 4s
Build Binaries / build_debian_bullseye (push) Failing after 5s
Build Binaries / build_debian_bookworm (push) Failing after 5s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-10-16 22:14:12 +02:00
fbbafddd3d fix frequency formatting when copying from frequency selector 2024-10-16 20:48:35 +02:00
1cbc8ec6f5 add copy/paste support to the frequency selector 2024-10-16 18:31:14 +02:00
9f65e3ec71 add clock selection got bladerf devices 2024-10-12 01:48:39 +02:00
08f3a7d201 make the modulation field of a baseband recording 'IQ' instead of 'Unknown'
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 6s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 3s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
2024-10-07 14:39:45 +02:00
9ce62f8885 add whitelist for plutosdr-like devices 2024-10-07 14:10:23 +02:00
caeaa2d46c add kcsdr_source to the readme
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 8s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 6s
Build Binaries / build_android (push) Failing after 7s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 4s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-10-02 19:15:13 +02:00
7ae030a3a6 fix fobossdr_source module missing on MacOS as described in #1485 2024-10-02 18:58:48 +02:00
1b27379a3d add beginning of kcsdr source 2024-10-02 18:57:05 +02:00
e52123038e Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus
Some checks failed
Build Binaries / build_debian_buster (push) Failing after 5s
Build Binaries / build_debian_bullseye (push) Failing after 4s
Build Binaries / build_debian_bookworm (push) Failing after 4s
Build Binaries / build_debian_sid (push) Failing after 4s
Build Binaries / build_ubuntu_focal (push) Failing after 4s
Build Binaries / build_ubuntu_jammy (push) Failing after 4s
Build Binaries / build_ubuntu_mantic (push) Failing after 4s
Build Binaries / build_ubuntu_noble (push) Failing after 4s
Build Binaries / build_android (push) Failing after 5s
Build Binaries / check_spelling (push) Failing after 4s
Build Binaries / check_formatting (push) Successful in 5s
Build Binaries / build_windows (push) Has been cancelled
Build Binaries / build_macos_intel (push) Has been cancelled
Build Binaries / build_macos_arm (push) Has been cancelled
Build Binaries / build_raspios_bullseye_armhf (push) Has been cancelled
Build Binaries / create_full_archive (push) Has been cancelled
Build Binaries / update_nightly_release (push) Has been cancelled
2024-09-28 03:32:14 +02:00
ec8c60111d uncomment lines that copy the fobossdr source module for windows packaging 2024-09-28 03:32:07 +02:00
f61799cf5f Merge pull request #1482 from AlexandreRouma/fobos_test
Enable the FobosSDR source on windows and linux
2024-09-28 02:44:04 +02:00
e835c8dd9a disable fobos source on windows 2024-09-14 15:04:07 +02:00
25 changed files with 1790 additions and 918 deletions

View File

@ -19,6 +19,7 @@ option(OPT_BUILD_FOBOSSDR_SOURCE "Build FobosSDR Source Module (Dependencies: li
option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
option(OPT_BUILD_KCSDR_SOURCE "Build KCSDR Source Module (Dependencies: libkcsdr)" OFF)
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
@ -160,6 +161,10 @@ if (OPT_BUILD_HERMES_SOURCE)
add_subdirectory("source_modules/hermes_source")
endif (OPT_BUILD_HERMES_SOURCE)
if (OPT_BUILD_KCSDR_SOURCE)
add_subdirectory("source_modules/kcsdr_source")
endif (OPT_BUILD_KCSDR_SOURCE)
if (OPT_BUILD_LIMESDR_SOURCE)
add_subdirectory("source_modules/limesdr_source")
endif (OPT_BUILD_LIMESDR_SOURCE)

View File

@ -183,6 +183,8 @@ int sdrpp_main(int argc, char* argv[]) {
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["Network Source"]["module"] = "network_source";
defConfig["moduleInstances"]["Network Source"]["enabled"] = true;
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";

View File

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

120
core/src/utils/hrfreq.cpp Normal file
View File

@ -0,0 +1,120 @@
#include "hrfreq.h"
#include <utils/flog.h>
namespace hrfreq {
std::string toString(double freq) {
// Determine the scale
int maxDecimals = 0;
const char* suffix = "Hz";
if (freq >= 1e9) {
freq /= 1e9;
maxDecimals = 9;
suffix = "GHz";
}
else if (freq >= 1e6) {
freq /= 1e6;
maxDecimals = 6;
suffix = "MHz";
}
else if (freq >= 1e3) {
freq /= 1e3;
maxDecimals = 3;
suffix = "KHz";
}
// Convert to string (TODO: Not sure if limiting the decimals rounds)
char numBuf[128];
int numLen = sprintf(numBuf, "%0.*lf", maxDecimals, freq);
// If there is a decimal point, remove the useless zeros
if (maxDecimals) {
for (int i = numLen-1; i >= 0; i--) {
bool dot = (numBuf[i] == '.');
if (numBuf[i] != '0' && !dot) { break; }
numBuf[i] = 0;
if (dot) { break; }
}
}
// Concat the suffix
char finalBuf[128];
sprintf(finalBuf, "%s%s", numBuf, suffix);
// Return the final string
return finalBuf;
}
bool isNumeric(char c) {
return std::isdigit(c) || c == '+' || c == '-' || c == '.' || c == ',';
}
bool fromString(const std::string& str, double& freq) {
// Skip non-numeric characters
int i = 0;
char c;
for (; i < str.size(); i++) {
if (isNumeric(str[i])) { break; }
}
// Extract the numeric part
std::string numeric;
for (; i < str.size(); i++) {
// Get the character
c = str[i];
// If it's a letter, stop
if (std::isalpha(c)) { break; }
// If isn't numeric, skip it
if (!isNumeric(c)) { continue; }
// If it's a comma, skip it for now. This enforces a dot as a decimal point
if (c == ',') { continue; }
// Add the character to the numeric string
numeric += c;
}
// Attempt to parse the numeric part
double num;
try {
num = std::stod(numeric);
}
catch (const std::exception& e) {
flog::error("Failed to parse numeric part: '{}'", numeric);
return false;
}
// If no more text is available, assume the numeric part gives a frequency in Hz
if (i == str.size()) {
flog::warn("No unit given, assuming it's Hz");
freq = num;
return true;
}
// Scale the numeric value depending on the first scale character
char scale = std::toupper(str[i]);
switch (scale) {
case 'G':
num *= 1e9;
break;
case 'M':
num *= 1e6;
break;
case 'K':
num *= 1e3;
break;
case 'H':
break;
default:
flog::warn("Unknown frequency scale: '{}'", scale);
break;
}
// Return the frequency
freq = num;
return true; // TODO
}
}

19
core/src/utils/hrfreq.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <string>
namespace hrfreq {
/**
* Convert a frequency to a human-readable string.
* @param freq Frequency in Hz.
* @return Human-readable representation of the frequency.
*/
std::string toString(double freq);
/**
* Convert a human-readable representation of a frequency to a frequency value.
* @param str String containing the human-readable frequency.
* @param freq Value to write the decoded frequency to.
* @return True on success, false otherwise.
*/
bool fromString(const std::string& str, double& freq);
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,34 @@
0123456789
--- ---
0*4
1*5
2*6
1*5
2*6 = L + 3*7 - 0*4
3*7
2*6
3*7 = L + 4*8 - 1*5
4*8
3*7
4*8 = L + 5*9 - 2*6
5*9
0*5
1*6
2*7
1*6
2*7
3*8
2*7
3*8
4*9
=> Use same technique to cache the interpolation results

View File

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

View File

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

View File

@ -35,10 +35,11 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/fobossdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/fobossdr_source/fobossdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/network_source/network_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib

View File

@ -35,6 +35,8 @@ cp $build_dir/source_modules/hermes_source/Release/hermes_source.dll sdrpp_windo
cp $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
cp $build_dir/source_modules/network_source/Release/network_source.dll sdrpp_windows_x64/modules/
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/

View File

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

View File

@ -324,12 +324,13 @@ Modules in beta are still included in releases for the most part but not enabled
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
| fobossdr_source | Beta | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
| fobossdr_source | Working | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
| harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ✅ |
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
| kcsdr_source | Unfinished | libkcsdr | OPT_BUILD_KCSDR_SOURCE | ⛔ | ⛔ | ⛔ |
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
| network_source | Unfinished | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | |
| network_source | Beta | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | |
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
| rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | ✅ | ✅ |
@ -351,8 +352,8 @@ Modules in beta are still included in releases for the most part but not enabled
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
| new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
| portaudio_sink | Beta | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
| new_portaudio_sink | Working | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
| portaudio_sink | Working | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
## Decoders
@ -418,8 +419,8 @@ If you still have an issue, please open an issue about it or ask on the discord.
# Contributing
Feel free to submit pull requests and report bugs via the GitHub issue tracker.
I will soon publish a contributing.md listing the code style to use.
Feel free to submit band plans via the GitHub issue tracker.
For code changes, please create a feature request instead.
# Credits

View File

@ -0,0 +1,117 @@
{
"name": "Ireland",
"country_name": "Republic Of Ireland",
"country_code": "IE",
"author_name": "Oskar Dudek",
"author_url": "",
"bands": [
{
"name": "2200m Ham Band",
"type": "amateur",
"start": 135700,
"end": 137800
},
{
"name": "Long wave",
"type": "broadcast",
"start": 148500,
"end": 282500
},
{
"name": "AM broadcast",
"type": "broadcast",
"start": 531000,
"end": 1602000
},
{
"name": "120m SW broadcast",
"type": "broadcast",
"start": 2300000,
"end": 2495000
},
{
"name": "90m SW Broadcast",
"type": "broadcast",
"start": 3200000,
"end": 3400000
},
{
"name": "75m SW Broadcast",
"type": "broadcast",
"start": 3900000,
"end": 4000000
},
{
"name": "60m SW Broadcast",
"type": "broadcast",
"start": 4750000,
"end": 5060000
},
{
"name": "49m SW Broadcast",
"type": "broadcast",
"start": 5900000,
"end": 6200000
},
{
"name": "40m SW Broadcast",
"type": "broadcast",
"start": 7200000,
"end": 7450000
},
{
"name": "31m SW Broadcast",
"type": "broadcast",
"start": 9400000,
"end": 9900000
},
{
"name": "25m SW Broadcast",
"type": "broadcast",
"start": 11600000,
"end": 12100000
},
{
"name": "22m SW Broadcast",
"type": "broadcast",
"start": 13570000,
"end": 13870000
},
{
"name": "19m SW Broadcast",
"type": "broadcast",
"start": 15100000,
"end": 15800000
},
{
"name": "16m SW Broadcast",
"type": "broadcast",
"start": 17480000,
"end": 17900000
},
{
"name": "15m SW Broadcast",
"type": "broadcast",
"start": 18900000,
"end": 19020000
},
{
"name": "13m SW Broadcast",
"type": "broadcast",
"start": 21450000,
"end": 21850000
},
{
"name": "11m SW Broadcast",
"type": "broadcast",
"start": 25670000,
"end": 26100000
},
{
"name": "FM Broadcast",
"type": "broadcast",
"start": 87500000,
"end": 108000000
}
]
}

View File

@ -0,0 +1,549 @@
{
"name": "Republic of Korea",
"country_name": "Republic of Korea",
"country_code": "KR",
"author_name": "SeoyeonBae",
"author_url": "https://github.com/bsy0317",
"bands": [
{
"name": "Radio Navigation",
"type": "aviation",
"start": 8300,
"end": 14000
},
{
"name": "Coastal Telegraph",
"type": "marine",
"start": 14000,
"end": 19950
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 19950,
"end": 20250
},
{
"name": "Coastal Telegraph",
"type": "marine",
"start": 20250,
"end": 70000
},
{
"name": "Radio Navigation",
"type": "navigation",
"start": 70000,
"end": 160000
},
{
"name": "Aviation Radio Navigation",
"type": "aviation",
"start": 160000,
"end": 285000
},
{
"name": "Aviation Maritime Radiobeacon",
"type": "aviation",
"start": 285000,
"end": 325000
},
{
"name": "Aviation Radio Navigation",
"type": "aviation",
"start": 325000,
"end": 472000
},
{
"name": "Amateur",
"type": "amateur",
"start": 472000,
"end": 479000
},
{
"name": "International Distress Safety Call",
"type": "marine",
"start": 479000,
"end": 505000
},
{
"name": "Maritime Telegraph",
"type": "marine",
"start": 505000,
"end": 526500
},
{
"name": "Standard Broadcast",
"type": "broadcast",
"start": 526500,
"end": 1606500
},
{
"name": "Radiobuoy",
"type": "navigation",
"start": 1606500,
"end": 1800000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 1800000,
"end": 1825000
},
{
"name": "Radiobuoy Control LORAN",
"type": "radiolocation",
"start": 1825000,
"end": 2000000
},
{
"name": "Radiobuoy",
"type": "fixed",
"start": 2000000,
"end": 2065000
},
{
"name": "Distress Call",
"type": "marine",
"start": 2065000,
"end": 2107000
},
{
"name": "International Distress Search and Rescue",
"type": "mobile",
"start": 2173500,
"end": 2190500
},
{
"name": "Road Management",
"type": "fixed",
"start": 2194000,
"end": 2495000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 2495000,
"end": 2505000
},
{
"name": "Ship Station Telephone",
"type": "fixed",
"start": 2505000,
"end": 2850000
},
{
"name": "Aviation Mobile R",
"type": "aviation",
"start": 2850000,
"end": 3025000
},
{
"name": "Aviation Mobile OR",
"type": "aviation",
"start": 3025000,
"end": 3155000
},
{
"name": "Aviation Mobile R",
"type": "aviation",
"start": 3400000,
"end": 3500000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 3500000,
"end": 3550000
},
{
"name": "Experimental Station",
"type": "fixed",
"start": 3550000,
"end": 3790000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 3790000,
"end": 3800000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 3900000,
"end": 3950000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 3995000,
"end": 4005000
},
{
"name": "Ship Station Telephone",
"type": "marine",
"start": 4005000,
"end": 4063000
},
{
"name": "Oceanographic Data",
"type": "marine",
"start": 4063000,
"end": 4065000
},
{
"name": "Ship Station Duplex Telephone",
"type": "marine",
"start": 4065000,
"end": 4146000
},
{
"name": "Ship Station Simplex Telephone",
"type": "marine",
"start": 4146000,
"end": 4152000
},
{
"name": "Ship Station Wideband Telegraph Fax",
"type": "marine",
"start": 4152000,
"end": 4172000
},
{
"name": "Ship Station Narrowband",
"type": "marine",
"start": 4172000,
"end": 4181750
},
{
"name": "Ship Station A1A Morse Code Communication",
"type": "marine",
"start": 4186750,
"end": 4202250
},
{
"name": "Radiolocation",
"type": "radiolocation",
"start": 4438000,
"end": 4488000
},
{
"name": "Calling Response",
"type": "fixed",
"start": 4488000,
"end": 4650000
},
{
"name": "Aviation Mobile R",
"type": "aviation",
"start": 4650000,
"end": 4850000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 4995000,
"end": 5005000
},
{
"name": "Search Rescue",
"type": "aviation",
"start": 5480000,
"end": 5730000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 5900000,
"end": 5950000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 5950000,
"end": 6200000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 7000000,
"end": 7100000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 7100000,
"end": 7200000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 7200000,
"end": 7450000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 7995000,
"end": 8005000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 9400000,
"end": 9500000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 9500000,
"end": 9900000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 9995000,
"end": 10005000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 10100000,
"end": 10150000
},
{
"name": "Aviation Mobile",
"type": "aviation",
"start": 10150000,
"end": 11600000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 11600000,
"end": 11650000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 11650000,
"end": 12050000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 12050000,
"end": 12100000
},
{
"name": "Aviation Mobile",
"type": "aviation",
"start": 13260000,
"end": 13360000
},
{
"name": "Radio Astronomy",
"type": "astronomy",
"start": 13360000,
"end": 13410000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 13570000,
"end": 13600000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 13600000,
"end": 13800000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 13800000,
"end": 13870000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 14000000,
"end": 14350000
},
{
"name": "Aviation Mobile",
"type": "aviation",
"start": 15010000,
"end": 15100000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 15100000,
"end": 15600000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 15600000,
"end": 15800000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 15800000,
"end": 15995000
},
{
"name": "Standard Frequency Time Signal",
"type": "utility",
"start": 15995000,
"end": 16005000
},
{
"name": "Broadcast",
"type": "broadcast",
"start": 18900000,
"end": 19020000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 21000000,
"end": 21450000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 21450000,
"end": 21850000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 24890000,
"end": 24990000
},
{
"name": "Shortwave Broadcast",
"type": "broadcast",
"start": 25670000,
"end": 26100000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 28000000,
"end": 29700000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 50000000,
"end": 54000000
},
{
"name": "TV Broadcast",
"type": "broadcast",
"start": 54000000,
"end": 72000000
},
{
"name": "Flood Warning",
"type": "broadcast",
"start": 72000000,
"end": 74800000
},
{
"name": "TV Broadcast",
"type": "broadcast",
"start": 76000000,
"end": 88000000
},
{
"name": "FM Broadcast",
"type": "broadcast",
"start": 88000000,
"end": 100000000
},
{
"name": "FM Broadcast",
"type": "broadcast",
"start": 100000000,
"end": 108000000
},
{
"name": "ILS Localizer VOR",
"type": "fixed",
"start": 108000000,
"end": 117975000
},
{
"name": "Amateur Station",
"type": "amateur",
"start": 144000000,
"end": 146000000
},
{
"name": "General Communication",
"type": "fixed",
"start": 146000000,
"end": 148000000
},
{
"name": "Low Power Device",
"type": "fixed",
"start": 162037500,
"end": 174000000
},
{
"name": "TV Broadcast",
"type": "broadcast",
"start": 174000000,
"end": 216000000
},
{
"name": "Low Power Device",
"type": "fixed",
"start": 216000000,
"end": 230000000
},
{
"name": "Low Power Device",
"type": "fixed",
"start": 273000000,
"end": 322000000
},
{
"name": "Personal Radio",
"type": "fixed",
"start": 420000000,
"end": 470000000
},
{
"name": "Public Network",
"type": "broadcast",
"start": 698000000,
"end": 806000000
},
{
"name": "Low Power Device",
"type": "fixed",
"start": 942000000,
"end": 960000000
},
{
"name": "Satellite Mobile Communication",
"type": "fixed",
"start": 15250000000,
"end": 16605000000
},
{
"name": "Mobile Communication",
"type": "mobile",
"start": 25000000000,
"end": 37000000000
}
]
}

View File

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

View File

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

View File

@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.13)
project(kcsdr_source)
file(GLOB SRC "src/*.cpp" "src/*.c")
include(${SDRPP_MODULE_CMAKE})
target_link_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10/x64/DLL")
target_include_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10")
target_link_libraries(kcsdr_source PRIVATE FTD3XX)

View File

@ -0,0 +1,209 @@
#include "kcsdr.h"
#include <string.h>
#include "../vendor/FTD3XXLibrary_1.3.0.10/FTD3XX.h"
#include <stdio.h>
#include <stddef.h>
#define KCSDR_PKT_EMPTY_LEN 0x0C
#define KCSDR_COMMAND_PIPE 0x02
#define KCSDR_RX_DATA_PIPE 0x83
#define KCSDR_TX_DATA_PIPE 0x03
struct kcsdr {
FT_HANDLE ft;
};
#pragma pack(push, 1)
struct kcsdr_packet {
uint8_t zeros0[4];
uint8_t length;
uint8_t zeros1[2];
uint8_t hex_eighty;
uint32_t command;
uint8_t data[188];
};
typedef struct kcsdr_packet kcsdr_packet_t;
#pragma pack(pop)
enum kcsdr_command {
CMD_NOT_USED_0x00 = 0x00,
CMD_SET_PORT = 0x01,
CMD_SET_FREQUENCY = 0x02,
CMD_SET_ATTENUATION = 0x03,
CMD_SET_AMPLIFIER = 0x04,
CMD_SET_BANDWIDTH = 0x05,
CMD_START = 0x06,
CMD_STOP = 0x07,
CMD_SET_EXT_AMP = 0x08,
CMD_START_REMOTE = 0x09,
CMD_STOP_REMOTE = 0x0A
};
typedef enum kcsdr_command kcsdr_command_t;
int kcsdr_send_command(kcsdr_t* dev, kcsdr_direction_t dir, kcsdr_command_t cmd, const uint8_t* data, int len) {
Sleep(50);
// Create an empty packet
kcsdr_packet_t pkt;
memset(&pkt, 0, sizeof(kcsdr_packet_t));
// Fill out the packet info
pkt.length = len + KCSDR_PKT_EMPTY_LEN;
pkt.hex_eighty = 0x80; // Whatever the fuck that is
pkt.command = (uint32_t)cmd | (uint32_t)dir;
// Copy the data if there is some
if (len) { memcpy(pkt.data, data, len); }
// Dump the bytes
uint8_t* dump = (uint8_t*)&pkt;
printf("Sending:");
for (int i = 0; i < pkt.length; i++) {
printf(" %02X", dump[i]);
}
printf("\n");
// Send the command to endpoint 0
int sent;
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_COMMAND_PIPE, (uint8_t*)&pkt, sizeof(kcsdr_packet_t), &sent, NULL);
if (err != FT_OK) {
return -err;
}
printf("Sent %d bytes (%d)\n", sent, err);
Sleep(50);
// Flush existing commands
FT_FlushPipe(dev->ft, KCSDR_COMMAND_PIPE);
return -(int)err;
}
int kcsdr_list_devices(kcsdr_info_t** devices) {
// Generate a list of FTDI devices
int ftdiDevCount = 0;
FT_STATUS err = FT_CreateDeviceInfoList(&ftdiDevCount);
if (err != FT_OK) {
return -1;
}
// If no device was found, return nothing
if (!ftdiDevCount) {
*devices = NULL;
return 0;
}
// Get said device list
FT_DEVICE_LIST_INFO_NODE* list = malloc(ftdiDevCount * sizeof(FT_DEVICE_LIST_INFO_NODE));
err = FT_GetDeviceInfoList(list, &ftdiDevCount);
if (err != FT_OK) {
return -1;
}
// Allocate the device info list
*devices = malloc(ftdiDevCount * sizeof(kcsdr_info_t));
// Find all KC908s
int kcCount = 0;
for (int i = 0; i < ftdiDevCount; i++) {
strcpy((*devices)[kcCount++].serial, list[i].SerialNumber);
}
// Free the FTDI list
free(list);
return kcCount;
}
void kcsdr_free_device_list(kcsdr_info_t* devices) {
// Free the list
if (devices) { free(devices); }
}
int kcsdr_open(kcsdr_t** dev, const char* serial) {
// Attempt to open the device using the serial number
FT_HANDLE ft;
FT_STATUS err = FT_Create(serial, FT_OPEN_BY_SERIAL_NUMBER, &ft);
if (err != FT_OK) {
return -1;
}
// Set the timeouts for the data pipes
FT_SetPipeTimeout(ft, KCSDR_RX_DATA_PIPE, 1000);
FT_SetPipeTimeout(ft, KCSDR_TX_DATA_PIPE, 1000);
// Allocate the device object
*dev = malloc(sizeof(kcsdr_t));
// Fill out the device object
(*dev)->ft = ft;
// Put device into remote control mode
return kcsdr_send_command(*dev, KCSDR_DIR_RX, CMD_START_REMOTE, NULL, 0);
}
void kcsdr_close(kcsdr_t* dev) {
// Put device back in normal mode
kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_STOP_REMOTE, NULL, 0);
// Close the device
FT_Close(dev->ft);
// Free the device object
free(dev);
}
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port) {
// Send SET_PORT command
return kcsdr_send_command(dev, dir, CMD_SET_PORT, &port, 1);
}
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq) {
// Send SET_FREQUENCY command
return kcsdr_send_command(dev, dir, CMD_SET_FREQUENCY, (uint8_t*)&freq, 8);
}
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att) {
// Send SET_ATTENUATION command
return kcsdr_send_command(dev, dir, CMD_SET_ATTENUATION, &att, 1);
}
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain) {
// Send SET_AMPLIFIER command
return kcsdr_send_command(dev, dir, CMD_SET_AMPLIFIER, &gain, 1);
}
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain) {
// Send CMD_SET_EXT_AMP command
return kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_SET_EXT_AMP, &gain, 1);
}
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate) {
// Set SET_BANDWIDTH command
return kcsdr_send_command(dev, dir, CMD_SET_BANDWIDTH, (uint8_t*)&samplerate, 4);
}
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir) {
// Send START command
return kcsdr_send_command(dev, dir, CMD_START, NULL, 0);
}
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir) {
// Send STOP command
return kcsdr_send_command(dev, dir, CMD_STOP, NULL, 0);
}
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count) {
// Receive samples (TODO: Endpoint might be 0x81)
int received;
FT_STATUS err = FT_ReadPipeEx(dev->ft, KCSDR_RX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &received, NULL);
return (err == FT_OK) ? received / (2*sizeof(uint16_t)) : -(int)err;
}
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count) {
// Transmit samples
int sent;
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_TX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &sent, NULL);
return (err == FT_OK) ? sent / (2*sizeof(uint16_t)) : -(int)err;
}

View File

@ -0,0 +1,150 @@
#pragma once
#include <stdint.h>
#define KCSDR_SERIAL_LEN 16
#define KCSDR_MAX_PORTS 6
// Detect C++
#ifdef __cplusplus
extern "C" {
#endif
/**
* KCSDR Device.
*/
struct kcsdr;
typedef struct kcsdr kcsdr_t;
/**
* Device Information
*/
struct kcsdr_info {
char serial[KCSDR_SERIAL_LEN+1];
};
typedef struct kcsdr_info kcsdr_info_t;
/**
* RF Direction.
*/
enum kcsdr_direction {
KCSDR_DIR_RX = 0x00,
KCSDR_DIR_TX = 0x80
};
typedef enum kcsdr_direction kcsdr_direction_t;
/**
* Get a list of KCSDR devices on the system.
* @param devices Pointer to an array of device info.
* @return Number of devices found or error code.
*/
int kcsdr_list_devices(kcsdr_info_t** devices);
/**
* Free a device list returned by `kcsdr_list_devices()`.
* @param devices Device list to free.
*/
void kcsdr_free_device_list(kcsdr_info_t* devices);
/**
* Open a KCSDR device.
* @param dev Newly open device.
* @param serial Serial number of the device to open as returned in the device list.
* @return 0 on success, error code otherwise.
*/
int kcsdr_open(kcsdr_t** dev, const char* serial);
/**
* Close a KCSDR device.
* @param dev Device to be closed.
*/
void kcsdr_close(kcsdr_t* dev);
/**
* Select the RF port.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @param port RF port number to select.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port);
/**
* Set the center frequency.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param freq Frequency in Hz.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq);
/**
* Set the attenuation.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param samplerate Attenuation in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att);
/**
* Set the internal amplifier gain.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param gain Gain in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain);
/**
* Set the external amplifier gain.
* @param dev Device to control.
* @param gain Gain in dB.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain);
/**
* Set the samplerate.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
* @param samplerate Samplerate in Hz.
* @return 0 on success, error code otherwise.
*/
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate);
/**
* Start streaming samples.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @return 0 on success, error code otherwise.
*/
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir);
/**
* Stop streaming samples.
* @param dev Device to control.
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
* @return 0 on success, error code otherwise.
*/
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir);
/**
* Receive a buffer of samples.
* @param samples Sample buffer.
* @param count Number of complex samples.
* @return Number of samples received.
*/
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count);
/**
* Transmit a buffer of samples.
* @param samples Sample buffer.
* @param count Number of complex samples.
* @return Number of samples transmitted.
*/
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count);
// Detect C++
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,324 @@
#include <imgui.h>
#include <module.h>
#include <gui/gui.h>
#include <gui/smgui.h>
#include <signal_path/signal_path.h>
#include <core.h>
#include <utils/optionlist.h>
#include "kcsdr.h"
#include <atomic>
SDRPP_MOD_INFO{
/* Name: */ "kcsdr_source",
/* Description: */ "KCSDR Source Module",
/* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0,
/* Max instances */ -1
};
#define CONCAT(a, b) ((std::string(a) + b).c_str())
class KCSDRSourceModule : public ModuleManager::Instance {
public:
KCSDRSourceModule(std::string name) {
this->name = name;
sampleRate = 2000000.0;
samplerates.define(40e6, "40MHz", 40e6);
samplerates.define(35e6, "35MHz", 35e6);
samplerates.define(30e6, "30MHz", 30e6);
samplerates.define(25e6, "25MHz", 25e6);
samplerates.define(20e6, "20MHz", 20e6);
samplerates.define(15e6, "15MHz", 15e6);
samplerates.define(10e6, "10MHz", 10e6);
samplerates.define(5e6, "5MHz", 5e6);
handler.ctx = this;
handler.selectHandler = menuSelected;
handler.deselectHandler = menuDeselected;
handler.menuHandler = menuHandler;
handler.startHandler = start;
handler.stopHandler = stop;
handler.tuneHandler = tune;
handler.stream = &stream;
// Refresh devices
refresh();
// Select first (TODO: Select from config)
select("");
sigpath::sourceManager.registerSource("KCSDR", &handler);
}
~KCSDRSourceModule() {
}
void postInit() {}
void enable() {
enabled = true;
}
void disable() {
enabled = false;
}
bool isEnabled() {
return enabled;
}
private:
void refresh() {
devices.clear();
// Get device list
kcsdr_info_t* list;
int count = kcsdr_list_devices(&list);
if (count < 0) {
flog::error("Failed to list devices: {}", count);
return;
}
// Create list
for (int i = 0; i < count; i++) {
devices.define(list[i].serial, list[i].serial, list[i].serial);
}
// Free the device list
kcsdr_free_device_list(list);
}
void select(const std::string& serial) {
// If there are no devices, give up
if (devices.empty()) {
selectedSerial.clear();
return;
}
// If the serial was not found, select the first available serial
if (!devices.keyExists(serial)) {
select(devices.key(0));
return;
}
// Get the menu ID
devId = devices.keyId(serial);
// TODO
// Update the samplerate
core::setInputSampleRate(sampleRate);
// Save serial number
selectedSerial = serial;
}
static void menuSelected(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
core::setInputSampleRate(_this->sampleRate);
flog::info("KCSDRSourceModule '{0}': Menu Select!", _this->name);
}
static void menuDeselected(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
flog::info("KCSDRSourceModule '{0}': Menu Deselect!", _this->name);
}
static void start(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) { return; }
// If no serial is given, do nothing
if (_this->selectedSerial.empty()) { return; }
// Open the device
int err = kcsdr_open(&_this->openDev, _this->selectedSerial.c_str());
if (err) {
flog::error("Failed to open device: {}", err);
return;
}
// Configure the device
kcsdr_set_port(_this->openDev, KCSDR_DIR_RX, 0);
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, _this->freq);
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
kcsdr_set_samplerate(_this->openDev, KCSDR_DIR_RX, _this->sampleRate);
// Start the stream
kcsdr_start(_this->openDev, KCSDR_DIR_RX);
// Start worker
_this->run = true;
_this->workerThread = std::thread(&KCSDRSourceModule::worker, _this);
_this->running = true;
flog::info("KCSDRSourceModule '{0}': Start!", _this->name);
}
static void stop(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (!_this->running) { return; }
_this->running = false;
// Stop worker
_this->run = false;
_this->stream.stopWriter();
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
_this->stream.clearWriteStop();
// Stop streaming
kcsdr_stop(_this->openDev, KCSDR_DIR_RX);
// Close the device
kcsdr_close(_this->openDev);
flog::info("KCSDRSourceModule '{0}': Stop!", _this->name);
}
static void tune(double freq, void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) {
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, freq);
}
_this->freq = freq;
flog::info("KCSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
}
static void menuHandler(void* ctx) {
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
if (_this->running) { SmGui::BeginDisabled(); }
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Combo(CONCAT("##_kcsdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
_this->select(_this->devices.key(_this->devId));
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
if (SmGui::Combo(CONCAT("##_kcsdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
_this->sampleRate = _this->samplerates.value(_this->srId);
core::setInputSampleRate(_this->sampleRate);
// TODO: Save
}
SmGui::SameLine();
SmGui::FillWidth();
SmGui::ForceSync();
if (SmGui::Button(CONCAT("Refresh##_kcsdr_refr_", _this->name))) {
_this->refresh();
_this->select(_this->selectedSerial);
core::setInputSampleRate(_this->sampleRate);
}
if (_this->running) { SmGui::EndDisabled(); }
// SmGui::LeftLabel("RX Port");
// SmGui::FillWidth();
// if (SmGui::Combo(CONCAT("##_kcsdr_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
// if (_this->running) {
// // TODO
// }
// // TODO: Save
// }
SmGui::LeftLabel("Attenuation");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_att_", _this->name), &_this->att, 0, 31)) {
if (_this->running) {
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
}
// TODO: Save
}
SmGui::LeftLabel("Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_gain_", _this->name), &_this->gain, 0, 31)) {
if (_this->running) {
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
}
// TODO: Save
}
SmGui::LeftLabel("External Gain");
SmGui::FillWidth();
if (SmGui::SliderInt(CONCAT("##_kcsdr_ext_gain_", _this->name), &_this->extGain, 0, 31)) {
if (_this->running) {
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
}
// TODO: Save
}
}
void worker() {
// Compute the buffer size
int bufferSize = 0x4000/4;//sampleRate / 200;
// Allocate the sample buffer
int16_t* samps = dsp::buffer::alloc<int16_t>(bufferSize*2);
// Loop
while (run) {
// Read samples
int count = kcsdr_rx(openDev, samps, bufferSize);
if (!count) { continue; }
if (count < 0) {
flog::debug("Failed to read samples: {}", count);
break;
}
// Convert the samples to float
volk_16i_s32f_convert_32f((float*)stream.writeBuf, samps, 8192.0f, count*2);
// Send out the samples
if (!stream.swap(count)) { break; }
}
// Free the sample buffer
dsp::buffer::free(samps);
}
std::string name;
bool enabled = true;
dsp::stream<dsp::complex_t> stream;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
OptionList<std::string, std::string> devices;
OptionList<int, double> samplerates;
int devId = 0;
int srId = 0;
int att = 0;
int gain = 30;
int extGain = 1;
int portId = 0;
std::string selectedSerial;
kcsdr_t* openDev;
std::thread workerThread;
std::atomic<bool> run = false;
};
MOD_EXPORT void _INIT_() {
// Nothing here
}
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
return new KCSDRSourceModule(name);
}
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
delete (KCSDRSourceModule*)instance;
}
MOD_EXPORT void _END_() {
// Nothing here
}

View File

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

View File

@ -23,6 +23,12 @@ SDRPP_MOD_INFO{
ConfigManager config;
const std::vector<const char*> deviceWhiteList = {
"PlutoSDR",
"ANTSDR",
"LibreSDR"
};
class PlutoSDRSourceModule : public ModuleManager::Instance {
public:
PlutoSDRSourceModule(std::string name) {
@ -130,7 +136,14 @@ private:
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) {
bool isPluto = false;
for (const auto type : deviceWhiteList) {
if (desc.find(type) != std::string::npos) {
isPluto = true;
break;
}
}
if (!isPluto) {
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
continue;
}