Fixed waterfall at very low samplerates

This commit is contained in:
Ryzerth 2021-07-31 16:36:04 +02:00
parent c13eb950b2
commit ee5b89c4aa
5 changed files with 86 additions and 48 deletions

View File

@ -38,9 +38,6 @@ void MainWindow::init() {
gui::waterfall.init(); gui::waterfall.init();
gui::waterfall.setRawFFTSize(fftSize); gui::waterfall.setRawFFTSize(fftSize);
appliedWindow = new float[fftSize];
generateFFTWindow(selectedWindow, fftSize);
credits::init(); credits::init();
core::configManager.acquire(); core::configManager.acquire();
@ -215,10 +212,14 @@ void MainWindow::init() {
void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) { void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) {
MainWindow* _this = (MainWindow*)ctx; MainWindow* _this = (MainWindow*)ctx;
std::lock_guard<std::mutex> lck(_this->fft_mtx); std::lock_guard<std::mutex> lck(_this->fft_mtx);
if (count != _this->fftSize) { return; }
// Apply window // Apply window
volk_32fc_32f_multiply_32fc((lv_32fc_t*)_this->fft_in, (lv_32fc_t*)samples, _this->appliedWindow, count); volk_32fc_32f_multiply_32fc((lv_32fc_t*)_this->fft_in, (lv_32fc_t*)samples, sigpath::signalPath.fftTaps, count);
// Zero out the rest of the samples
if (count < _this->fftSize) {
memset(&_this->fft_in[count], 0, (_this->fftSize-count) * sizeof(dsp::complex_t));
}
// Execute FFT // Execute FFT
fftwf_execute(_this->fftwPlan); fftwf_execute(_this->fftwPlan);
@ -231,7 +232,7 @@ void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) {
} }
// Take power of spectrum // Take power of spectrum
volk_32fc_s32f_power_spectrum_32f(fftBuf, (lv_32fc_t*)_this->fft_out, count, count); volk_32fc_s32f_power_spectrum_32f(fftBuf, (lv_32fc_t*)_this->fft_out, _this->fftSize, _this->fftSize);
// Push back data // Push back data
gui::waterfall.pushFFT(); gui::waterfall.pushFFT();
@ -663,30 +664,11 @@ void MainWindow::setFFTSize(int size) {
fft_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); fft_in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize);
fft_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize); fft_out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fftSize);
fftwPlan = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE); fftwPlan = fftwf_plan_dft_1d(fftSize, fft_in, fft_out, FFTW_FORWARD, FFTW_ESTIMATE);
delete appliedWindow;
appliedWindow = new float[fftSize];
generateFFTWindow(selectedWindow, fftSize);
} }
void MainWindow::setFFTWindow(int win) { void MainWindow::setFFTWindow(int win) {
std::lock_guard<std::mutex> lck(fft_mtx); std::lock_guard<std::mutex> lck(fft_mtx);
selectedWindow = win; sigpath::signalPath.setFFTWindow(win);
generateFFTWindow(selectedWindow, fftSize);
}
void MainWindow::generateFFTWindow(int win, int size) {
if (win == FFT_WINDOW_RECTANGULAR) {
for (int i = 0; i < size; i++) {
appliedWindow[i] = (i%2) ? 1 : -1;
}
}
else if (win == FFT_WINDOW_BLACKMAN) {
for (int i = 0; i < size; i++) {
appliedWindow[i] = ((i%2) ? dsp::window_function::blackman(i, size) : -dsp::window_function::blackman(i, size))*2;
}
}
} }
bool MainWindow::isPlaying() { bool MainWindow::isPlaying() {

View File

@ -11,12 +11,6 @@
#define WINDOW_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground #define WINDOW_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground
enum {
FFT_WINDOW_RECTANGULAR,
FFT_WINDOW_BLACKMAN,
_FFT_WINDOW_COUNT
};
class MainWindow { class MainWindow {
public: public:
void init(); void init();
@ -37,7 +31,6 @@ public:
Event<bool> onPlayStateChange; Event<bool> onPlayStateChange;
private: private:
void generateFFTWindow(int win, int size);
static void fftHandler(dsp::complex_t* samples, int count, void* ctx); static void fftHandler(dsp::complex_t* samples, int count, void* ctx);
static void vfoAddedHandler(VFOManager::VFO* vfo, void* ctx); static void vfoAddedHandler(VFOManager::VFO* vfo, void* ctx);
@ -46,7 +39,6 @@ private:
std::mutex fft_mtx; std::mutex fft_mtx;
fftwf_complex *fft_in, *fft_out; fftwf_complex *fft_in, *fft_out;
fftwf_plan fftwPlan; fftwf_plan fftwPlan;
float* appliedWindow;
// GUI Variables // GUI Variables
bool firstMenuRender = true; bool firstMenuRender = true;

View File

@ -14,12 +14,22 @@ void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream
halfBandWindow.init(1000000, 200000, 4000000); halfBandWindow.init(1000000, 200000, 4000000);
// split.init(input);
inputBuffer.init(input); inputBuffer.init(input);
corrector.init(&inputBuffer.out, 50.0f / sampleRate); corrector.init(&inputBuffer.out, 50.0f / sampleRate);
split.init(&inputBuffer.out); split.init(&inputBuffer.out);
reshape.init(&fftStream, fftSize, (sampleRate / fftRate) - fftSize); // Allocate the fft taps
fftTaps = new float[fftSize];
// Calculate the parameters for the reshaper
int fftInterval = sampleRate / fftRate;
fftOutputSampleCount = std::min<int>(fftInterval, fftSize);
int fftSkip = fftInterval - fftOutputSampleCount;
// Generate FFT Windows
generateFFTWindow(fftWindow, fftTaps, fftOutputSampleCount);
reshape.init(&fftStream, fftSize, fftSkip);
split.bindStream(&fftStream); split.bindStream(&fftStream);
fftHandlerSink.init(&reshape.out, fftHandler, fftHandlerCtx); fftHandlerSink.init(&reshape.out, fftHandler, fftHandlerCtx);
} }
@ -27,26 +37,29 @@ void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream
void SignalPath::setSampleRate(double sampleRate) { void SignalPath::setSampleRate(double sampleRate) {
this->sampleRate = sampleRate; this->sampleRate = sampleRate;
// Stop the splitter
split.stop(); split.stop();
reshape.stop();
// Stop all VFOs
for (auto const& [name, vfo] : vfos) { for (auto const& [name, vfo] : vfos) {
vfo.vfo->stop(); vfo.vfo->stop();
} }
// Claculate skip to maintain a constant fft rate updateFFTDSP();
int skip = (sampleRate / fftRate) - fftSize;
reshape.setSkip(skip);
// TODO: Tell modules that the block size has changed (maybe?)
// Update the sample rate for all VFOs and start them up
for (auto const& [name, vfo] : vfos) { for (auto const& [name, vfo] : vfos) {
vfo.vfo->setInSampleRate(sampleRate); vfo.vfo->setInSampleRate(sampleRate);
vfo.vfo->start(); vfo.vfo->start();
} }
// Update correction rate on the IQ corrector
corrector.setCorrectionRate(50.0f / sampleRate); corrector.setCorrectionRate(50.0f / sampleRate);
// Start the splitter
split.start(); split.start();
reshape.start();
} }
double SignalPath::getSampleRate() { double SignalPath::getSampleRate() {
@ -117,19 +130,15 @@ void SignalPath::unbindIQStream(dsp::stream<dsp::complex_t>* stream) {
void SignalPath::setFFTSize(int size) { void SignalPath::setFFTSize(int size) {
fftSize = size; fftSize = size;
int skip = (sampleRate / fftRate) - fftSize;
reshape.stop(); reshape.stop();
reshape.setSkip(skip); updateFFTDSP();
reshape.setKeep(fftSize);
reshape.start(); reshape.start();
} }
void SignalPath::setFFTRate(double rate) { void SignalPath::setFFTRate(double rate) {
fftRate = rate; fftRate = rate;
int skip = (sampleRate / fftRate) - fftSize;
reshape.stop(); reshape.stop();
reshape.setSkip(skip); updateFFTDSP();
reshape.setKeep(fftSize);
reshape.start(); reshape.start();
} }
@ -227,3 +236,44 @@ void SignalPath::setIQCorrection(bool enabled) {
corrector.offset.im = 0; corrector.offset.im = 0;
} }
} }
void SignalPath::setFFTWindow(int win) {
fftWindow = win;
reshape.stop();
updateFFTDSP();
reshape.start();
}
void SignalPath::generateFFTWindow(int win, float* taps, int size) {
if (win == FFT_WINDOW_RECTANGULAR) {
for (int i = 0; i < size; i++) {
taps[i] = (i%2) ? 1 : -1;
}
}
else if (win == FFT_WINDOW_BLACKMAN) {
for (int i = 0; i < size; i++) {
taps[i] = ((i%2) ? dsp::window_function::blackman(i, size) : -dsp::window_function::blackman(i, size))*2;
}
}
}
void SignalPath::updateFFTDSP() {
// Allocate the fft taps
if (fftTaps != NULL) { delete[] fftTaps; }
fftTaps = new float[fftSize];
// Calculate the parameters for the reshaper
int fftInterval = sampleRate / fftRate;
fftOutputSampleCount = std::min<int>(fftInterval, fftSize);
int fftSkip = fftInterval - fftOutputSampleCount;
// Generate FFT Windows
generateFFTWindow(fftWindow, fftTaps, fftOutputSampleCount);
// Update parameters of the reshaper
reshape.setKeep(fftOutputSampleCount);
reshape.setSkip(fftSkip);
spdlog::info("Updating FFT DSP settings: Keep: {0}, Skip: {1}", fftOutputSampleCount, fftSkip);
}

View File

@ -6,6 +6,12 @@
#include <dsp/decimation.h> #include <dsp/decimation.h>
#include <dsp/correction.h> #include <dsp/correction.h>
enum {
FFT_WINDOW_RECTANGULAR,
FFT_WINDOW_BLACKMAN,
_FFT_WINDOW_COUNT
};
class SignalPath { class SignalPath {
public: public:
SignalPath(); SignalPath();
@ -26,12 +32,18 @@ public:
void setBuffering(bool enabled); void setBuffering(bool enabled);
void setDecimation(int dec); void setDecimation(int dec);
void setIQCorrection(bool enabled); void setIQCorrection(bool enabled);
void setFFTWindow(int win);
dsp::SampleFrameBuffer<dsp::complex_t> inputBuffer; dsp::SampleFrameBuffer<dsp::complex_t> inputBuffer;
double sourceSampleRate = 0; double sourceSampleRate = 0;
int decimation = 0; int decimation = 0;
float* fftTaps = NULL;
private: private:
void generateFFTWindow(int win, float* taps, int size);
void updateFFTDSP();
struct VFO_t { struct VFO_t {
dsp::stream<dsp::complex_t>* inputStream; dsp::stream<dsp::complex_t>* inputStream;
dsp::VFO* vfo; dsp::VFO* vfo;
@ -50,10 +62,12 @@ private:
std::vector<dsp::HalfDecimator<dsp::complex_t>*> decimators; std::vector<dsp::HalfDecimator<dsp::complex_t>*> decimators;
dsp::filter_window::BlackmanWindow halfBandWindow; dsp::filter_window::BlackmanWindow halfBandWindow;
int fftOutputSampleCount = 0;
double sampleRate; double sampleRate;
double fftRate; double fftRate;
int fftSize; int fftSize;
int inputBlockSize; int inputBlockSize;
int fftWindow = FFT_WINDOW_RECTANGULAR;
bool bufferingEnabled = false; bool bufferingEnabled = false;
bool running = false; bool running = false;
bool iqCorrection = false; bool iqCorrection = false;

View File

@ -1,3 +1,3 @@
#pragma once #pragma once
#define VERSION_STR "1.0.0_rc4" #define VERSION_STR "1.0.0_rc5"