mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-07-08 18:15:21 +02:00
Compare commits
3 Commits
5ab3428b90
...
dab_experi
Author | SHA1 | Date | |
---|---|---|---|
f8078ac3f0 | |||
064f25ee73 | |||
d87ae23560 |
@ -19,7 +19,6 @@ 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_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON)
|
||||||
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
|
option(OPT_BUILD_HAROGIC_SOURCE "Build Harogic Source Module (Dependencies: htra_api)" OFF)
|
||||||
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
option(OPT_BUILD_HERMES_SOURCE "Build Hermes Source Module (no dependencies required)" ON)
|
||||||
option(OPT_BUILD_KCSDR_SOURCE "Build KCSDR Source Module (Dependencies: libkcsdr)" OFF)
|
|
||||||
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
|
option(OPT_BUILD_LIMESDR_SOURCE "Build LimeSDR Source Module (Dependencies: liblimesuite)" OFF)
|
||||||
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
|
option(OPT_BUILD_NETWORK_SOURCE "Build Network Source Module (no dependencies required)" ON)
|
||||||
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
|
option(OPT_BUILD_PERSEUS_SOURCE "Build Perseus Source Module (Dependencies: libperseus-sdr)" OFF)
|
||||||
@ -161,10 +160,6 @@ if (OPT_BUILD_HERMES_SOURCE)
|
|||||||
add_subdirectory("source_modules/hermes_source")
|
add_subdirectory("source_modules/hermes_source")
|
||||||
endif (OPT_BUILD_HERMES_SOURCE)
|
endif (OPT_BUILD_HERMES_SOURCE)
|
||||||
|
|
||||||
if (OPT_BUILD_KCSDR_SOURCE)
|
|
||||||
add_subdirectory("source_modules/kcsdr_source")
|
|
||||||
endif (OPT_BUILD_KCSDR_SOURCE)
|
|
||||||
|
|
||||||
if (OPT_BUILD_LIMESDR_SOURCE)
|
if (OPT_BUILD_LIMESDR_SOURCE)
|
||||||
add_subdirectory("source_modules/limesdr_source")
|
add_subdirectory("source_modules/limesdr_source")
|
||||||
endif (OPT_BUILD_LIMESDR_SOURCE)
|
endif (OPT_BUILD_LIMESDR_SOURCE)
|
||||||
|
@ -183,8 +183,6 @@ int sdrpp_main(int argc, char* argv[]) {
|
|||||||
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["Hermes Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
|
defConfig["moduleInstances"]["LimeSDR Source"]["module"] = "limesdr_source";
|
||||||
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["LimeSDR Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["Network Source"]["module"] = "network_source";
|
|
||||||
defConfig["moduleInstances"]["Network Source"]["enabled"] = true;
|
|
||||||
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
|
defConfig["moduleInstances"]["PerseusSDR Source"]["module"] = "perseus_source";
|
||||||
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
|
defConfig["moduleInstances"]["PerseusSDR Source"]["enabled"] = true;
|
||||||
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
|
defConfig["moduleInstances"]["PlutoSDR Source"]["module"] = "plutosdr_source";
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include <gui/style.h>
|
#include <gui/style.h>
|
||||||
#include <gui/gui.h>
|
#include <gui/gui.h>
|
||||||
#include <backend.h>
|
#include <backend.h>
|
||||||
#include <utils/hrfreq.h>
|
|
||||||
|
|
||||||
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
#ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
@ -91,7 +90,6 @@ void FrequencySelect::moveCursorToDigit(int i) {
|
|||||||
|
|
||||||
void FrequencySelect::draw() {
|
void FrequencySelect::draw() {
|
||||||
auto window = ImGui::GetCurrentWindow();
|
auto window = ImGui::GetCurrentWindow();
|
||||||
auto io = ImGui::GetIO();
|
|
||||||
widgetPos = ImGui::GetWindowContentRegionMin();
|
widgetPos = ImGui::GetWindowContentRegionMin();
|
||||||
ImVec2 cursorPos = ImGui::GetCursorPos();
|
ImVec2 cursorPos = ImGui::GetCursorPos();
|
||||||
widgetPos.x += window->Pos.x + cursorPos.x;
|
widgetPos.x += window->Pos.x + cursorPos.x;
|
||||||
@ -134,7 +132,7 @@ void FrequencySelect::draw() {
|
|||||||
ImVec2 mousePos = ImGui::GetMousePos();
|
ImVec2 mousePos = ImGui::GetMousePos();
|
||||||
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
bool leftClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
|
||||||
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
|
bool rightClick = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
|
||||||
int mw = io.MouseWheel;
|
int mw = ImGui::GetIO().MouseWheel;
|
||||||
bool onDigit = false;
|
bool onDigit = false;
|
||||||
bool hovered = false;
|
bool hovered = false;
|
||||||
|
|
||||||
@ -176,7 +174,7 @@ void FrequencySelect::draw() {
|
|||||||
moveCursorToDigit(i + 1);
|
moveCursorToDigit(i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto chars = io.InputQueueCharacters;
|
auto chars = ImGui::GetIO().InputQueueCharacters;
|
||||||
|
|
||||||
// For each keyboard characters, type it
|
// For each keyboard characters, type it
|
||||||
for (int j = 0; j < chars.Size; j++) {
|
for (int j = 0; j < chars.Size; j++) {
|
||||||
@ -196,34 +194,6 @@ void FrequencySelect::draw() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
digitHovered = hovered;
|
digitHovered = hovered;
|
||||||
|
|
||||||
if (isInArea(mousePos, digitTopMins[0], digitBottomMaxs[11])) {
|
|
||||||
bool shortcutKey = io.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiKeyModFlags_Super) : (io.KeyMods == ImGuiKeyModFlags_Ctrl);
|
|
||||||
bool ctrlOnly = (io.KeyMods == ImGuiKeyModFlags_Ctrl);
|
|
||||||
bool shiftOnly = (io.KeyMods == ImGuiKeyModFlags_Shift);
|
|
||||||
bool copy = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_C)) || (ctrlOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
|
|
||||||
bool paste = ((shortcutKey && ImGui::IsKeyPressed(ImGuiKey_V)) || (shiftOnly && ImGui::IsKeyPressed(ImGuiKey_Insert)));
|
|
||||||
if (copy) {
|
|
||||||
// Convert the freqency to a string
|
|
||||||
std::string freqStr = hrfreq::toString(frequency);
|
|
||||||
|
|
||||||
// Write it to the clipboard
|
|
||||||
ImGui::SetClipboardText(freqStr.c_str());
|
|
||||||
}
|
|
||||||
if (paste) {
|
|
||||||
// Attempt to parse the clipboard as a number
|
|
||||||
const char* clip = ImGui::GetClipboardText();
|
|
||||||
|
|
||||||
// If the clipboard is not empty, attempt to parse it
|
|
||||||
if (clip) {
|
|
||||||
double newFreq;
|
|
||||||
if (hrfreq::fromString(clip, newFreq)) {
|
|
||||||
setFrequency(abs(newFreq));
|
|
||||||
frequencyChanged = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t freq = 0;
|
uint64_t freq = 0;
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
#include "hrfreq.h"
|
|
||||||
#include <utils/flog.h>
|
|
||||||
|
|
||||||
namespace hrfreq {
|
|
||||||
|
|
||||||
|
|
||||||
std::string toString(double freq) {
|
|
||||||
// Determine the scale
|
|
||||||
int maxDecimals = 0;
|
|
||||||
const char* suffix = "Hz";
|
|
||||||
if (freq >= 1e9) {
|
|
||||||
freq /= 1e9;
|
|
||||||
maxDecimals = 9;
|
|
||||||
suffix = "GHz";
|
|
||||||
}
|
|
||||||
else if (freq >= 1e6) {
|
|
||||||
freq /= 1e6;
|
|
||||||
maxDecimals = 6;
|
|
||||||
suffix = "MHz";
|
|
||||||
}
|
|
||||||
else if (freq >= 1e3) {
|
|
||||||
freq /= 1e3;
|
|
||||||
maxDecimals = 3;
|
|
||||||
suffix = "KHz";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to string (TODO: Not sure if limiting the decimals rounds)
|
|
||||||
char numBuf[128];
|
|
||||||
int numLen = sprintf(numBuf, "%0.*lf", maxDecimals, freq);
|
|
||||||
|
|
||||||
// If there is a decimal point, remove the useless zeros
|
|
||||||
if (maxDecimals) {
|
|
||||||
for (int i = numLen-1; i >= 0; i--) {
|
|
||||||
bool dot = (numBuf[i] == '.');
|
|
||||||
if (numBuf[i] != '0' && !dot) { break; }
|
|
||||||
numBuf[i] = 0;
|
|
||||||
if (dot) { break; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Concat the suffix
|
|
||||||
char finalBuf[128];
|
|
||||||
sprintf(finalBuf, "%s%s", numBuf, suffix);
|
|
||||||
|
|
||||||
// Return the final string
|
|
||||||
return finalBuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNumeric(char c) {
|
|
||||||
return std::isdigit(c) || c == '+' || c == '-' || c == '.' || c == ',';
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fromString(const std::string& str, double& freq) {
|
|
||||||
// Skip non-numeric characters
|
|
||||||
int i = 0;
|
|
||||||
char c;
|
|
||||||
for (; i < str.size(); i++) {
|
|
||||||
if (isNumeric(str[i])) { break; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the numeric part
|
|
||||||
std::string numeric;
|
|
||||||
for (; i < str.size(); i++) {
|
|
||||||
// Get the character
|
|
||||||
c = str[i];
|
|
||||||
|
|
||||||
// If it's a letter, stop
|
|
||||||
if (std::isalpha(c)) { break; }
|
|
||||||
|
|
||||||
// If isn't numeric, skip it
|
|
||||||
if (!isNumeric(c)) { continue; }
|
|
||||||
|
|
||||||
// If it's a comma, skip it for now. This enforces a dot as a decimal point
|
|
||||||
if (c == ',') { continue; }
|
|
||||||
|
|
||||||
// Add the character to the numeric string
|
|
||||||
numeric += c;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to parse the numeric part
|
|
||||||
double num;
|
|
||||||
try {
|
|
||||||
num = std::stod(numeric);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e) {
|
|
||||||
flog::error("Failed to parse numeric part: '{}'", numeric);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no more text is available, assume the numeric part gives a frequency in Hz
|
|
||||||
if (i == str.size()) {
|
|
||||||
flog::warn("No unit given, assuming it's Hz");
|
|
||||||
freq = num;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scale the numeric value depending on the first scale character
|
|
||||||
char scale = std::toupper(str[i]);
|
|
||||||
switch (scale) {
|
|
||||||
case 'G':
|
|
||||||
num *= 1e9;
|
|
||||||
break;
|
|
||||||
case 'M':
|
|
||||||
num *= 1e6;
|
|
||||||
break;
|
|
||||||
case 'K':
|
|
||||||
num *= 1e3;
|
|
||||||
break;
|
|
||||||
case 'H':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
flog::warn("Unknown frequency scale: '{}'", scale);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the frequency
|
|
||||||
freq = num;
|
|
||||||
return true; // TODO
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace hrfreq {
|
|
||||||
/**
|
|
||||||
* Convert a frequency to a human-readable string.
|
|
||||||
* @param freq Frequency in Hz.
|
|
||||||
* @return Human-readable representation of the frequency.
|
|
||||||
*/
|
|
||||||
std::string toString(double freq);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a human-readable representation of a frequency to a frequency value.
|
|
||||||
* @param str String containing the human-readable frequency.
|
|
||||||
* @param freq Value to write the decoded frequency to.
|
|
||||||
* @return True on success, false otherwise.
|
|
||||||
*/
|
|
||||||
bool fromString(const std::string& str, double& freq);
|
|
||||||
}
|
|
@ -5,139 +5,139 @@
|
|||||||
#include "dab_phase_sym.h"
|
#include "dab_phase_sym.h"
|
||||||
|
|
||||||
namespace dab {
|
namespace dab {
|
||||||
class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
// class CyclicSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||||
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
// using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||||
public:
|
// public:
|
||||||
CyclicSync() {}
|
// CyclicSync() {}
|
||||||
|
|
||||||
// TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
|
// // TODO: The default AGC rate is probably way too fast, plot out the avgCorr to see how much it moves
|
||||||
CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
|
// CyclicSync(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) { init(in, symbolLength, cyclicPrefixLength, samplerate, agcRate); }
|
||||||
|
|
||||||
void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
|
// void init(dsp::stream<dsp::complex_t>* in, double symbolLength, double cyclicPrefixLength, double samplerate, float agcRate = 1e-3) {
|
||||||
// Computer the number of samples for the symbol and its cyclic prefix
|
// // Computer the number of samples for the symbol and its cyclic prefix
|
||||||
symbolSamps = round(samplerate * symbolLength);
|
// symbolSamps = round(samplerate * symbolLength);
|
||||||
prefixSamps = round(samplerate * cyclicPrefixLength);
|
// prefixSamps = round(samplerate * cyclicPrefixLength);
|
||||||
|
|
||||||
// Allocate and clear the delay buffer
|
// // Allocate and clear the delay buffer
|
||||||
delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
|
// delayBuf = dsp::buffer::alloc<dsp::complex_t>(STREAM_BUFFER_SIZE + 64000);
|
||||||
dsp::buffer::clear(delayBuf, symbolSamps);
|
// dsp::buffer::clear(delayBuf, symbolSamps);
|
||||||
|
|
||||||
// Allocate and clear the history buffer
|
// // Allocate and clear the history buffer
|
||||||
histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
|
// histBuf = dsp::buffer::alloc<dsp::complex_t>(prefixSamps);
|
||||||
dsp::buffer::clear(histBuf, prefixSamps);
|
// dsp::buffer::clear(histBuf, prefixSamps);
|
||||||
|
|
||||||
// Compute the delay input addresses
|
// // Compute the delay input addresses
|
||||||
delayBufInput = &delayBuf[symbolSamps];
|
// delayBufInput = &delayBuf[symbolSamps];
|
||||||
|
|
||||||
// Compute the correlation AGC configuration
|
// // Compute the correlation AGC configuration
|
||||||
this->agcRate = agcRate;
|
// this->agcRate = agcRate;
|
||||||
agcRateInv = 1.0f - agcRate;
|
// agcRateInv = 1.0f - agcRate;
|
||||||
|
|
||||||
base_type::init(in);
|
// base_type::init(in);
|
||||||
}
|
// }
|
||||||
|
|
||||||
void reset() {
|
// void reset() {
|
||||||
assert(base_type::_block_init);
|
// assert(base_type::_block_init);
|
||||||
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
// std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
base_type::tempStop();
|
// base_type::tempStop();
|
||||||
|
|
||||||
base_type::tempStart();
|
// base_type::tempStart();
|
||||||
}
|
// }
|
||||||
|
|
||||||
int run() {
|
// int run() {
|
||||||
int count = base_type::_in->read();
|
// int count = base_type::_in->read();
|
||||||
if (count < 0) { return -1; }
|
// if (count < 0) { return -1; }
|
||||||
|
|
||||||
// Copy the data into the normal delay buffer
|
// // Copy the data into the normal delay buffer
|
||||||
memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
|
// memcpy(delayBufInput, base_type::_in->readBuf, count * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
// Flush the input stream
|
// // Flush the input stream
|
||||||
base_type::_in->flush();
|
// base_type::_in->flush();
|
||||||
|
|
||||||
// Do cross-correlation
|
// // Do cross-correlation
|
||||||
for (int i = 0; i < count; i++) {
|
// for (int i = 0; i < count; i++) {
|
||||||
// Get the current history slot
|
// // Get the current history slot
|
||||||
dsp::complex_t* slot = &histBuf[histId++];
|
// dsp::complex_t* slot = &histBuf[histId++];
|
||||||
|
|
||||||
// Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
|
// // Wrap around the history slot index (TODO: Check that the history buffer's length is correct)
|
||||||
histId %= prefixSamps;
|
// histId %= prefixSamps;
|
||||||
|
|
||||||
// Kick out last value from the correlation
|
// // Kick out last value from the correlation
|
||||||
corr -= *slot;
|
// corr -= *slot;
|
||||||
|
|
||||||
// Save input value and compute the new prodct
|
// // Save input value and compute the new prodct
|
||||||
dsp::complex_t val = delayBuf[i];
|
// dsp::complex_t val = delayBuf[i];
|
||||||
dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
|
// dsp::complex_t prod = val.conj()*delayBuf[i+symbolSamps];
|
||||||
|
|
||||||
// Add the new value to the correlation
|
// // Add the new value to the correlation
|
||||||
*slot = prod;
|
// *slot = prod;
|
||||||
|
|
||||||
// Add the new value to the history buffer
|
// // Add the new value to the history buffer
|
||||||
corr += prod;
|
// corr += prod;
|
||||||
|
|
||||||
// Compute sample amplitude
|
// // Compute sample amplitude
|
||||||
float rcorr = corr.amplitude();
|
// float rcorr = corr.amplitude();
|
||||||
|
|
||||||
// If a high enough peak is reached, reset the symbol counter
|
// // If a high enough peak is reached, reset the symbol counter
|
||||||
if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
|
// if (rcorr > avgCorr && rcorr > peakCorr) { // Note keeping an average level might not be needed
|
||||||
peakCorr = rcorr;
|
// peakCorr = rcorr;
|
||||||
peakLCorr = lastCorr;
|
// peakLCorr = lastCorr;
|
||||||
samplesSincePeak = 0;
|
// samplesSincePeak = 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// If this is the sample right after the peak, save it
|
// // If this is the sample right after the peak, save it
|
||||||
if (samplesSincePeak == 1) {
|
// if (samplesSincePeak == 1) {
|
||||||
peakRCorr = rcorr;
|
// peakRCorr = rcorr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Write the sample to the output
|
// // Write the sample to the output
|
||||||
out.writeBuf[samplesSincePeak++] = val;
|
// out.writeBuf[samplesSincePeak++] = val;
|
||||||
|
|
||||||
// If the end of the symbol is reached, send it off
|
// // If the end of the symbol is reached, send it off
|
||||||
if (samplesSincePeak >= symbolSamps) {
|
// if (samplesSincePeak >= symbolSamps) {
|
||||||
if (!out.swap(symbolSamps)) {
|
// if (!out.swap(symbolSamps)) {
|
||||||
return -1;
|
// return -1;
|
||||||
}
|
// }
|
||||||
samplesSincePeak = 0;
|
// samplesSincePeak = 0;
|
||||||
peakCorr = 0;
|
// peakCorr = 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Update the average correlation
|
// // Update the average correlation
|
||||||
lastCorr = rcorr;
|
// lastCorr = rcorr;
|
||||||
|
|
||||||
// Update the average correlation value
|
// // Update the average correlation value
|
||||||
avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
|
// avgCorr = agcRate*rcorr + agcRateInv*avgCorr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Move unused data
|
// // Move unused data
|
||||||
memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
|
// memmove(delayBuf, &delayBuf[count], symbolSamps * sizeof(dsp::complex_t));
|
||||||
|
|
||||||
return count;
|
// return count;
|
||||||
}
|
// }
|
||||||
|
|
||||||
protected:
|
// protected:
|
||||||
int symbolSamps;
|
// int symbolSamps;
|
||||||
int prefixSamps;
|
// int prefixSamps;
|
||||||
|
|
||||||
int histId = 0;
|
// int histId = 0;
|
||||||
dsp::complex_t* histBuf;
|
// dsp::complex_t* histBuf;
|
||||||
|
|
||||||
dsp::complex_t* delayBuf;
|
// dsp::complex_t* delayBuf;
|
||||||
dsp::complex_t* delayBufInput;
|
// dsp::complex_t* delayBufInput;
|
||||||
|
|
||||||
dsp::complex_t corr = { 0.0f, 0.0f };
|
// dsp::complex_t corr = { 0.0f, 0.0f };
|
||||||
|
|
||||||
int samplesSincePeak = 0;
|
// int samplesSincePeak = 0;
|
||||||
float lastCorr = 0.0f;
|
// float lastCorr = 0.0f;
|
||||||
float peakCorr = 0.0f;
|
// float peakCorr = 0.0f;
|
||||||
float peakLCorr = 0.0f;
|
// float peakLCorr = 0.0f;
|
||||||
float peakRCorr = 0.0f;
|
// float peakRCorr = 0.0f;
|
||||||
|
|
||||||
// Note only required for DAB
|
// // Note only required for DAB
|
||||||
float avgCorr = 0.0f;
|
// float avgCorr = 0.0f;
|
||||||
float agcRate;
|
// float agcRate;
|
||||||
float agcRateInv;
|
// float agcRateInv;
|
||||||
};
|
// };
|
||||||
|
|
||||||
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
class FrameFreqSync : public dsp::Processor<dsp::complex_t, dsp::complex_t> {
|
||||||
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
using base_type = dsp::Processor<dsp::complex_t, dsp::complex_t>;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include "dab_dsp.h"
|
#include "dab_dsp.h"
|
||||||
#include <gui/widgets/constellation_diagram.h>
|
#include <gui/widgets/constellation_diagram.h>
|
||||||
|
#include "ofdm.h"
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ public:
|
|||||||
M17DecoderModule(std::string name) {
|
M17DecoderModule(std::string name) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
|
|
||||||
file = std::ofstream("sync4.f32", std::ios::out | std::ios::binary);
|
file = std::ofstream("sync5.f32", std::ios::out | std::ios::binary);
|
||||||
|
|
||||||
// Load config
|
// Load config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
@ -47,7 +48,7 @@ public:
|
|||||||
vfo->setSnapInterval(250);
|
vfo->setSnapInterval(250);
|
||||||
|
|
||||||
// Initialize DSP here
|
// Initialize DSP here
|
||||||
csync.init(vfo->output, 1e-3, 246e-6, INPUT_SAMPLE_RATE);
|
csync.init(vfo->output, 2048, 504, 1e-3, INPUT_SAMPLE_RATE, 1e-6, 0.01, 0.005);
|
||||||
ffsync.init(&csync.out);
|
ffsync.init(&csync.out);
|
||||||
ns.init(&ffsync.out, handler, this);
|
ns.init(&ffsync.out, handler, this);
|
||||||
|
|
||||||
@ -131,8 +132,9 @@ private:
|
|||||||
std::string name;
|
std::string name;
|
||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
|
|
||||||
dab::CyclicSync csync;
|
//dab::CyclicSync csync;
|
||||||
dab::FrameFreqSync ffsync;
|
dab::FrameFreqSync ffsync;
|
||||||
|
dsp::ofdm::CyclicTimeSync csync;
|
||||||
dsp::sink::Handler<dsp::complex_t> ns;
|
dsp::sink::Handler<dsp::complex_t> ns;
|
||||||
|
|
||||||
ImGui::ConstellationDiagram constDiagram;
|
ImGui::ConstellationDiagram constDiagram;
|
||||||
|
324
decoder_modules/dab_decoder/src/ofdm.h
Normal file
324
decoder_modules/dab_decoder/src/ofdm.h
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/processor.h>
|
||||||
|
#include <dsp/loop/phase_control_loop.h>
|
||||||
|
#include <dsp/taps/windowed_sinc.h>
|
||||||
|
#include <dsp/multirate/polyphase_bank.h>
|
||||||
|
#include <dsp/math/step.h>
|
||||||
|
|
||||||
|
namespace dsp::ofdm {
|
||||||
|
class CyclicTimeSync : public Processor<complex_t, complex_t> {
|
||||||
|
using base_type = Processor<complex_t, complex_t> ;
|
||||||
|
public:
|
||||||
|
CyclicTimeSync() {}
|
||||||
|
|
||||||
|
CyclicTimeSync(stream<complex_t>* in, int fftSize, int cpSize, double usefulSymbolTime, double samplerate,
|
||||||
|
double omegaGain, double muGain, double omegaRelLimit, int interpPhaseCount = 128, int interpTapCount = 8) {
|
||||||
|
init(in, fftSize, cpSize, usefulSymbolTime, samplerate, omegaGain, muGain, omegaRelLimit, interpPhaseCount, interpTapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CyclicTimeSync() {
|
||||||
|
if (!base_type::_block_init) { return; }
|
||||||
|
base_type::stop();
|
||||||
|
dsp::multirate::freePolyphaseBank(interpBank);
|
||||||
|
buffer::free(corrSampCache);
|
||||||
|
buffer::free(corrProdCache);
|
||||||
|
buffer::free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<complex_t>* in, int fftSize, int cpSize, double usefulSymbolTime, double samplerate,
|
||||||
|
double omegaGain, double muGain, double omegaRelLimit, int interpPhaseCount = 128, int interpTapCount = 8) {
|
||||||
|
// Save parameters
|
||||||
|
this->fftSize = fftSize;
|
||||||
|
this->cpSize = cpSize;
|
||||||
|
period = fftSize + cpSize;
|
||||||
|
|
||||||
|
// Compute the interpolator settings
|
||||||
|
omega = (usefulSymbolTime * samplerate) / (double)fftSize;
|
||||||
|
this->omegaGain = omegaGain;
|
||||||
|
this->muGain = muGain;
|
||||||
|
this->omegaRelLimit = omegaRelLimit;
|
||||||
|
this->interpPhaseCount = interpPhaseCount;
|
||||||
|
this->interpTapCount = interpTapCount;
|
||||||
|
|
||||||
|
// Compute the correlator AGC settings
|
||||||
|
// TODO: Compute it using he FFT and CP sizes
|
||||||
|
this->corrAgcRate = 1e-4;
|
||||||
|
corrAgcInvRate = 1.0f - corrAgcRate;
|
||||||
|
|
||||||
|
// Initialize the control loop
|
||||||
|
pcl.init(muGain, omegaGain, 0.0, 0.0, 1.0, omega, omega * (1.0 - omegaRelLimit), omega * (1.0 + omegaRelLimit));
|
||||||
|
|
||||||
|
// Generate the interpolation taps
|
||||||
|
generateInterpTaps();
|
||||||
|
|
||||||
|
// Allocate the buffers
|
||||||
|
corrSampCache = buffer::alloc<complex_t>(fftSize);
|
||||||
|
corrProdCache = buffer::alloc<complex_t>(cpSize);
|
||||||
|
buffer = buffer::alloc<complex_t>(STREAM_BUFFER_SIZE + interpTapCount);
|
||||||
|
bufStart = &buffer[interpTapCount - 1];
|
||||||
|
|
||||||
|
// Clear the buffers
|
||||||
|
buffer::clear(corrSampCache, fftSize);
|
||||||
|
buffer::clear(corrProdCache, cpSize);
|
||||||
|
buffer::clear(buffer, interpTapCount - 1);
|
||||||
|
|
||||||
|
base_type::init(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setOmegaGain(double omegaGain) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
this->omegaGain = omegaGain;
|
||||||
|
pcl.setCoefficients(muGain, omegaGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMuGain(double muGain) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
this->muGain = muGain;
|
||||||
|
pcl.setCoefficients(muGain, omegaGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setOmegaRelLimit(double omegaRelLimit) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
this->omegaRelLimit = omegaRelLimit;
|
||||||
|
pcl.setFreqLimits(omega * (1.0 - omegaRelLimit), omega * (1.0 + omegaRelLimit));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInterpParams(int interpPhaseCount, int interpTapCount) {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
base_type::tempStop();
|
||||||
|
this->interpPhaseCount = interpPhaseCount;
|
||||||
|
this->interpTapCount = interpTapCount;
|
||||||
|
dsp::multirate::freePolyphaseBank(interpBank);
|
||||||
|
buffer::free(buffer);
|
||||||
|
generateInterpTaps();
|
||||||
|
buffer = buffer::alloc<complex_t>(STREAM_BUFFER_SIZE + interpTapCount);
|
||||||
|
bufStart = &buffer[interpTapCount - 1];
|
||||||
|
base_type::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
assert(base_type::_block_init);
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
|
||||||
|
base_type::tempStop();
|
||||||
|
offset = 0;
|
||||||
|
pcl.phase = 0.0f;
|
||||||
|
pcl.freq = omega;
|
||||||
|
// TODO: The rest
|
||||||
|
base_type::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = base_type::_in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
// Copy data to work buffer
|
||||||
|
memcpy(bufStart, base_type::_in->readBuf, count * sizeof(complex_t));
|
||||||
|
|
||||||
|
// Process all samples
|
||||||
|
while (offset < count) {
|
||||||
|
// Get the cache slots
|
||||||
|
complex_t* sampSlot = &corrSampCache[corrSampCacheId++];
|
||||||
|
complex_t* prodSlot = &corrProdCache[corrProdCacheId++];
|
||||||
|
corrSampCacheId %= fftSize;
|
||||||
|
corrProdCacheId %= cpSize;
|
||||||
|
|
||||||
|
// Compute the interpolated sample
|
||||||
|
complex_t sample;
|
||||||
|
int phase = std::clamp<int>(floorf(pcl.phase * (float)interpPhaseCount), 0, interpPhaseCount - 1);
|
||||||
|
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&sample, (lv_32fc_t*)&buffer[offset], interpBank.phases[phase], interpTapCount);
|
||||||
|
|
||||||
|
// Write the sample to the output
|
||||||
|
if (outCount >= cpSize) {
|
||||||
|
out.writeBuf[outCount - cpSize] = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send out a symbol when it's fully received
|
||||||
|
if ((++outCount) >= fftSize+cpSize) {
|
||||||
|
if (!out.swap(outCount)) { break; }
|
||||||
|
outCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run autocorrelation
|
||||||
|
complex_t prod = sample.conj()*(*sampSlot);
|
||||||
|
corr += prod;
|
||||||
|
corr -= *prodSlot;
|
||||||
|
|
||||||
|
// Write back the new sample and product value to the cache
|
||||||
|
*sampSlot = sample;
|
||||||
|
*prodSlot = prod;
|
||||||
|
|
||||||
|
// Compute the correlation level
|
||||||
|
float corrLvl = corr.amplitude();
|
||||||
|
|
||||||
|
// Detect peak in autocorrelation (TODO: level check maybe not needed now that corrPeak is reset to corrLvl)
|
||||||
|
if (corrLvl > corrAvg && corrLvl > corrPeak) {
|
||||||
|
// Save the current correlation as the peak
|
||||||
|
corrPeak = corrLvl;
|
||||||
|
|
||||||
|
// Save the value of the previous correlation as the left side of the peak
|
||||||
|
corrPeakL = corrLast;
|
||||||
|
|
||||||
|
// Reset the peak distance counter
|
||||||
|
sincePeak = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first sample after a peak is the right-side sample
|
||||||
|
if (sincePeak == 1) {
|
||||||
|
corrPeakR = corrLvl;
|
||||||
|
}
|
||||||
|
else if (sincePeak == cpSize) {
|
||||||
|
// Start the useful symbol counter
|
||||||
|
sinceCp = 0;
|
||||||
|
|
||||||
|
// Compute the fractional error (TODO: Probably very inaccurate with noise, use real slopes instead)
|
||||||
|
if (corrPeakL > corrPeakR) {
|
||||||
|
float maxSlope = corrPeakR - corrPeak;
|
||||||
|
float slope = corrPeak - corrPeakL;
|
||||||
|
fracErr = std::clamp<float>(0.5f * (1.0f + slope / maxSlope), -0.5f, 0.5f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float maxSlope = corrPeak - corrPeakL;
|
||||||
|
float slope = corrPeakR - corrPeak;
|
||||||
|
fracErr = std::clamp<float>(-0.5f * (1.0f + slope / maxSlope), -0.5f, 0.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (sincePeak == fftSize) {
|
||||||
|
// Reset the peak detector
|
||||||
|
corrPeak = corrAvg;
|
||||||
|
}
|
||||||
|
// NOTE: THIS IS ONLY NEEDED FOR DAB
|
||||||
|
// Detect a wider-than-normal distance to adapt the output counter
|
||||||
|
else if (sincePeak == 2656) {
|
||||||
|
// Reset the output counter
|
||||||
|
outCount = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last sample of useful symbol
|
||||||
|
if (sinceCp == fftSize) {
|
||||||
|
// If the fractional error is valid, run closed-loop
|
||||||
|
float err = 0.0f;
|
||||||
|
if (!std::isnan(fracErr)) {
|
||||||
|
// Compute the measured period using the distance to the last symbol
|
||||||
|
float measuredPeriod = (float)sinceLastSym - fracErr;
|
||||||
|
// NOTE: THIS IS ONLY NEEDED FOR DAB
|
||||||
|
if (measuredPeriod > 3828.0f) {
|
||||||
|
// Null symbol
|
||||||
|
err = measuredPeriod - (2552.0f+2656.0f);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Regular symbol
|
||||||
|
err = measuredPeriod - period;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = std::clamp<float>(err, -10.0f, 10.0f);
|
||||||
|
|
||||||
|
// Run the control loop in closed-loop mode
|
||||||
|
pcl.advance(err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Otherwise, run open-loop
|
||||||
|
pcl.advancePhase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("%d\n", outCount);
|
||||||
|
|
||||||
|
// Nudge the symbol window if it's too out of sync
|
||||||
|
if (outCount > 100) {
|
||||||
|
// TODO: MOVE THE LAST SAMPLES OR THE SYMBOL WILL BE CORRUPTED!
|
||||||
|
outCount = 50;
|
||||||
|
flog::debug("NUDGE!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the period counter
|
||||||
|
sinceLastSym = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Run the control loop in open-loop mode
|
||||||
|
pcl.advancePhase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the offset and phase
|
||||||
|
float delta = floorf(pcl.phase);
|
||||||
|
offset += delta;
|
||||||
|
pcl.phase -= delta;
|
||||||
|
|
||||||
|
// Update the last correlation level
|
||||||
|
corrLast = corrLvl;
|
||||||
|
|
||||||
|
// Update correlation AGC
|
||||||
|
corrAvg = corrAvg*corrAgcInvRate + corrLvl*corrAgcRate;
|
||||||
|
|
||||||
|
// Increment the distance counters (TODO: Check if they happen at the right point, eg. after being reset to zero)
|
||||||
|
sincePeak++;
|
||||||
|
sinceLastSym++;
|
||||||
|
sinceCp++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare offset for next buffer of samples
|
||||||
|
offset -= count;
|
||||||
|
|
||||||
|
// Update delay buffer
|
||||||
|
memmove(buffer, &buffer[count], (interpTapCount - 1) * sizeof(complex_t));
|
||||||
|
|
||||||
|
// Swap if some data was generated
|
||||||
|
base_type::_in->flush();
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void generateInterpTaps() {
|
||||||
|
double bw = 0.5 / (double)interpPhaseCount;
|
||||||
|
dsp::tap<float> lp = dsp::taps::windowedSinc<float>(interpPhaseCount * interpTapCount, dsp::math::hzToRads(bw, 1.0), dsp::window::nuttall, interpPhaseCount);
|
||||||
|
interpBank = dsp::multirate::buildPolyphaseBank<float>(interpPhaseCount, lp);
|
||||||
|
taps::free(lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OFDM Configuration
|
||||||
|
int fftSize;
|
||||||
|
int cpSize;
|
||||||
|
float period;
|
||||||
|
|
||||||
|
// Interpolator
|
||||||
|
dsp::multirate::PolyphaseBank<float> interpBank;
|
||||||
|
int interpPhaseCount;
|
||||||
|
int interpTapCount;
|
||||||
|
int offset = 0;
|
||||||
|
complex_t* buffer = NULL;
|
||||||
|
complex_t* bufStart;
|
||||||
|
|
||||||
|
// Control loop
|
||||||
|
loop::PhaseControlLoop<float, false> pcl;
|
||||||
|
double omega;
|
||||||
|
double omegaGain;
|
||||||
|
double muGain;
|
||||||
|
double omegaRelLimit;
|
||||||
|
float fracErr = 0.0f;
|
||||||
|
|
||||||
|
// Autocorrelator
|
||||||
|
complex_t corr = {0.0f, 0.0f};
|
||||||
|
complex_t* corrSampCache = NULL;
|
||||||
|
complex_t* corrProdCache = NULL;
|
||||||
|
int corrSampCacheId = 0;
|
||||||
|
int corrProdCacheId = 0;
|
||||||
|
float corrAgcRate;
|
||||||
|
float corrAgcInvRate;
|
||||||
|
float corrAvg = 0;
|
||||||
|
float corrLast = 0;
|
||||||
|
float corrPeakR = 0;
|
||||||
|
float corrPeak = 0;
|
||||||
|
float corrPeakL = 0;
|
||||||
|
|
||||||
|
// Peak detection
|
||||||
|
int sincePeak = 0;
|
||||||
|
int sinceLastSym = 0;
|
||||||
|
int sinceCp = 0;
|
||||||
|
|
||||||
|
// Other shit to categorize
|
||||||
|
int outCount = 0;
|
||||||
|
};
|
||||||
|
};
|
@ -1,34 +0,0 @@
|
|||||||
0123456789
|
|
||||||
--- ---
|
|
||||||
|
|
||||||
0*4
|
|
||||||
1*5
|
|
||||||
2*6
|
|
||||||
|
|
||||||
1*5
|
|
||||||
2*6 = L + 3*7 - 0*4
|
|
||||||
3*7
|
|
||||||
|
|
||||||
2*6
|
|
||||||
3*7 = L + 4*8 - 1*5
|
|
||||||
4*8
|
|
||||||
|
|
||||||
3*7
|
|
||||||
4*8 = L + 5*9 - 2*6
|
|
||||||
5*9
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
0*5
|
|
||||||
1*6
|
|
||||||
2*7
|
|
||||||
|
|
||||||
1*6
|
|
||||||
2*7
|
|
||||||
3*8
|
|
||||||
|
|
||||||
2*7
|
|
||||||
3*8
|
|
||||||
4*9
|
|
||||||
|
|
||||||
=> Use same technique to cache the interpolation results
|
|
31
decoder_modules/dab_decoder/src/optmized.txt
Normal file
31
decoder_modules/dab_decoder/src/optmized.txt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
cyclicLen = 4
|
||||||
|
usefulLen = 12
|
||||||
|
|
||||||
|
A = 0*12 + 1*13 + 2*14 + 3*15
|
||||||
|
B = 1*13 + 2*14 + 3*15 + 4*16 = A - 0*12 + 4*16
|
||||||
|
C = 2*14 + 3*15 + 4*16 + 5*17 = B - 1*13 + 5*17
|
||||||
|
D = 3*15 + 4*16 + 5*17 + 6*18 = C - 2*14 + 6*18
|
||||||
|
E = 4*16 + 5*17 + 6*18 + 7*19 = D - 3*15 + 7*19
|
||||||
|
F = 5*17 + 6*18 + 7*19 + 8*20 = E - 4*16 + 8*20
|
||||||
|
G = 6*18 + 7*19 + 8*20 + 9*21 = F - 5*17 + 9*21
|
||||||
|
H = 7*19 + 8*20 + 9*21 + 10*22 = G - 6*18 + 10*22
|
||||||
|
I = 8*20 + 9*21 + 10*22 + 11*23 = H - 7*19 + 11*23
|
||||||
|
J = 9*21 + 10*22 + 11*23 + 12*24 = I - 8*20 + 12*24
|
||||||
|
K = 10*22 + 11*23 + 12*24 + 13*25 = J - 9*21 + 13*25
|
||||||
|
L = 11*23 + 12*24 + 13*25 + 14*26 = K - 10*22 + 14*26
|
||||||
|
M = 12*24 + 13*25 + 14*26 + 15*27 = L - 11*23 + 15*27
|
||||||
|
N = 13*25 + 14*26 + 15*27 + 16*28 = M - 12*24 + 16*28
|
||||||
|
O = 14*26 + 15*27 + 16*28 + 17*29 = N - 13*25 + 17*29
|
||||||
|
P = 15*27 + 16*28 + 17*29 + 18*30 = O - 14*26 + 18*30
|
||||||
|
Q = 16*28 + 17*29 + 18*30 + 19*31 = P - 15*27 + 19*31
|
||||||
|
R = 17*29 + 18*30 + 19*31 + 20*32 = Q - 16*28 + 20*32
|
||||||
|
S = 18*30 + 19*31 + 20*32 + 21*33 = R - 17*29 + 21*33
|
||||||
|
T = 19*31 + 20*32 + 21*33 + 22*34 = S - 18*30 + 22*34
|
||||||
|
U = 20*32 + 21*33 + 22*34 + 23*35 = T - 19*31 + 23*35
|
||||||
|
|
||||||
|
Conclusion:
|
||||||
|
sampCacheLen = usefulLen
|
||||||
|
prodCacheLen = cyclicLen
|
||||||
|
|
||||||
|
Peak correlation occurs when the current interpolated value is the last FFT sample
|
||||||
|
|
397
decoder_modules/dab_decoder/src/test.txt
Normal file
397
decoder_modules/dab_decoder/src/test.txt
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
5209
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2555
|
||||||
|
2549
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2548
|
||||||
|
2558
|
||||||
|
2551
|
||||||
|
2546
|
||||||
|
2559
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2555
|
||||||
|
2549
|
||||||
|
2550
|
||||||
|
2549
|
||||||
|
2557
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2546
|
||||||
|
2559
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2550
|
||||||
|
2557
|
||||||
|
2550
|
||||||
|
2556
|
||||||
|
2543
|
||||||
|
2556
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2546
|
||||||
|
2558
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2550
|
||||||
|
2555
|
||||||
|
2551
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2549
|
||||||
|
2552
|
||||||
|
5208
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2554
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2546
|
||||||
|
2557
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2548
|
||||||
|
2557
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2562
|
||||||
|
2539
|
||||||
|
2559
|
||||||
|
2548
|
||||||
|
2551
|
||||||
|
2551
|
||||||
|
2550
|
||||||
|
2556
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2548
|
||||||
|
2556
|
||||||
|
2550
|
||||||
|
2554
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2550
|
||||||
|
2555
|
||||||
|
2558
|
||||||
|
2545
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2548
|
||||||
|
2555
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2557
|
||||||
|
2550
|
||||||
|
2548
|
||||||
|
2553
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2547
|
||||||
|
2558
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2555
|
||||||
|
2549
|
||||||
|
2552
|
||||||
|
5208
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2550
|
||||||
|
2555
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2547
|
||||||
|
2557
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2546
|
||||||
|
2558
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2556
|
||||||
|
2548
|
||||||
|
2551
|
||||||
|
2555
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2555
|
||||||
|
2549
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2548
|
||||||
|
2550
|
||||||
|
2555
|
||||||
|
2554
|
||||||
|
2553
|
||||||
|
2556
|
||||||
|
2547
|
||||||
|
2552
|
||||||
|
2554
|
||||||
|
2552
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
5209
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2551
|
||||||
|
2547
|
||||||
|
2558
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2549
|
||||||
|
2551
|
||||||
|
2557
|
||||||
|
2549
|
||||||
|
2554
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2551
|
||||||
|
2548
|
||||||
|
2559
|
||||||
|
2549
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2551
|
||||||
|
2550
|
||||||
|
2557
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2555
|
||||||
|
2549
|
||||||
|
2557
|
||||||
|
2549
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2549
|
||||||
|
2554
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2547
|
||||||
|
2557
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2549
|
||||||
|
2555
|
||||||
|
2556
|
||||||
|
2550
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2541
|
||||||
|
2563
|
||||||
|
2552
|
||||||
|
5210
|
||||||
|
2550
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2558
|
||||||
|
2547
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2550
|
||||||
|
2554
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2551
|
||||||
|
2555
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2558
|
||||||
|
2546
|
||||||
|
2553
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2552
|
||||||
|
2553
|
||||||
|
2548
|
||||||
|
2554
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2551
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2554
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2551
|
||||||
|
2555
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2552
|
||||||
|
2550
|
||||||
|
2554
|
||||||
|
2550
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2553
|
||||||
|
2550
|
||||||
|
5210
|
||||||
|
2553
|
||||||
|
2549
|
||||||
|
2553
|
||||||
|
2551
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2556
|
||||||
|
2548
|
||||||
|
2549
|
||||||
|
2555
|
||||||
|
2551
|
||||||
|
2557
|
||||||
|
2548
|
||||||
|
2552
|
||||||
|
2552
|
||||||
|
2552
|
@ -35,11 +35,10 @@ bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules
|
|||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/airspyhf_source/airspyhf_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/bladerf_source/bladerf_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/file_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/fobossdr_source/fobossdr_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/file_source/fobossdr_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hackrf_source/hackrf_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/hermes_source/hermes_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/limesdr_source/limesdr_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/network_source/network_source.dylib
|
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/perseus_source/perseus_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/plutosdr_source/plutosdr_source.dylib
|
||||||
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib
|
bundle_install_binary $BUNDLE $BUNDLE/Contents/Plugins $BUILD_DIR/source_modules/rfnm_source/rfnm_source.dylib
|
||||||
|
@ -35,8 +35,6 @@ 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 $build_dir/source_modules/limesdr_source/Release/limesdr_source.dll sdrpp_windows_x64/modules/
|
||||||
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
|
cp 'C:/Program Files/PothosSDR/bin/LimeSuite.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
cp $build_dir/source_modules/network_source/Release/network_source.dll sdrpp_windows_x64/modules/
|
|
||||||
|
|
||||||
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
|
cp $build_dir/source_modules/perseus_source/Release/perseus_source.dll sdrpp_windows_x64/modules/
|
||||||
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
|
cp 'C:/Program Files/PothosSDR/bin/perseus-sdr.dll' sdrpp_windows_x64/
|
||||||
|
|
||||||
|
@ -168,9 +168,10 @@ public:
|
|||||||
writer.setSamplerate(samplerate);
|
writer.setSamplerate(samplerate);
|
||||||
|
|
||||||
// Open file
|
// Open file
|
||||||
|
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
||||||
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
|
std::string vfoName = (recMode == RECORDER_MODE_AUDIO) ? selectedStreamName : "";
|
||||||
std::string extension = ".wav";
|
std::string extension = ".wav";
|
||||||
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, recMode, vfoName) + extension);
|
std::string expandedPath = expandString(folderSelect.path + "/" + genFileName(nameTemplate, type, vfoName) + extension);
|
||||||
if (!writer.open(expandedPath)) {
|
if (!writer.open(expandedPath)) {
|
||||||
flog::error("Failed to open file for recording: {0}", expandedPath);
|
flog::error("Failed to open file for recording: {0}", expandedPath);
|
||||||
return;
|
return;
|
||||||
@ -451,7 +452,7 @@ private:
|
|||||||
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
{ RADIO_IFACE_MODE_RAW, "RAW" }
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string genFileName(std::string templ, int mode, std::string name) {
|
std::string genFileName(std::string templ, std::string type, std::string name) {
|
||||||
// Get data
|
// Get data
|
||||||
time_t now = time(0);
|
time_t now = time(0);
|
||||||
tm* ltm = localtime(&now);
|
tm* ltm = localtime(&now);
|
||||||
@ -461,9 +462,6 @@ private:
|
|||||||
freq += gui::waterfall.vfos[name]->generalOffset;
|
freq += gui::waterfall.vfos[name]->generalOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select the recording type string
|
|
||||||
std::string type = (recMode == RECORDER_MODE_AUDIO) ? "audio" : "baseband";
|
|
||||||
|
|
||||||
// Format to string
|
// Format to string
|
||||||
char freqStr[128];
|
char freqStr[128];
|
||||||
char hourStr[128];
|
char hourStr[128];
|
||||||
@ -472,7 +470,7 @@ private:
|
|||||||
char dayStr[128];
|
char dayStr[128];
|
||||||
char monStr[128];
|
char monStr[128];
|
||||||
char yearStr[128];
|
char yearStr[128];
|
||||||
const char* modeStr = (recMode == RECORDER_MODE_AUDIO) ? "Unknown" : "IQ";
|
const char* modeStr = "Unknown";
|
||||||
sprintf(freqStr, "%.0lfHz", freq);
|
sprintf(freqStr, "%.0lfHz", freq);
|
||||||
sprintf(hourStr, "%02d", ltm->tm_hour);
|
sprintf(hourStr, "%02d", ltm->tm_hour);
|
||||||
sprintf(minStr, "%02d", ltm->tm_min);
|
sprintf(minStr, "%02d", ltm->tm_min);
|
||||||
|
13
readme.md
13
readme.md
@ -324,13 +324,12 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
|
| audio_source | Working | rtaudio | OPT_BUILD_AUDIO_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
|
| bladerf_source | Working | libbladeRF | OPT_BUILD_BLADERF_SOURCE | ⛔ | ✅ (not Debian Buster) | ✅ |
|
||||||
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
| file_source | Working | - | OPT_BUILD_FILE_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| fobossdr_source | Working | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
|
| fobossdr_source | Beta | libfobos | OPT_BUILD_FOBOSSDR_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
|
| hackrf_source | Working | libhackrf | OPT_BUILD_HACKRF_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ✅ |
|
| harogic_source | Beta | htra_api | OPT_BUILD_HAROGIC_SOURCE | ⛔ | ⛔ | ✅ |
|
||||||
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
|
| hermes_source | Beta | - | OPT_BUILD_HERMES_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| kcsdr_source | Unfinished | libkcsdr | OPT_BUILD_KCSDR_SOURCE | ⛔ | ⛔ | ⛔ |
|
|
||||||
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
|
| limesdr_source | Working | liblimesuite | OPT_BUILD_LIMESDR_SOURCE | ⛔ | ✅ | ✅ |
|
||||||
| network_source | Beta | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | ✅ |
|
| network_source | Unfinished | - | OPT_BUILD_NETWORK_SOURCE | ✅ | ✅ | ⛔ |
|
||||||
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
|
| perseus_source | Beta | libperseus-sdr | OPT_BUILD_PERSEUS_SOURCE | ⛔ | ✅ | ✅ |
|
||||||
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
|
| plutosdr_source | Working | libiio, libad9361 | OPT_BUILD_PLUTOSDR_SOURCE | ✅ | ✅ | ✅ |
|
||||||
| rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | ✅ | ✅ |
|
| rfnm_source | Beta | librfnm | OPT_BUILD_RFNM_SOURCE | ⛔ | ✅ | ✅ |
|
||||||
@ -352,8 +351,8 @@ Modules in beta are still included in releases for the most part but not enabled
|
|||||||
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
|
| android_audio_sink | Working | - | OPT_BUILD_ANDROID_AUDIO_SINK | ⛔ | ✅ | ✅ (Android only) |
|
||||||
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
|
| audio_sink | Working | rtaudio | OPT_BUILD_AUDIO_SINK | ✅ | ✅ | ✅ |
|
||||||
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
|
| network_sink | Working | - | OPT_BUILD_NETWORK_SINK | ✅ | ✅ | ✅ |
|
||||||
| new_portaudio_sink | Working | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
| new_portaudio_sink | Beta | portaudio | OPT_BUILD_NEW_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||||
| portaudio_sink | Working | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
| portaudio_sink | Beta | portaudio | OPT_BUILD_PORTAUDIO_SINK | ⛔ | ✅ | ⛔ |
|
||||||
|
|
||||||
## Decoders
|
## Decoders
|
||||||
|
|
||||||
@ -419,8 +418,8 @@ If you still have an issue, please open an issue about it or ask on the discord.
|
|||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
Feel free to submit band plans via the GitHub issue tracker.
|
Feel free to submit pull requests and report bugs via the GitHub issue tracker.
|
||||||
For code changes, please create a feature request instead.
|
I will soon publish a contributing.md listing the code style to use.
|
||||||
|
|
||||||
# Credits
|
# Credits
|
||||||
|
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Ireland",
|
|
||||||
"country_name": "Republic Of Ireland",
|
|
||||||
"country_code": "IE",
|
|
||||||
"author_name": "Oskar Dudek",
|
|
||||||
"author_url": "",
|
|
||||||
"bands": [
|
|
||||||
{
|
|
||||||
"name": "2200m Ham Band",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 135700,
|
|
||||||
"end": 137800
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Long wave",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 148500,
|
|
||||||
"end": 282500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "AM broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 531000,
|
|
||||||
"end": 1602000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,549 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Republic of Korea",
|
|
||||||
"country_name": "Republic of Korea",
|
|
||||||
"country_code": "KR",
|
|
||||||
"author_name": "SeoyeonBae",
|
|
||||||
"author_url": "https://github.com/bsy0317",
|
|
||||||
"bands": [
|
|
||||||
{
|
|
||||||
"name": "Radio Navigation",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 8300,
|
|
||||||
"end": 14000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Coastal Telegraph",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 14000,
|
|
||||||
"end": 19950
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Standard Frequency Time Signal",
|
|
||||||
"type": "utility",
|
|
||||||
"start": 19950,
|
|
||||||
"end": 20250
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Coastal Telegraph",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 20250,
|
|
||||||
"end": 70000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Radio Navigation",
|
|
||||||
"type": "navigation",
|
|
||||||
"start": 70000,
|
|
||||||
"end": 160000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Radio Navigation",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 160000,
|
|
||||||
"end": 285000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Maritime Radiobeacon",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 285000,
|
|
||||||
"end": 325000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Radio Navigation",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 325000,
|
|
||||||
"end": 472000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 472000,
|
|
||||||
"end": 479000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "International Distress Safety Call",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 479000,
|
|
||||||
"end": 505000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Maritime Telegraph",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 505000,
|
|
||||||
"end": 526500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Standard Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 526500,
|
|
||||||
"end": 1606500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Radiobuoy",
|
|
||||||
"type": "navigation",
|
|
||||||
"start": 1606500,
|
|
||||||
"end": 1800000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 1800000,
|
|
||||||
"end": 1825000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Radiobuoy Control LORAN",
|
|
||||||
"type": "radiolocation",
|
|
||||||
"start": 1825000,
|
|
||||||
"end": 2000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Radiobuoy",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 2000000,
|
|
||||||
"end": 2065000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Distress Call",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 2065000,
|
|
||||||
"end": 2107000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "International Distress Search and Rescue",
|
|
||||||
"type": "mobile",
|
|
||||||
"start": 2173500,
|
|
||||||
"end": 2190500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Road Management",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 2194000,
|
|
||||||
"end": 2495000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Standard Frequency Time Signal",
|
|
||||||
"type": "utility",
|
|
||||||
"start": 2495000,
|
|
||||||
"end": 2505000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ship Station Telephone",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 2505000,
|
|
||||||
"end": 2850000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Mobile R",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 2850000,
|
|
||||||
"end": 3025000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Mobile OR",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 3025000,
|
|
||||||
"end": 3155000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Mobile R",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 3400000,
|
|
||||||
"end": 3500000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 3500000,
|
|
||||||
"end": 3550000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Experimental Station",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 3550000,
|
|
||||||
"end": 3790000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 3790000,
|
|
||||||
"end": 3800000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 3900000,
|
|
||||||
"end": 3950000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Standard Frequency Time Signal",
|
|
||||||
"type": "utility",
|
|
||||||
"start": 3995000,
|
|
||||||
"end": 4005000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ship Station Telephone",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 4005000,
|
|
||||||
"end": 4063000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Oceanographic Data",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 4063000,
|
|
||||||
"end": 4065000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ship Station Duplex Telephone",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 4065000,
|
|
||||||
"end": 4146000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ship Station Simplex Telephone",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 4146000,
|
|
||||||
"end": 4152000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ship Station Wideband Telegraph Fax",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 4152000,
|
|
||||||
"end": 4172000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ship Station Narrowband",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 4172000,
|
|
||||||
"end": 4181750
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ship Station A1A Morse Code Communication",
|
|
||||||
"type": "marine",
|
|
||||||
"start": 4186750,
|
|
||||||
"end": 4202250
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Radiolocation",
|
|
||||||
"type": "radiolocation",
|
|
||||||
"start": 4438000,
|
|
||||||
"end": 4488000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Calling Response",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 4488000,
|
|
||||||
"end": 4650000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Mobile R",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 4650000,
|
|
||||||
"end": 4850000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Standard Frequency Time Signal",
|
|
||||||
"type": "utility",
|
|
||||||
"start": 4995000,
|
|
||||||
"end": 5005000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Search Rescue",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 5480000,
|
|
||||||
"end": 5730000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 5900000,
|
|
||||||
"end": 5950000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 5950000,
|
|
||||||
"end": 6200000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 7000000,
|
|
||||||
"end": 7100000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 7100000,
|
|
||||||
"end": 7200000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 7200000,
|
|
||||||
"end": 7450000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Standard Frequency Time Signal",
|
|
||||||
"type": "utility",
|
|
||||||
"start": 7995000,
|
|
||||||
"end": 8005000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 9400000,
|
|
||||||
"end": 9500000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 9500000,
|
|
||||||
"end": 9900000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Standard Frequency Time Signal",
|
|
||||||
"type": "utility",
|
|
||||||
"start": 9995000,
|
|
||||||
"end": 10005000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 10100000,
|
|
||||||
"end": 10150000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Mobile",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 10150000,
|
|
||||||
"end": 11600000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 11600000,
|
|
||||||
"end": 11650000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 11650000,
|
|
||||||
"end": 12050000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 12050000,
|
|
||||||
"end": 12100000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Mobile",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 13260000,
|
|
||||||
"end": 13360000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Radio Astronomy",
|
|
||||||
"type": "astronomy",
|
|
||||||
"start": 13360000,
|
|
||||||
"end": 13410000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 13570000,
|
|
||||||
"end": 13600000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 13600000,
|
|
||||||
"end": 13800000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 13800000,
|
|
||||||
"end": 13870000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 14000000,
|
|
||||||
"end": 14350000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Aviation Mobile",
|
|
||||||
"type": "aviation",
|
|
||||||
"start": 15010000,
|
|
||||||
"end": 15100000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 15100000,
|
|
||||||
"end": 15600000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 15600000,
|
|
||||||
"end": 15800000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 15800000,
|
|
||||||
"end": 15995000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Standard Frequency Time Signal",
|
|
||||||
"type": "utility",
|
|
||||||
"start": 15995000,
|
|
||||||
"end": 16005000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 18900000,
|
|
||||||
"end": 19020000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 21000000,
|
|
||||||
"end": 21450000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 21450000,
|
|
||||||
"end": 21850000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 24890000,
|
|
||||||
"end": 24990000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Shortwave Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 25670000,
|
|
||||||
"end": 26100000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 28000000,
|
|
||||||
"end": 29700000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 50000000,
|
|
||||||
"end": 54000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TV Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 54000000,
|
|
||||||
"end": 72000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Flood Warning",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 72000000,
|
|
||||||
"end": 74800000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TV Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 76000000,
|
|
||||||
"end": 88000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FM Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 88000000,
|
|
||||||
"end": 100000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "FM Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 100000000,
|
|
||||||
"end": 108000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ILS Localizer VOR",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 108000000,
|
|
||||||
"end": 117975000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Amateur Station",
|
|
||||||
"type": "amateur",
|
|
||||||
"start": 144000000,
|
|
||||||
"end": 146000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "General Communication",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 146000000,
|
|
||||||
"end": 148000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Low Power Device",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 162037500,
|
|
||||||
"end": 174000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "TV Broadcast",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 174000000,
|
|
||||||
"end": 216000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Low Power Device",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 216000000,
|
|
||||||
"end": 230000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Low Power Device",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 273000000,
|
|
||||||
"end": 322000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Personal Radio",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 420000000,
|
|
||||||
"end": 470000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Public Network",
|
|
||||||
"type": "broadcast",
|
|
||||||
"start": 698000000,
|
|
||||||
"end": 806000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Low Power Device",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 942000000,
|
|
||||||
"end": 960000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Satellite Mobile Communication",
|
|
||||||
"type": "fixed",
|
|
||||||
"start": 15250000000,
|
|
||||||
"end": 16605000000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Mobile Communication",
|
|
||||||
"type": "mobile",
|
|
||||||
"start": 25000000000,
|
|
||||||
"end": 37000000000
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -10,7 +10,6 @@
|
|||||||
#include <libbladeRF.h>
|
#include <libbladeRF.h>
|
||||||
#include <gui/smgui.h>
|
#include <gui/smgui.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utils/optionlist.h>
|
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
@ -38,10 +37,6 @@ public:
|
|||||||
BladeRFSourceModule(std::string name) {
|
BladeRFSourceModule(std::string name) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
|
|
||||||
// Define clocks
|
|
||||||
clocks.define("onboard", "On-Board", CLOCK_SELECT_ONBOARD);
|
|
||||||
clocks.define("external", "External", CLOCK_SELECT_EXTERNAL);
|
|
||||||
|
|
||||||
sampleRate = 1000000.0;
|
sampleRate = 1000000.0;
|
||||||
|
|
||||||
handler.ctx = this;
|
handler.ctx = this;
|
||||||
@ -272,15 +267,6 @@ public:
|
|||||||
}
|
}
|
||||||
config.release(true);
|
config.release(true);
|
||||||
|
|
||||||
// Load clock source
|
|
||||||
clkId = clocks.keyId("onboard");
|
|
||||||
if (config.conf["devices"][selectedSerial].contains("clock")) {
|
|
||||||
std::string clkStr = config.conf["devices"][selectedSerial]["clock"];
|
|
||||||
if (clocks.keyExists(clkStr)) {
|
|
||||||
clkId = clocks.keyId(clkStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load gain mode
|
// Load gain mode
|
||||||
if (config.conf["devices"][selectedSerial].contains("gainMode")) {
|
if (config.conf["devices"][selectedSerial].contains("gainMode")) {
|
||||||
std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
|
std::string gm = config.conf["devices"][selectedSerial]["gainMode"];
|
||||||
@ -378,7 +364,6 @@ private:
|
|||||||
if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
|
if (_this->bufferSize < 1024) { _this->bufferSize = 1024; }
|
||||||
|
|
||||||
// Setup device parameters
|
// Setup device parameters
|
||||||
_this->setClockSource(_this->clocks[_this->clkId]);
|
|
||||||
bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL);
|
bladerf_set_sample_rate(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->sampleRate, NULL);
|
||||||
bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
|
bladerf_set_frequency(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), _this->freq);
|
||||||
bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
|
bladerf_set_bandwidth(_this->openDev, BLADERF_CHANNEL_RX(_this->chanId), (_this->bwId == _this->bandwidths.size()) ? std::clamp<uint64_t>(_this->sampleRate, _this->bwRange->min, _this->bwRange->max) : _this->bandwidths[_this->bwId], NULL);
|
||||||
@ -501,19 +486,6 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SmGui::LeftLabel("Clock Source");
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::Combo(CONCAT("##_balderf_clk_sel_", _this->name), &_this->clkId, _this->clocks.txt)) {
|
|
||||||
if (_this->running) {
|
|
||||||
_this->setClockSource(_this->clocks[_this->clkId]);
|
|
||||||
}
|
|
||||||
if (_this->selectedSerial != "") {
|
|
||||||
config.acquire();
|
|
||||||
config.conf["devices"][_this->selectedSerial]["clock"] = _this->clocks.key(_this->clkId);
|
|
||||||
config.release(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// General config BS
|
// General config BS
|
||||||
SmGui::LeftLabel("Gain control mode");
|
SmGui::LeftLabel("Gain control mode");
|
||||||
SmGui::FillWidth();
|
SmGui::FillWidth();
|
||||||
@ -565,15 +537,6 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setClockSource(bladerf_clock_select clk) {
|
|
||||||
if (selectedBladeType == BLADERF_TYPE_V1) {
|
|
||||||
bladerf_set_smb_mode(openDev, (clk == CLOCK_SELECT_EXTERNAL) ? BLADERF_SMB_MODE_INPUT : BLADERF_SMB_MODE_DISABLED);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
bladerf_set_clock_select(openDev, clk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void worker() {
|
void worker() {
|
||||||
int16_t* buffer = new int16_t[bufferSize * 2];
|
int16_t* buffer = new int16_t[bufferSize * 2];
|
||||||
bladerf_metadata meta;
|
bladerf_metadata meta;
|
||||||
@ -602,7 +565,6 @@ private:
|
|||||||
int devId = 0;
|
int devId = 0;
|
||||||
int srId = 0;
|
int srId = 0;
|
||||||
int bwId = 0;
|
int bwId = 0;
|
||||||
int clkId = 0;
|
|
||||||
int chanId = 0;
|
int chanId = 0;
|
||||||
int gainMode = 0;
|
int gainMode = 0;
|
||||||
bool streamingEnabled = false;
|
bool streamingEnabled = false;
|
||||||
@ -618,8 +580,8 @@ private:
|
|||||||
std::string sampleRatesTxt;
|
std::string sampleRatesTxt;
|
||||||
std::vector<uint64_t> bandwidths;
|
std::vector<uint64_t> bandwidths;
|
||||||
std::string bandwidthsTxt;
|
std::string bandwidthsTxt;
|
||||||
|
|
||||||
std::string channelNamesTxt;
|
std::string channelNamesTxt;
|
||||||
OptionList<std::string, bladerf_clock_select> clocks;
|
|
||||||
|
|
||||||
int bufferSize;
|
int bufferSize;
|
||||||
struct bladerf_stream* rxStream;
|
struct bladerf_stream* rxStream;
|
||||||
|
1
source_modules/kcsdr_source/.gitignore
vendored
1
source_modules/kcsdr_source/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
vendor/*
|
|
@ -1,10 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
|
||||||
project(kcsdr_source)
|
|
||||||
|
|
||||||
file(GLOB SRC "src/*.cpp" "src/*.c")
|
|
||||||
|
|
||||||
include(${SDRPP_MODULE_CMAKE})
|
|
||||||
|
|
||||||
target_link_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10/x64/DLL")
|
|
||||||
target_include_directories(kcsdr_source PRIVATE "vendor/FTD3XXLibrary_1.3.0.10")
|
|
||||||
target_link_libraries(kcsdr_source PRIVATE FTD3XX)
|
|
@ -1,209 +0,0 @@
|
|||||||
#include "kcsdr.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include "../vendor/FTD3XXLibrary_1.3.0.10/FTD3XX.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#define KCSDR_PKT_EMPTY_LEN 0x0C
|
|
||||||
|
|
||||||
#define KCSDR_COMMAND_PIPE 0x02
|
|
||||||
#define KCSDR_RX_DATA_PIPE 0x83
|
|
||||||
#define KCSDR_TX_DATA_PIPE 0x03
|
|
||||||
|
|
||||||
struct kcsdr {
|
|
||||||
FT_HANDLE ft;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
|
||||||
struct kcsdr_packet {
|
|
||||||
uint8_t zeros0[4];
|
|
||||||
uint8_t length;
|
|
||||||
uint8_t zeros1[2];
|
|
||||||
uint8_t hex_eighty;
|
|
||||||
uint32_t command;
|
|
||||||
uint8_t data[188];
|
|
||||||
};
|
|
||||||
typedef struct kcsdr_packet kcsdr_packet_t;
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
enum kcsdr_command {
|
|
||||||
CMD_NOT_USED_0x00 = 0x00,
|
|
||||||
CMD_SET_PORT = 0x01,
|
|
||||||
CMD_SET_FREQUENCY = 0x02,
|
|
||||||
CMD_SET_ATTENUATION = 0x03,
|
|
||||||
CMD_SET_AMPLIFIER = 0x04,
|
|
||||||
CMD_SET_BANDWIDTH = 0x05,
|
|
||||||
CMD_START = 0x06,
|
|
||||||
CMD_STOP = 0x07,
|
|
||||||
CMD_SET_EXT_AMP = 0x08,
|
|
||||||
CMD_START_REMOTE = 0x09,
|
|
||||||
CMD_STOP_REMOTE = 0x0A
|
|
||||||
};
|
|
||||||
typedef enum kcsdr_command kcsdr_command_t;
|
|
||||||
|
|
||||||
int kcsdr_send_command(kcsdr_t* dev, kcsdr_direction_t dir, kcsdr_command_t cmd, const uint8_t* data, int len) {
|
|
||||||
Sleep(50);
|
|
||||||
|
|
||||||
// Create an empty packet
|
|
||||||
kcsdr_packet_t pkt;
|
|
||||||
memset(&pkt, 0, sizeof(kcsdr_packet_t));
|
|
||||||
|
|
||||||
// Fill out the packet info
|
|
||||||
pkt.length = len + KCSDR_PKT_EMPTY_LEN;
|
|
||||||
pkt.hex_eighty = 0x80; // Whatever the fuck that is
|
|
||||||
pkt.command = (uint32_t)cmd | (uint32_t)dir;
|
|
||||||
|
|
||||||
// Copy the data if there is some
|
|
||||||
if (len) { memcpy(pkt.data, data, len); }
|
|
||||||
|
|
||||||
// Dump the bytes
|
|
||||||
uint8_t* dump = (uint8_t*)&pkt;
|
|
||||||
printf("Sending:");
|
|
||||||
for (int i = 0; i < pkt.length; i++) {
|
|
||||||
printf(" %02X", dump[i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// Send the command to endpoint 0
|
|
||||||
int sent;
|
|
||||||
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_COMMAND_PIPE, (uint8_t*)&pkt, sizeof(kcsdr_packet_t), &sent, NULL);
|
|
||||||
if (err != FT_OK) {
|
|
||||||
return -err;
|
|
||||||
}
|
|
||||||
printf("Sent %d bytes (%d)\n", sent, err);
|
|
||||||
|
|
||||||
Sleep(50);
|
|
||||||
|
|
||||||
// Flush existing commands
|
|
||||||
FT_FlushPipe(dev->ft, KCSDR_COMMAND_PIPE);
|
|
||||||
|
|
||||||
return -(int)err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_list_devices(kcsdr_info_t** devices) {
|
|
||||||
// Generate a list of FTDI devices
|
|
||||||
int ftdiDevCount = 0;
|
|
||||||
FT_STATUS err = FT_CreateDeviceInfoList(&ftdiDevCount);
|
|
||||||
if (err != FT_OK) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no device was found, return nothing
|
|
||||||
if (!ftdiDevCount) {
|
|
||||||
*devices = NULL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get said device list
|
|
||||||
FT_DEVICE_LIST_INFO_NODE* list = malloc(ftdiDevCount * sizeof(FT_DEVICE_LIST_INFO_NODE));
|
|
||||||
err = FT_GetDeviceInfoList(list, &ftdiDevCount);
|
|
||||||
if (err != FT_OK) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate the device info list
|
|
||||||
*devices = malloc(ftdiDevCount * sizeof(kcsdr_info_t));
|
|
||||||
|
|
||||||
// Find all KC908s
|
|
||||||
int kcCount = 0;
|
|
||||||
for (int i = 0; i < ftdiDevCount; i++) {
|
|
||||||
strcpy((*devices)[kcCount++].serial, list[i].SerialNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free the FTDI list
|
|
||||||
free(list);
|
|
||||||
|
|
||||||
return kcCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void kcsdr_free_device_list(kcsdr_info_t* devices) {
|
|
||||||
// Free the list
|
|
||||||
if (devices) { free(devices); }
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_open(kcsdr_t** dev, const char* serial) {
|
|
||||||
// Attempt to open the device using the serial number
|
|
||||||
FT_HANDLE ft;
|
|
||||||
FT_STATUS err = FT_Create(serial, FT_OPEN_BY_SERIAL_NUMBER, &ft);
|
|
||||||
if (err != FT_OK) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the timeouts for the data pipes
|
|
||||||
FT_SetPipeTimeout(ft, KCSDR_RX_DATA_PIPE, 1000);
|
|
||||||
FT_SetPipeTimeout(ft, KCSDR_TX_DATA_PIPE, 1000);
|
|
||||||
|
|
||||||
// Allocate the device object
|
|
||||||
*dev = malloc(sizeof(kcsdr_t));
|
|
||||||
|
|
||||||
// Fill out the device object
|
|
||||||
(*dev)->ft = ft;
|
|
||||||
|
|
||||||
// Put device into remote control mode
|
|
||||||
return kcsdr_send_command(*dev, KCSDR_DIR_RX, CMD_START_REMOTE, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void kcsdr_close(kcsdr_t* dev) {
|
|
||||||
// Put device back in normal mode
|
|
||||||
kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_STOP_REMOTE, NULL, 0);
|
|
||||||
|
|
||||||
// Close the device
|
|
||||||
FT_Close(dev->ft);
|
|
||||||
|
|
||||||
// Free the device object
|
|
||||||
free(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port) {
|
|
||||||
// Send SET_PORT command
|
|
||||||
return kcsdr_send_command(dev, dir, CMD_SET_PORT, &port, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq) {
|
|
||||||
// Send SET_FREQUENCY command
|
|
||||||
return kcsdr_send_command(dev, dir, CMD_SET_FREQUENCY, (uint8_t*)&freq, 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att) {
|
|
||||||
// Send SET_ATTENUATION command
|
|
||||||
return kcsdr_send_command(dev, dir, CMD_SET_ATTENUATION, &att, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain) {
|
|
||||||
// Send SET_AMPLIFIER command
|
|
||||||
return kcsdr_send_command(dev, dir, CMD_SET_AMPLIFIER, &gain, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain) {
|
|
||||||
// Send CMD_SET_EXT_AMP command
|
|
||||||
return kcsdr_send_command(dev, KCSDR_DIR_RX, CMD_SET_EXT_AMP, &gain, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate) {
|
|
||||||
// Set SET_BANDWIDTH command
|
|
||||||
return kcsdr_send_command(dev, dir, CMD_SET_BANDWIDTH, (uint8_t*)&samplerate, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir) {
|
|
||||||
// Send START command
|
|
||||||
return kcsdr_send_command(dev, dir, CMD_START, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir) {
|
|
||||||
// Send STOP command
|
|
||||||
return kcsdr_send_command(dev, dir, CMD_STOP, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count) {
|
|
||||||
// Receive samples (TODO: Endpoint might be 0x81)
|
|
||||||
int received;
|
|
||||||
FT_STATUS err = FT_ReadPipeEx(dev->ft, KCSDR_RX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &received, NULL);
|
|
||||||
return (err == FT_OK) ? received / (2*sizeof(uint16_t)) : -(int)err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count) {
|
|
||||||
// Transmit samples
|
|
||||||
int sent;
|
|
||||||
FT_STATUS err = FT_WritePipeEx(dev->ft, KCSDR_TX_DATA_PIPE, (uint8_t*)samples, count*2*sizeof(uint16_t), &sent, NULL);
|
|
||||||
return (err == FT_OK) ? sent / (2*sizeof(uint16_t)) : -(int)err;
|
|
||||||
}
|
|
@ -1,150 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define KCSDR_SERIAL_LEN 16
|
|
||||||
#define KCSDR_MAX_PORTS 6
|
|
||||||
|
|
||||||
// Detect C++
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* KCSDR Device.
|
|
||||||
*/
|
|
||||||
struct kcsdr;
|
|
||||||
typedef struct kcsdr kcsdr_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Device Information
|
|
||||||
*/
|
|
||||||
struct kcsdr_info {
|
|
||||||
char serial[KCSDR_SERIAL_LEN+1];
|
|
||||||
};
|
|
||||||
typedef struct kcsdr_info kcsdr_info_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* RF Direction.
|
|
||||||
*/
|
|
||||||
enum kcsdr_direction {
|
|
||||||
KCSDR_DIR_RX = 0x00,
|
|
||||||
KCSDR_DIR_TX = 0x80
|
|
||||||
};
|
|
||||||
typedef enum kcsdr_direction kcsdr_direction_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of KCSDR devices on the system.
|
|
||||||
* @param devices Pointer to an array of device info.
|
|
||||||
* @return Number of devices found or error code.
|
|
||||||
*/
|
|
||||||
int kcsdr_list_devices(kcsdr_info_t** devices);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a device list returned by `kcsdr_list_devices()`.
|
|
||||||
* @param devices Device list to free.
|
|
||||||
*/
|
|
||||||
void kcsdr_free_device_list(kcsdr_info_t* devices);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a KCSDR device.
|
|
||||||
* @param dev Newly open device.
|
|
||||||
* @param serial Serial number of the device to open as returned in the device list.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_open(kcsdr_t** dev, const char* serial);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close a KCSDR device.
|
|
||||||
* @param dev Device to be closed.
|
|
||||||
*/
|
|
||||||
void kcsdr_close(kcsdr_t* dev);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select the RF port.
|
|
||||||
* @param dev Device to control.
|
|
||||||
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
|
|
||||||
* @param port RF port number to select.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_set_port(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t port);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the center frequency.
|
|
||||||
* @param dev Device to control.
|
|
||||||
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
|
|
||||||
* @param freq Frequency in Hz.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_set_frequency(kcsdr_t* dev, kcsdr_direction_t dir, uint64_t freq);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the attenuation.
|
|
||||||
* @param dev Device to control.
|
|
||||||
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
|
|
||||||
* @param samplerate Attenuation in dB.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_set_attenuation(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t att);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the internal amplifier gain.
|
|
||||||
* @param dev Device to control.
|
|
||||||
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
|
|
||||||
* @param gain Gain in dB.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_set_amp_gain(kcsdr_t* dev, kcsdr_direction_t dir, uint8_t gain);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the external amplifier gain.
|
|
||||||
* @param dev Device to control.
|
|
||||||
* @param gain Gain in dB.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_set_rx_ext_amp_gain(kcsdr_t* dev, uint8_t gain);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the samplerate.
|
|
||||||
* @param dev Device to control.
|
|
||||||
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX
|
|
||||||
* @param samplerate Samplerate in Hz.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_set_samplerate(kcsdr_t* dev, kcsdr_direction_t dir, uint32_t samplerate);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start streaming samples.
|
|
||||||
* @param dev Device to control.
|
|
||||||
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_start(kcsdr_t* dev, kcsdr_direction_t dir);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop streaming samples.
|
|
||||||
* @param dev Device to control.
|
|
||||||
* @param dir Either KCSDR_DIR_RX or KCSDR_DIR_TX.
|
|
||||||
* @return 0 on success, error code otherwise.
|
|
||||||
*/
|
|
||||||
int kcsdr_stop(kcsdr_t* dev, kcsdr_direction_t dir);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receive a buffer of samples.
|
|
||||||
* @param samples Sample buffer.
|
|
||||||
* @param count Number of complex samples.
|
|
||||||
* @return Number of samples received.
|
|
||||||
*/
|
|
||||||
int kcsdr_rx(kcsdr_t* dev, int16_t* samples, int count);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transmit a buffer of samples.
|
|
||||||
* @param samples Sample buffer.
|
|
||||||
* @param count Number of complex samples.
|
|
||||||
* @return Number of samples transmitted.
|
|
||||||
*/
|
|
||||||
int kcsdr_tx(kcsdr_t* dev, const int16_t* samples, int count);
|
|
||||||
|
|
||||||
// Detect C++
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,324 +0,0 @@
|
|||||||
#include <imgui.h>
|
|
||||||
#include <module.h>
|
|
||||||
#include <gui/gui.h>
|
|
||||||
#include <gui/smgui.h>
|
|
||||||
#include <signal_path/signal_path.h>
|
|
||||||
#include <core.h>
|
|
||||||
#include <utils/optionlist.h>
|
|
||||||
#include "kcsdr.h"
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
SDRPP_MOD_INFO{
|
|
||||||
/* Name: */ "kcsdr_source",
|
|
||||||
/* Description: */ "KCSDR Source Module",
|
|
||||||
/* Author: */ "Ryzerth",
|
|
||||||
/* Version: */ 0, 1, 0,
|
|
||||||
/* Max instances */ -1
|
|
||||||
};
|
|
||||||
|
|
||||||
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
|
||||||
|
|
||||||
class KCSDRSourceModule : public ModuleManager::Instance {
|
|
||||||
public:
|
|
||||||
KCSDRSourceModule(std::string name) {
|
|
||||||
this->name = name;
|
|
||||||
|
|
||||||
sampleRate = 2000000.0;
|
|
||||||
samplerates.define(40e6, "40MHz", 40e6);
|
|
||||||
samplerates.define(35e6, "35MHz", 35e6);
|
|
||||||
samplerates.define(30e6, "30MHz", 30e6);
|
|
||||||
samplerates.define(25e6, "25MHz", 25e6);
|
|
||||||
samplerates.define(20e6, "20MHz", 20e6);
|
|
||||||
samplerates.define(15e6, "15MHz", 15e6);
|
|
||||||
samplerates.define(10e6, "10MHz", 10e6);
|
|
||||||
samplerates.define(5e6, "5MHz", 5e6);
|
|
||||||
|
|
||||||
handler.ctx = this;
|
|
||||||
handler.selectHandler = menuSelected;
|
|
||||||
handler.deselectHandler = menuDeselected;
|
|
||||||
handler.menuHandler = menuHandler;
|
|
||||||
handler.startHandler = start;
|
|
||||||
handler.stopHandler = stop;
|
|
||||||
handler.tuneHandler = tune;
|
|
||||||
handler.stream = &stream;
|
|
||||||
|
|
||||||
// Refresh devices
|
|
||||||
refresh();
|
|
||||||
|
|
||||||
// Select first (TODO: Select from config)
|
|
||||||
select("");
|
|
||||||
|
|
||||||
sigpath::sourceManager.registerSource("KCSDR", &handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
~KCSDRSourceModule() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void postInit() {}
|
|
||||||
|
|
||||||
void enable() {
|
|
||||||
enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable() {
|
|
||||||
enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void refresh() {
|
|
||||||
devices.clear();
|
|
||||||
|
|
||||||
// Get device list
|
|
||||||
kcsdr_info_t* list;
|
|
||||||
int count = kcsdr_list_devices(&list);
|
|
||||||
if (count < 0) {
|
|
||||||
flog::error("Failed to list devices: {}", count);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create list
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
devices.define(list[i].serial, list[i].serial, list[i].serial);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free the device list
|
|
||||||
kcsdr_free_device_list(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
void select(const std::string& serial) {
|
|
||||||
// If there are no devices, give up
|
|
||||||
if (devices.empty()) {
|
|
||||||
selectedSerial.clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the serial was not found, select the first available serial
|
|
||||||
if (!devices.keyExists(serial)) {
|
|
||||||
select(devices.key(0));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the menu ID
|
|
||||||
devId = devices.keyId(serial);
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
// Update the samplerate
|
|
||||||
core::setInputSampleRate(sampleRate);
|
|
||||||
|
|
||||||
// Save serial number
|
|
||||||
selectedSerial = serial;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void menuSelected(void* ctx) {
|
|
||||||
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
|
||||||
flog::info("KCSDRSourceModule '{0}': Menu Select!", _this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void menuDeselected(void* ctx) {
|
|
||||||
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
|
||||||
flog::info("KCSDRSourceModule '{0}': Menu Deselect!", _this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void start(void* ctx) {
|
|
||||||
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
|
||||||
if (_this->running) { return; }
|
|
||||||
|
|
||||||
// If no serial is given, do nothing
|
|
||||||
if (_this->selectedSerial.empty()) { return; }
|
|
||||||
|
|
||||||
// Open the device
|
|
||||||
int err = kcsdr_open(&_this->openDev, _this->selectedSerial.c_str());
|
|
||||||
if (err) {
|
|
||||||
flog::error("Failed to open device: {}", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure the device
|
|
||||||
kcsdr_set_port(_this->openDev, KCSDR_DIR_RX, 0);
|
|
||||||
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, _this->freq);
|
|
||||||
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
|
|
||||||
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
|
|
||||||
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
|
|
||||||
kcsdr_set_samplerate(_this->openDev, KCSDR_DIR_RX, _this->sampleRate);
|
|
||||||
|
|
||||||
// Start the stream
|
|
||||||
kcsdr_start(_this->openDev, KCSDR_DIR_RX);
|
|
||||||
|
|
||||||
// Start worker
|
|
||||||
_this->run = true;
|
|
||||||
_this->workerThread = std::thread(&KCSDRSourceModule::worker, _this);
|
|
||||||
|
|
||||||
_this->running = true;
|
|
||||||
flog::info("KCSDRSourceModule '{0}': Start!", _this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void stop(void* ctx) {
|
|
||||||
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
|
||||||
if (!_this->running) { return; }
|
|
||||||
_this->running = false;
|
|
||||||
|
|
||||||
// Stop worker
|
|
||||||
_this->run = false;
|
|
||||||
_this->stream.stopWriter();
|
|
||||||
if (_this->workerThread.joinable()) { _this->workerThread.join(); }
|
|
||||||
_this->stream.clearWriteStop();
|
|
||||||
|
|
||||||
// Stop streaming
|
|
||||||
kcsdr_stop(_this->openDev, KCSDR_DIR_RX);
|
|
||||||
|
|
||||||
// Close the device
|
|
||||||
kcsdr_close(_this->openDev);
|
|
||||||
|
|
||||||
flog::info("KCSDRSourceModule '{0}': Stop!", _this->name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tune(double freq, void* ctx) {
|
|
||||||
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
|
||||||
if (_this->running) {
|
|
||||||
kcsdr_set_frequency(_this->openDev, KCSDR_DIR_RX, freq);
|
|
||||||
}
|
|
||||||
_this->freq = freq;
|
|
||||||
flog::info("KCSDRSourceModule '{0}': Tune: {1}!", _this->name, freq);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void menuHandler(void* ctx) {
|
|
||||||
KCSDRSourceModule* _this = (KCSDRSourceModule*)ctx;
|
|
||||||
|
|
||||||
if (_this->running) { SmGui::BeginDisabled(); }
|
|
||||||
|
|
||||||
SmGui::FillWidth();
|
|
||||||
SmGui::ForceSync();
|
|
||||||
if (SmGui::Combo(CONCAT("##_kcsdr_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) {
|
|
||||||
_this->select(_this->devices.key(_this->devId));
|
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SmGui::Combo(CONCAT("##_kcsdr_sr_sel_", _this->name), &_this->srId, _this->samplerates.txt)) {
|
|
||||||
_this->sampleRate = _this->samplerates.value(_this->srId);
|
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
|
|
||||||
SmGui::SameLine();
|
|
||||||
SmGui::FillWidth();
|
|
||||||
SmGui::ForceSync();
|
|
||||||
if (SmGui::Button(CONCAT("Refresh##_kcsdr_refr_", _this->name))) {
|
|
||||||
_this->refresh();
|
|
||||||
_this->select(_this->selectedSerial);
|
|
||||||
core::setInputSampleRate(_this->sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_this->running) { SmGui::EndDisabled(); }
|
|
||||||
|
|
||||||
// SmGui::LeftLabel("RX Port");
|
|
||||||
// SmGui::FillWidth();
|
|
||||||
// if (SmGui::Combo(CONCAT("##_kcsdr_port_", _this->name), &_this->portId, _this->rxPorts.txt)) {
|
|
||||||
// if (_this->running) {
|
|
||||||
// // TODO
|
|
||||||
// }
|
|
||||||
// // TODO: Save
|
|
||||||
// }
|
|
||||||
|
|
||||||
SmGui::LeftLabel("Attenuation");
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::SliderInt(CONCAT("##_kcsdr_att_", _this->name), &_this->att, 0, 31)) {
|
|
||||||
if (_this->running) {
|
|
||||||
kcsdr_set_attenuation(_this->openDev, KCSDR_DIR_RX, _this->att);
|
|
||||||
}
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
|
|
||||||
SmGui::LeftLabel("Gain");
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::SliderInt(CONCAT("##_kcsdr_gain_", _this->name), &_this->gain, 0, 31)) {
|
|
||||||
if (_this->running) {
|
|
||||||
kcsdr_set_amp_gain(_this->openDev, KCSDR_DIR_RX, _this->gain);
|
|
||||||
}
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
|
|
||||||
SmGui::LeftLabel("External Gain");
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::SliderInt(CONCAT("##_kcsdr_ext_gain_", _this->name), &_this->extGain, 0, 31)) {
|
|
||||||
if (_this->running) {
|
|
||||||
kcsdr_set_rx_ext_amp_gain(_this->openDev, _this->extGain);
|
|
||||||
}
|
|
||||||
// TODO: Save
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void worker() {
|
|
||||||
// Compute the buffer size
|
|
||||||
int bufferSize = 0x4000/4;//sampleRate / 200;
|
|
||||||
|
|
||||||
// Allocate the sample buffer
|
|
||||||
int16_t* samps = dsp::buffer::alloc<int16_t>(bufferSize*2);
|
|
||||||
|
|
||||||
// Loop
|
|
||||||
while (run) {
|
|
||||||
// Read samples
|
|
||||||
int count = kcsdr_rx(openDev, samps, bufferSize);
|
|
||||||
if (!count) { continue; }
|
|
||||||
if (count < 0) {
|
|
||||||
flog::debug("Failed to read samples: {}", count);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the samples to float
|
|
||||||
volk_16i_s32f_convert_32f((float*)stream.writeBuf, samps, 8192.0f, count*2);
|
|
||||||
|
|
||||||
// Send out the samples
|
|
||||||
if (!stream.swap(count)) { break; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free the sample buffer
|
|
||||||
dsp::buffer::free(samps);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name;
|
|
||||||
bool enabled = true;
|
|
||||||
dsp::stream<dsp::complex_t> stream;
|
|
||||||
double sampleRate;
|
|
||||||
SourceManager::SourceHandler handler;
|
|
||||||
bool running = false;
|
|
||||||
double freq;
|
|
||||||
|
|
||||||
OptionList<std::string, std::string> devices;
|
|
||||||
OptionList<int, double> samplerates;
|
|
||||||
int devId = 0;
|
|
||||||
int srId = 0;
|
|
||||||
int att = 0;
|
|
||||||
int gain = 30;
|
|
||||||
int extGain = 1;
|
|
||||||
int portId = 0;
|
|
||||||
std::string selectedSerial;
|
|
||||||
|
|
||||||
kcsdr_t* openDev;
|
|
||||||
|
|
||||||
std::thread workerThread;
|
|
||||||
std::atomic<bool> run = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
MOD_EXPORT void _INIT_() {
|
|
||||||
// Nothing here
|
|
||||||
}
|
|
||||||
|
|
||||||
MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) {
|
|
||||||
return new KCSDRSourceModule(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
MOD_EXPORT void _DELETE_INSTANCE_(void* instance) {
|
|
||||||
delete (KCSDRSourceModule*)instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
MOD_EXPORT void _END_() {
|
|
||||||
// Nothing here
|
|
||||||
}
|
|
@ -36,10 +36,10 @@ enum SampleType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const size_t SAMPLE_TYPE_SIZE[] {
|
const size_t SAMPLE_TYPE_SIZE[] {
|
||||||
2*sizeof(int8_t),
|
sizeof(int8_t)*2,
|
||||||
2*sizeof(int16_t),
|
sizeof(int16_t)*2,
|
||||||
2*sizeof(int32_t),
|
sizeof(int32_t)*2,
|
||||||
2*sizeof(float),
|
sizeof(float)*2,
|
||||||
};
|
};
|
||||||
|
|
||||||
class NetworkSourceModule : public ModuleManager::Instance {
|
class NetworkSourceModule : public ModuleManager::Instance {
|
||||||
@ -58,6 +58,20 @@ public:
|
|||||||
handler.tuneHandler = tune;
|
handler.tuneHandler = tune;
|
||||||
handler.stream = &stream;
|
handler.stream = &stream;
|
||||||
|
|
||||||
|
// Define samplerates
|
||||||
|
for (int i = 3000; i <= 192000; i <<= 1) {
|
||||||
|
samplerates.define(i, getSrScaled(i), i);
|
||||||
|
}
|
||||||
|
for (int i = 250000; i < 1000000; i += 250000) {
|
||||||
|
samplerates.define(i, getSrScaled(i), i);
|
||||||
|
}
|
||||||
|
for (int i = 1000000; i < 10000000; i += 500000) {
|
||||||
|
samplerates.define(i, getSrScaled(i), i);
|
||||||
|
}
|
||||||
|
for (int i = 10000000; i <= 100000000; i += 5000000) {
|
||||||
|
samplerates.define(i, getSrScaled(i), i);
|
||||||
|
}
|
||||||
|
|
||||||
// Define protocols
|
// Define protocols
|
||||||
// protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
|
// protocols.define("TCP (Server)", PROTOCOL_TCP_SERVER);
|
||||||
protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
|
protocols.define("TCP (Client)", PROTOCOL_TCP_CLIENT);
|
||||||
@ -72,8 +86,8 @@ public:
|
|||||||
// Load config
|
// Load config
|
||||||
config.acquire();
|
config.acquire();
|
||||||
if (config.conf[name].contains("samplerate")) {
|
if (config.conf[name].contains("samplerate")) {
|
||||||
samplerate = config.conf[name]["samplerate"];
|
int sr = config.conf[name]["samplerate"];
|
||||||
tempSamplerate = samplerate;
|
if (samplerates.keyExists(sr)) { samplerate = samplerates.value(samplerates.keyId(sr)); }
|
||||||
}
|
}
|
||||||
if (config.conf[name].contains("protocol")) {
|
if (config.conf[name].contains("protocol")) {
|
||||||
std::string protoStr = config.conf[name]["protocol"];
|
std::string protoStr = config.conf[name]["protocol"];
|
||||||
@ -94,6 +108,7 @@ public:
|
|||||||
config.release();
|
config.release();
|
||||||
|
|
||||||
// Set menu IDs
|
// Set menu IDs
|
||||||
|
srId = samplerates.valueId(samplerate);
|
||||||
protoId = protocols.valueId(proto);
|
protoId = protocols.valueId(proto);
|
||||||
sampTypeId = sampleTypes.valueId(sampType);
|
sampTypeId = sampleTypes.valueId(sampType);
|
||||||
|
|
||||||
@ -213,24 +228,35 @@ private:
|
|||||||
if (_this->running) { SmGui::BeginDisabled(); }
|
if (_this->running) { SmGui::BeginDisabled(); }
|
||||||
|
|
||||||
// Hostname and port field
|
// Hostname and port field
|
||||||
if (SmGui::InputText(("##network_source_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
|
if (ImGui::InputText(("##iq_exporter_host_" + _this->name).c_str(), _this->hostname, sizeof(_this->hostname))) {
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["host"] = _this->hostname;
|
config.conf[_this->name]["host"] = _this->hostname;
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
SmGui::SameLine();
|
ImGui::SameLine();
|
||||||
SmGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
if (SmGui::InputInt(("##network_source_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
|
if (ImGui::InputInt(("##iq_exporter_port_" + _this->name).c_str(), &_this->port, 0, 0)) {
|
||||||
_this->port = std::clamp<int>(_this->port, 1, 65535);
|
_this->port = std::clamp<int>(_this->port, 1, 65535);
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["port"] = _this->port;
|
config.conf[_this->name]["port"] = _this->port;
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Samplerate selector
|
||||||
|
ImGui::LeftLabel("Samplerate");
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Combo(("##iq_exporter_sr_" + _this->name).c_str(), &_this->srId, _this->samplerates.txt)) {
|
||||||
|
_this->samplerate = _this->samplerates.value(_this->srId);
|
||||||
|
core::setInputSampleRate(_this->samplerate);
|
||||||
|
config.acquire();
|
||||||
|
config.conf[_this->name]["samplerate"] = _this->samplerates.key(_this->srId);
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Mode protocol selector
|
// Mode protocol selector
|
||||||
SmGui::LeftLabel("Protocol");
|
ImGui::LeftLabel("Protocol");
|
||||||
SmGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
if (SmGui::Combo(("##network_source_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
|
if (ImGui::Combo(("##iq_exporter_proto_" + _this->name).c_str(), &_this->protoId, _this->protocols.txt)) {
|
||||||
_this->proto = _this->protocols.value(_this->protoId);
|
_this->proto = _this->protocols.value(_this->protoId);
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
|
config.conf[_this->name]["protocol"] = _this->protocols.key(_this->protoId);
|
||||||
@ -238,38 +264,15 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sample type selector
|
// Sample type selector
|
||||||
SmGui::LeftLabel("Sample type");
|
ImGui::LeftLabel("Sample type");
|
||||||
SmGui::FillWidth();
|
ImGui::FillWidth();
|
||||||
if (SmGui::Combo(("##network_source_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
|
if (ImGui::Combo(("##iq_exporter_samp_" + _this->name).c_str(), &_this->sampTypeId, _this->sampleTypes.txt)) {
|
||||||
_this->sampType = _this->sampleTypes.value(_this->sampTypeId);
|
_this->sampType = _this->sampleTypes.value(_this->sampTypeId);
|
||||||
config.acquire();
|
config.acquire();
|
||||||
config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
|
config.conf[_this->name]["sampleType"] = _this->sampleTypes.key(_this->sampTypeId);
|
||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Samplerate selector
|
|
||||||
SmGui::LeftLabel("Samplerate");
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::InputInt(("##network_source_sr_" + _this->name).c_str(), &_this->tempSamplerate)) {
|
|
||||||
// Prevent silly values from silly users
|
|
||||||
_this->tempSamplerate = std::max<int>(_this->tempSamplerate, 1000);
|
|
||||||
}
|
|
||||||
bool applyEn = (!_this->running && _this->tempSamplerate != _this->samplerate);
|
|
||||||
if (!applyEn) { SmGui::BeginDisabled(); }
|
|
||||||
SmGui::FillWidth();
|
|
||||||
if (SmGui::Button(("Apply##network_source_apply_" + _this->name).c_str())) {
|
|
||||||
_this->samplerate = _this->tempSamplerate;
|
|
||||||
core::setInputSampleRate(_this->samplerate);
|
|
||||||
config.acquire();
|
|
||||||
config.conf[_this->name]["samplerate"] = _this->samplerate;
|
|
||||||
config.release(true);
|
|
||||||
}
|
|
||||||
if (!applyEn) { SmGui::EndDisabled(); }
|
|
||||||
|
|
||||||
if (_this->tempSamplerate != _this->samplerate) {
|
|
||||||
SmGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Warning: Samplerate not applied yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_this->running) { SmGui::EndDisabled(); }
|
if (_this->running) { SmGui::EndDisabled(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,17 +280,14 @@ private:
|
|||||||
// Compute sizes
|
// Compute sizes
|
||||||
int blockSize = samplerate / 200;
|
int blockSize = samplerate / 200;
|
||||||
int sampleSize = SAMPLE_TYPE_SIZE[sampType];
|
int sampleSize = SAMPLE_TYPE_SIZE[sampType];
|
||||||
|
int frameSize = blockSize*sampleSize;
|
||||||
// Chose amount of bytes to attempt to read
|
|
||||||
bool forceSize = (proto != PROTOCOL_UDP);
|
|
||||||
int frameSize = sampleSize * (forceSize ? blockSize : STREAM_BUFFER_SIZE);
|
|
||||||
|
|
||||||
// Allocate receive buffer
|
// Allocate receive buffer
|
||||||
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
|
uint8_t* buffer = dsp::buffer::alloc<uint8_t>(frameSize);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Read samples from socket
|
// Read samples from socket
|
||||||
int bytes = sock->recv(buffer, frameSize, forceSize);
|
int bytes = sock->recv(buffer, frameSize, true);
|
||||||
if (bytes <= 0) { break; }
|
if (bytes <= 0) { break; }
|
||||||
|
|
||||||
// Convert to CF32 (note: problem if partial sample)
|
// Convert to CF32 (note: problem if partial sample)
|
||||||
@ -325,7 +325,7 @@ private:
|
|||||||
double freq;
|
double freq;
|
||||||
|
|
||||||
int samplerate = 1000000;
|
int samplerate = 1000000;
|
||||||
int tempSamplerate = 1000000;
|
int srId;
|
||||||
Protocol proto = PROTOCOL_UDP;
|
Protocol proto = PROTOCOL_UDP;
|
||||||
int protoId;
|
int protoId;
|
||||||
SampleType sampType = SAMPLE_TYPE_INT16;
|
SampleType sampType = SAMPLE_TYPE_INT16;
|
||||||
@ -333,6 +333,7 @@ private:
|
|||||||
char hostname[1024] = "localhost";
|
char hostname[1024] = "localhost";
|
||||||
int port = 1234;
|
int port = 1234;
|
||||||
|
|
||||||
|
OptionList<int, int> samplerates;
|
||||||
OptionList<std::string, Protocol> protocols;
|
OptionList<std::string, Protocol> protocols;
|
||||||
OptionList<std::string, SampleType> sampleTypes;
|
OptionList<std::string, SampleType> sampleTypes;
|
||||||
|
|
||||||
|
@ -23,12 +23,6 @@ SDRPP_MOD_INFO{
|
|||||||
|
|
||||||
ConfigManager config;
|
ConfigManager config;
|
||||||
|
|
||||||
const std::vector<const char*> deviceWhiteList = {
|
|
||||||
"PlutoSDR",
|
|
||||||
"ANTSDR",
|
|
||||||
"LibreSDR"
|
|
||||||
};
|
|
||||||
|
|
||||||
class PlutoSDRSourceModule : public ModuleManager::Instance {
|
class PlutoSDRSourceModule : public ModuleManager::Instance {
|
||||||
public:
|
public:
|
||||||
PlutoSDRSourceModule(std::string name) {
|
PlutoSDRSourceModule(std::string name) {
|
||||||
@ -136,14 +130,7 @@ private:
|
|||||||
std::string duri = iio_context_info_get_uri(info);
|
std::string duri = iio_context_info_get_uri(info);
|
||||||
|
|
||||||
// If the device is not a plutosdr, don't include it
|
// If the device is not a plutosdr, don't include it
|
||||||
bool isPluto = false;
|
if (desc.find("PlutoSDR") == std::string::npos) {
|
||||||
for (const auto type : deviceWhiteList) {
|
|
||||||
if (desc.find(type) != std::string::npos) {
|
|
||||||
isPluto = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isPluto) {
|
|
||||||
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
|
flog::warn("Ignored IIO device: [{}] {}", duri, desc);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user