mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-26 10:58:29 +01:00
Added decimation
This commit is contained in:
parent
ffed602246
commit
e62351c206
@ -47,13 +47,15 @@ namespace core {
|
|||||||
GLFWwindow* window;
|
GLFWwindow* window;
|
||||||
|
|
||||||
void setInputSampleRate(double samplerate) {
|
void setInputSampleRate(double samplerate) {
|
||||||
|
sigpath::signalPath.sourceSampleRate = samplerate;
|
||||||
|
double effectiveSr = samplerate / ((double)(1 << sigpath::signalPath.decimation));
|
||||||
// NOTE: Zoom controls won't work
|
// NOTE: Zoom controls won't work
|
||||||
spdlog::info("New DSP samplerate: {0}", samplerate);
|
spdlog::info("New DSP samplerate: {0} (source samplerate is {1})", effectiveSr, samplerate);
|
||||||
gui::waterfall.setBandwidth(samplerate);
|
gui::waterfall.setBandwidth(effectiveSr);
|
||||||
gui::waterfall.setViewOffset(0);
|
gui::waterfall.setViewOffset(0);
|
||||||
gui::waterfall.setViewBandwidth(samplerate);
|
gui::waterfall.setViewBandwidth(effectiveSr);
|
||||||
sigpath::signalPath.setSampleRate(samplerate);
|
sigpath::signalPath.setSampleRate(effectiveSr);
|
||||||
gui::mainWindow.setViewBandwidthSlider(samplerate);
|
gui::mainWindow.setViewBandwidthSlider(effectiveSr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -202,6 +204,7 @@ int sdrpp_main(int argc, char *argv[]) {
|
|||||||
defConfig["showMenu"] = true;
|
defConfig["showMenu"] = true;
|
||||||
defConfig["showWaterfall"] = true;
|
defConfig["showWaterfall"] = true;
|
||||||
defConfig["source"] = "";
|
defConfig["source"] = "";
|
||||||
|
defConfig["decimationPower"] = 0;
|
||||||
|
|
||||||
defConfig["streams"]["Radio"]["muted"] = false;
|
defConfig["streams"]["Radio"]["muted"] = false;
|
||||||
defConfig["streams"]["Radio"]["sink"] = "Audio";
|
defConfig["streams"]["Radio"]["sink"] = "Audio";
|
||||||
|
108
core/src/dsp/decimation.h
Normal file
108
core/src/dsp/decimation.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <dsp/block.h>
|
||||||
|
#include <dsp/stream.h>
|
||||||
|
#include <dsp/types.h>
|
||||||
|
#include <dsp/window.h>
|
||||||
|
|
||||||
|
namespace dsp {
|
||||||
|
template <class T>
|
||||||
|
class HalfDecimator : public generic_block<HalfDecimator<T>> {
|
||||||
|
public:
|
||||||
|
HalfDecimator() {}
|
||||||
|
|
||||||
|
HalfDecimator(stream<T>* in, dsp::filter_window::generic_window* window) { init(in, window); }
|
||||||
|
|
||||||
|
~HalfDecimator() {
|
||||||
|
if (!generic_block<HalfDecimator<T>>::_block_init) { return; }
|
||||||
|
generic_block<HalfDecimator<T>>::stop();
|
||||||
|
volk_free(buffer);
|
||||||
|
volk_free(taps);
|
||||||
|
generic_block<HalfDecimator<T>>::_block_init = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(stream<T>* in, dsp::filter_window::generic_window* window) {
|
||||||
|
_in = in;
|
||||||
|
|
||||||
|
tapCount = window->getTapCount();
|
||||||
|
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||||
|
window->createTaps(taps, tapCount);
|
||||||
|
|
||||||
|
buffer = (T*)volk_malloc(STREAM_BUFFER_SIZE * sizeof(T) * 2, volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
|
generic_block<HalfDecimator<T>>::registerInput(_in);
|
||||||
|
generic_block<HalfDecimator<T>>::registerOutput(&out);
|
||||||
|
generic_block<HalfDecimator<T>>::_block_init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInput(stream<T>* in) {
|
||||||
|
assert(generic_block<HalfDecimator<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<HalfDecimator<T>>::ctrlMtx);
|
||||||
|
generic_block<HalfDecimator<T>>::tempStop();
|
||||||
|
generic_block<HalfDecimator<T>>::unregisterInput(_in);
|
||||||
|
_in = in;
|
||||||
|
generic_block<HalfDecimator<T>>::registerInput(_in);
|
||||||
|
generic_block<HalfDecimator<T>>::tempStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateWindow(dsp::filter_window::generic_window* window) {
|
||||||
|
assert(generic_block<HalfDecimator<T>>::_block_init);
|
||||||
|
std::lock_guard<std::mutex> lck(generic_block<HalfDecimator<T>>::ctrlMtx);
|
||||||
|
_window = window;
|
||||||
|
volk_free(taps);
|
||||||
|
tapCount = window->getTapCount();
|
||||||
|
taps = (float*)volk_malloc(tapCount * sizeof(float), volk_get_alignment());
|
||||||
|
bufStart = &buffer[tapCount];
|
||||||
|
window->createTaps(taps, tapCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
int count = _in->read();
|
||||||
|
if (count < 0) { return -1; }
|
||||||
|
|
||||||
|
generic_block<HalfDecimator<T>>::ctrlMtx.lock();
|
||||||
|
|
||||||
|
memcpy(bufStart, _in->readBuf, count * sizeof(T));
|
||||||
|
_in->flush();
|
||||||
|
|
||||||
|
int inIndex = _inIndex;
|
||||||
|
int outIndex = 0;
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
while (inIndex < count) {
|
||||||
|
volk_32f_x2_dot_prod_32f((float*)&out.writeBuf[outIndex], (float*)&buffer[inIndex+1], taps, tapCount);
|
||||||
|
inIndex += 2;
|
||||||
|
outIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<T, complex_t>) {
|
||||||
|
while (inIndex < count) {
|
||||||
|
volk_32fc_32f_dot_prod_32fc((lv_32fc_t*)&out.writeBuf[outIndex], (lv_32fc_t*)&buffer[inIndex+1], taps, tapCount);
|
||||||
|
inIndex += 2;
|
||||||
|
outIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_inIndex = inIndex - count;
|
||||||
|
|
||||||
|
if (!out.swap(outIndex)) { return -1; }
|
||||||
|
|
||||||
|
memmove(buffer, &buffer[count], tapCount * sizeof(T));
|
||||||
|
|
||||||
|
generic_block<HalfDecimator<T>>::ctrlMtx.unlock();
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream<T> out;
|
||||||
|
|
||||||
|
private:
|
||||||
|
stream<T>* _in;
|
||||||
|
|
||||||
|
dsp::filter_window::generic_window* _window;
|
||||||
|
|
||||||
|
T* bufStart;
|
||||||
|
T* buffer;
|
||||||
|
int tapCount;
|
||||||
|
float* taps;
|
||||||
|
int _inIndex = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
@ -11,6 +11,7 @@ namespace sourecmenu {
|
|||||||
int sourceId = 0;
|
int sourceId = 0;
|
||||||
double customOffset = 0.0;
|
double customOffset = 0.0;
|
||||||
double effectiveOffset = 0.0;
|
double effectiveOffset = 0.0;
|
||||||
|
int decimationPower = 0;
|
||||||
|
|
||||||
EventHandler<std::string> sourceRegisteredHandler;
|
EventHandler<std::string> sourceRegisteredHandler;
|
||||||
EventHandler<std::string> sourceUnregisterHandler;
|
EventHandler<std::string> sourceUnregisterHandler;
|
||||||
@ -39,6 +40,14 @@ namespace sourecmenu {
|
|||||||
"Ku LNB (9750MHz)\0"
|
"Ku LNB (9750MHz)\0"
|
||||||
"Ku LNB (10700MHz)\0";
|
"Ku LNB (10700MHz)\0";
|
||||||
|
|
||||||
|
const char* decimationStages = "None\0"
|
||||||
|
"2\0"
|
||||||
|
"4\0"
|
||||||
|
"8\0"
|
||||||
|
"16\0"
|
||||||
|
"32\0"
|
||||||
|
"64\0";
|
||||||
|
|
||||||
void updateOffset() {
|
void updateOffset() {
|
||||||
if (offsetMode == OFFSET_MODE_CUSTOM) { effectiveOffset = customOffset; }
|
if (offsetMode == OFFSET_MODE_CUSTOM) { effectiveOffset = customOffset; }
|
||||||
else if (offsetMode == OFFSET_MODE_SPYVERTER) { effectiveOffset = 120000000; } // 120MHz Up-conversion
|
else if (offsetMode == OFFSET_MODE_SPYVERTER) { effectiveOffset = 120000000; } // 120MHz Up-conversion
|
||||||
@ -113,10 +122,12 @@ namespace sourecmenu {
|
|||||||
std::string selected = core::configManager.conf["source"];
|
std::string selected = core::configManager.conf["source"];
|
||||||
customOffset = core::configManager.conf["offset"];
|
customOffset = core::configManager.conf["offset"];
|
||||||
offsetMode = core::configManager.conf["offsetMode"];
|
offsetMode = core::configManager.conf["offsetMode"];
|
||||||
|
decimationPower = core::configManager.conf["decimationPower"];
|
||||||
updateOffset();
|
updateOffset();
|
||||||
|
|
||||||
refreshSources();
|
refreshSources();
|
||||||
selectSource(selected);
|
selectSource(selected);
|
||||||
|
sigpath::signalPath.setDecimation(decimationPower);
|
||||||
|
|
||||||
sourceRegisteredHandler.handler = onSourceRegistered;
|
sourceRegisteredHandler.handler = onSourceRegistered;
|
||||||
sourceUnregisterHandler.handler = onSourceUnregister;
|
sourceUnregisterHandler.handler = onSourceUnregister;
|
||||||
@ -130,8 +141,9 @@ namespace sourecmenu {
|
|||||||
|
|
||||||
void draw(void* ctx) {
|
void draw(void* ctx) {
|
||||||
float itemWidth = ImGui::GetContentRegionAvailWidth();
|
float itemWidth = ImGui::GetContentRegionAvailWidth();
|
||||||
|
bool running = gui::mainWindow.sdrIsRunning();
|
||||||
|
|
||||||
if (gui::mainWindow.sdrIsRunning()) { style::beginDisabled(); }
|
if (running) { style::beginDisabled(); }
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(itemWidth);
|
ImGui::SetNextItemWidth(itemWidth);
|
||||||
if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
|
if (ImGui::Combo("##source", &sourceId, sourceNamesTxt.c_str())) {
|
||||||
@ -141,7 +153,7 @@ namespace sourecmenu {
|
|||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gui::mainWindow.sdrIsRunning()) { style::endDisabled(); }
|
if (running) { style::endDisabled(); }
|
||||||
|
|
||||||
sigpath::sourceManager.showSelectedMenu();
|
sigpath::sourceManager.showSelectedMenu();
|
||||||
|
|
||||||
@ -171,5 +183,17 @@ namespace sourecmenu {
|
|||||||
ImGui::InputDouble("##freq_offset", &effectiveOffset, 1.0, 100.0);
|
ImGui::InputDouble("##freq_offset", &effectiveOffset, 1.0, 100.0);
|
||||||
style::endDisabled();
|
style::endDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (running) { style::beginDisabled(); }
|
||||||
|
ImGui::Text("Decimation");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(itemWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::Combo("##source_decim", &decimationPower, decimationStages)) {
|
||||||
|
sigpath::signalPath.setDecimation(decimationPower);
|
||||||
|
core::configManager.acquire();
|
||||||
|
core::configManager.conf["decimationPower"] = decimationPower;
|
||||||
|
core::configManager.release(true);
|
||||||
|
}
|
||||||
|
if (running) { style::endDisabled(); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <signal_path/dsp.h>
|
#include <signal_path/dsp.h>
|
||||||
|
#include <core.h>
|
||||||
|
|
||||||
SignalPath::SignalPath() {
|
SignalPath::SignalPath() {
|
||||||
|
|
||||||
@ -6,10 +7,13 @@ SignalPath::SignalPath() {
|
|||||||
|
|
||||||
void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream<dsp::complex_t>* input, dsp::complex_t* fftBuffer, void fftHandler(dsp::complex_t*,int,void*), void* fftHandlerCtx) {
|
void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream<dsp::complex_t>* input, dsp::complex_t* fftBuffer, void fftHandler(dsp::complex_t*,int,void*), void* fftHandlerCtx) {
|
||||||
this->sampleRate = sampleRate;
|
this->sampleRate = sampleRate;
|
||||||
|
this->sourceSampleRate = sampleRate;
|
||||||
this->fftRate = fftRate;
|
this->fftRate = fftRate;
|
||||||
this->fftSize = fftSize;
|
this->fftSize = fftSize;
|
||||||
inputBlockSize = sampleRate / 200.0f;
|
inputBlockSize = sampleRate / 200.0f;
|
||||||
|
|
||||||
|
halfBandWindow.init(1000000, 200000, 4000000);
|
||||||
|
|
||||||
// split.init(input);
|
// split.init(input);
|
||||||
inputBuffer.init(input);
|
inputBuffer.init(input);
|
||||||
split.init(&inputBuffer.out);
|
split.init(&inputBuffer.out);
|
||||||
@ -47,17 +51,25 @@ double SignalPath::getSampleRate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SignalPath::start() {
|
void SignalPath::start() {
|
||||||
|
for (auto& decimator : decimators) {
|
||||||
|
decimator->start();
|
||||||
|
}
|
||||||
inputBuffer.start();
|
inputBuffer.start();
|
||||||
split.start();
|
split.start();
|
||||||
reshape.start();
|
reshape.start();
|
||||||
fftHandlerSink.start();
|
fftHandlerSink.start();
|
||||||
|
running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalPath::stop() {
|
void SignalPath::stop() {
|
||||||
|
for (auto& decimator : decimators) {
|
||||||
|
decimator->stop();
|
||||||
|
}
|
||||||
inputBuffer.stop();
|
inputBuffer.stop();
|
||||||
split.stop();
|
split.stop();
|
||||||
reshape.stop();
|
reshape.stop();
|
||||||
fftHandlerSink.stop();
|
fftHandlerSink.stop();
|
||||||
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
dsp::VFO* SignalPath::addVFO(std::string name, double outSampleRate, double bandwidth, double offset) {
|
dsp::VFO* SignalPath::addVFO(std::string name, double outSampleRate, double bandwidth, double offset) {
|
||||||
@ -87,7 +99,6 @@ void SignalPath::removeVFO(std::string name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SignalPath::setInput(dsp::stream<dsp::complex_t>* input) {
|
void SignalPath::setInput(dsp::stream<dsp::complex_t>* input) {
|
||||||
// split.setInput(input);
|
|
||||||
inputBuffer.setInput(input);
|
inputBuffer.setInput(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,3 +132,37 @@ void SignalPath::stopFFT() {
|
|||||||
void SignalPath::setBuffering(bool enabled) {
|
void SignalPath::setBuffering(bool enabled) {
|
||||||
inputBuffer.bypass = !enabled;
|
inputBuffer.bypass = !enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SignalPath::setDecimation(int dec) {
|
||||||
|
decimation = dec;
|
||||||
|
|
||||||
|
// Stop existing decimators
|
||||||
|
if (!decimators.empty()) {
|
||||||
|
for (auto& decimator : decimators) {
|
||||||
|
decimator->stop();
|
||||||
|
delete decimator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
decimators.clear();
|
||||||
|
|
||||||
|
// If no decimation, reconnect
|
||||||
|
if (!dec) {
|
||||||
|
split.setInput(&inputBuffer.out);
|
||||||
|
core::setInputSampleRate(sourceSampleRate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new decimators
|
||||||
|
if (running) { split.stop(); }
|
||||||
|
for (int i = 0; i < dec; i++) {
|
||||||
|
dsp::HalfDecimator<dsp::complex_t>* decimator = new dsp::HalfDecimator<dsp::complex_t>((i == 0) ? &inputBuffer.out : &decimators[i-1]->out, &halfBandWindow);
|
||||||
|
if (running) { decimator->start(); }
|
||||||
|
// TODO: ONLY start if running
|
||||||
|
decimators.push_back(decimator);
|
||||||
|
}
|
||||||
|
split.setInput(&decimators[decimators.size()-1]->out);
|
||||||
|
if (running) { split.start(); }
|
||||||
|
|
||||||
|
// Update the DSP sample rate
|
||||||
|
core::setInputSampleRate(sourceSampleRate);
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
#include <dsp/vfo.h>
|
#include <dsp/vfo.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <dsp/sink.h>
|
#include <dsp/sink.h>
|
||||||
|
#include <dsp/decimation.h>
|
||||||
|
|
||||||
class SignalPath {
|
class SignalPath {
|
||||||
public:
|
public:
|
||||||
@ -21,8 +22,11 @@ public:
|
|||||||
void startFFT();
|
void startFFT();
|
||||||
void stopFFT();
|
void stopFFT();
|
||||||
void setBuffering(bool enabled);
|
void setBuffering(bool enabled);
|
||||||
|
void setDecimation(int dec);
|
||||||
|
|
||||||
dsp::SampleFrameBuffer<dsp::complex_t> inputBuffer;
|
dsp::SampleFrameBuffer<dsp::complex_t> inputBuffer;
|
||||||
|
double sourceSampleRate = 0;
|
||||||
|
int decimation = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct VFO_t {
|
struct VFO_t {
|
||||||
@ -39,10 +43,13 @@ private:
|
|||||||
|
|
||||||
// VFO
|
// VFO
|
||||||
std::map<std::string, VFO_t> vfos;
|
std::map<std::string, VFO_t> vfos;
|
||||||
|
std::vector<dsp::HalfDecimator<dsp::complex_t>*> decimators;
|
||||||
|
dsp::filter_window::BlackmanWindow halfBandWindow;
|
||||||
|
|
||||||
double sampleRate;
|
double sampleRate;
|
||||||
double fftRate;
|
double fftRate;
|
||||||
int fftSize;
|
int fftSize;
|
||||||
int inputBlockSize;
|
int inputBlockSize;
|
||||||
bool bufferingEnabled = false;
|
bool bufferingEnabled = false;
|
||||||
|
bool running = false;
|
||||||
};
|
};
|
Loading…
Reference in New Issue
Block a user