From ee5b89c4aa04654969dd09a22f480d66a8c5941e Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Sat, 31 Jul 2021 16:36:04 +0200 Subject: [PATCH] Fixed waterfall at very low samplerates --- core/src/gui/main_window.cpp | 34 ++++------------ core/src/gui/main_window.h | 8 ---- core/src/signal_path/dsp.cpp | 76 ++++++++++++++++++++++++++++++------ core/src/signal_path/dsp.h | 14 +++++++ core/src/version.h | 2 +- 5 files changed, 86 insertions(+), 48 deletions(-) diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 75d9fd7..29cf044 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -38,9 +38,6 @@ void MainWindow::init() { gui::waterfall.init(); gui::waterfall.setRawFFTSize(fftSize); - appliedWindow = new float[fftSize]; - generateFFTWindow(selectedWindow, fftSize); - credits::init(); core::configManager.acquire(); @@ -215,10 +212,14 @@ void MainWindow::init() { void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) { MainWindow* _this = (MainWindow*)ctx; std::lock_guard lck(_this->fft_mtx); - if (count != _this->fftSize) { return; } // 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 fftwf_execute(_this->fftwPlan); @@ -231,7 +232,7 @@ void MainWindow::fftHandler(dsp::complex_t* samples, int count, void* ctx) { } // 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 gui::waterfall.pushFFT(); @@ -663,30 +664,11 @@ void MainWindow::setFFTSize(int size) { fft_in = (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); - - delete appliedWindow; - - appliedWindow = new float[fftSize]; - generateFFTWindow(selectedWindow, fftSize); } void MainWindow::setFFTWindow(int win) { std::lock_guard lck(fft_mtx); - selectedWindow = 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; - } - } + sigpath::signalPath.setFFTWindow(win); } bool MainWindow::isPlaying() { diff --git a/core/src/gui/main_window.h b/core/src/gui/main_window.h index a04f936..b2413cb 100644 --- a/core/src/gui/main_window.h +++ b/core/src/gui/main_window.h @@ -11,12 +11,6 @@ #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 { public: void init(); @@ -37,7 +31,6 @@ public: Event onPlayStateChange; private: - void generateFFTWindow(int win, int size); static void fftHandler(dsp::complex_t* samples, int count, void* ctx); static void vfoAddedHandler(VFOManager::VFO* vfo, void* ctx); @@ -46,7 +39,6 @@ private: std::mutex fft_mtx; fftwf_complex *fft_in, *fft_out; fftwf_plan fftwPlan; - float* appliedWindow; // GUI Variables bool firstMenuRender = true; diff --git a/core/src/signal_path/dsp.cpp b/core/src/signal_path/dsp.cpp index 269a4f0..7cf9cb9 100644 --- a/core/src/signal_path/dsp.cpp +++ b/core/src/signal_path/dsp.cpp @@ -14,12 +14,22 @@ void SignalPath::init(uint64_t sampleRate, int fftRate, int fftSize, dsp::stream halfBandWindow.init(1000000, 200000, 4000000); - // split.init(input); inputBuffer.init(input); corrector.init(&inputBuffer.out, 50.0f / sampleRate); 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(fftInterval, fftSize); + int fftSkip = fftInterval - fftOutputSampleCount; + + // Generate FFT Windows + generateFFTWindow(fftWindow, fftTaps, fftOutputSampleCount); + + reshape.init(&fftStream, fftSize, fftSkip); split.bindStream(&fftStream); 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) { this->sampleRate = sampleRate; + // Stop the splitter split.stop(); + reshape.stop(); + // Stop all VFOs for (auto const& [name, vfo] : vfos) { vfo.vfo->stop(); } - // Claculate skip to maintain a constant fft rate - int skip = (sampleRate / fftRate) - fftSize; - reshape.setSkip(skip); - - // TODO: Tell modules that the block size has changed (maybe?) + updateFFTDSP(); + // Update the sample rate for all VFOs and start them up for (auto const& [name, vfo] : vfos) { vfo.vfo->setInSampleRate(sampleRate); vfo.vfo->start(); } + // Update correction rate on the IQ corrector corrector.setCorrectionRate(50.0f / sampleRate); + // Start the splitter split.start(); + reshape.start(); } double SignalPath::getSampleRate() { @@ -117,19 +130,15 @@ void SignalPath::unbindIQStream(dsp::stream* stream) { void SignalPath::setFFTSize(int size) { fftSize = size; - int skip = (sampleRate / fftRate) - fftSize; reshape.stop(); - reshape.setSkip(skip); - reshape.setKeep(fftSize); + updateFFTDSP(); reshape.start(); } void SignalPath::setFFTRate(double rate) { fftRate = rate; - int skip = (sampleRate / fftRate) - fftSize; reshape.stop(); - reshape.setSkip(skip); - reshape.setKeep(fftSize); + updateFFTDSP(); reshape.start(); } @@ -226,4 +235,45 @@ void SignalPath::setIQCorrection(bool enabled) { corrector.offset.re = 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(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); } \ No newline at end of file diff --git a/core/src/signal_path/dsp.h b/core/src/signal_path/dsp.h index 5f12221..59f1f02 100644 --- a/core/src/signal_path/dsp.h +++ b/core/src/signal_path/dsp.h @@ -6,6 +6,12 @@ #include #include +enum { + FFT_WINDOW_RECTANGULAR, + FFT_WINDOW_BLACKMAN, + _FFT_WINDOW_COUNT +}; + class SignalPath { public: SignalPath(); @@ -26,12 +32,18 @@ public: void setBuffering(bool enabled); void setDecimation(int dec); void setIQCorrection(bool enabled); + void setFFTWindow(int win); dsp::SampleFrameBuffer inputBuffer; double sourceSampleRate = 0; int decimation = 0; + float* fftTaps = NULL; + private: + void generateFFTWindow(int win, float* taps, int size); + void updateFFTDSP(); + struct VFO_t { dsp::stream* inputStream; dsp::VFO* vfo; @@ -50,10 +62,12 @@ private: std::vector*> decimators; dsp::filter_window::BlackmanWindow halfBandWindow; + int fftOutputSampleCount = 0; double sampleRate; double fftRate; int fftSize; int inputBlockSize; + int fftWindow = FFT_WINDOW_RECTANGULAR; bool bufferingEnabled = false; bool running = false; bool iqCorrection = false; diff --git a/core/src/version.h b/core/src/version.h index 7d6b458..d522347 100644 --- a/core/src/version.h +++ b/core/src/version.h @@ -1,3 +1,3 @@ #pragma once -#define VERSION_STR "1.0.0_rc4" \ No newline at end of file +#define VERSION_STR "1.0.0_rc5" \ No newline at end of file