From 56e49baa3b4eadf2e274b44a8cdfae88bfef1acf Mon Sep 17 00:00:00 2001 From: f4exb Date: Sun, 1 Jul 2018 02:16:59 +0200 Subject: [PATCH] Spectrum averaging: fixed average (1) --- plugins/channelrx/demodbfm/bfmdemodgui.cpp | 2 +- plugins/channelrx/udpsrc/udpsrcgui.cpp | 2 +- plugins/channeltx/udpsink/udpsinkgui.cpp | 2 +- sdrbase/util/fixedaverage2d.h | 121 ++++++++++++++++++ sdrgui/dsp/spectrumvis.cpp | 136 +++++++++++++++------ sdrgui/dsp/spectrumvis.h | 32 ++++- sdrgui/gui/glspectrumgui.cpp | 39 +++++- 7 files changed, 287 insertions(+), 47 deletions(-) create mode 100644 sdrbase/util/fixedaverage2d.h diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index cda2d6889..87dfc9ac8 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -359,7 +359,7 @@ BFMDemodGUI::BFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban ui->glSpectrum->setDisplayWaterfall(false); ui->glSpectrum->setDisplayMaxHold(false); ui->glSpectrum->setSsbSpectrum(true); - m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), 64, 10, 0, FFTWindow::BlackmanHarris); + m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), 64, 10, 0, 0, FFTWindow::BlackmanHarris); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); m_channelMarker.blockSignals(true); diff --git a/plugins/channelrx/udpsrc/udpsrcgui.cpp b/plugins/channelrx/udpsrc/udpsrcgui.cpp index 66826ec56..2a645c287 100644 --- a/plugins/channelrx/udpsrc/udpsrcgui.cpp +++ b/plugins/channelrx/udpsrc/udpsrcgui.cpp @@ -187,7 +187,7 @@ UDPSrcGUI::UDPSrcGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam ui->glSpectrum->setSampleRate(ui->sampleRate->text().toInt()); ui->glSpectrum->setDisplayWaterfall(true); ui->glSpectrum->setDisplayMaxHold(true); - m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), 64, 10, 0, FFTWindow::BlackmanHarris); + m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), 64, 10, 0, 0, FFTWindow::BlackmanHarris); ui->glSpectrum->connectTimer(MainWindow::getInstance()->getMasterTimer()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); diff --git a/plugins/channeltx/udpsink/udpsinkgui.cpp b/plugins/channeltx/udpsink/udpsinkgui.cpp index 4e40e2cce..88e33c399 100644 --- a/plugins/channeltx/udpsink/udpsinkgui.cpp +++ b/plugins/channeltx/udpsink/udpsinkgui.cpp @@ -142,7 +142,7 @@ UDPSinkGUI::UDPSinkGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS ui->glSpectrum->setSampleRate(ui->sampleRate->text().toInt()); ui->glSpectrum->setDisplayWaterfall(true); ui->glSpectrum->setDisplayMaxHold(true); - m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), 64, 10, 0, FFTWindow::BlackmanHarris); + m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), 64, 10, 0, 0, FFTWindow::BlackmanHarris); ui->glSpectrum->connectTimer(MainWindow::getInstance()->getMasterTimer()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); diff --git a/sdrbase/util/fixedaverage2d.h b/sdrbase/util/fixedaverage2d.h new file mode 100644 index 000000000..a4735889b --- /dev/null +++ b/sdrbase/util/fixedaverage2d.h @@ -0,0 +1,121 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2018 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_UTIL_FIXEDAVERAGE2D_H_ +#define SDRBASE_UTIL_FIXEDAVERAGE2D_H_ + +#include + +template +class FixedAverage2D +{ +public: + FixedAverage2D() : m_sum(0), m_sumSize(0), m_width(0), m_size(0), m_avgIndex(0) {} + + ~FixedAverage2D() + { + if (m_sum) { + delete[] m_sum; + } + } + + void resize(unsigned int width, unsigned int size) + { + if (width > m_sumSize) + { + m_sumSize = width; + if (m_sum) { + delete[] m_sum; + } + m_sum = new T[m_sumSize]; + } + + m_width = width; + m_size = size; + + std::fill(m_sum, m_sum+m_width, 0); + m_avgIndex = 0; + } + + bool storeAndGetAvg(T& avg, T v, unsigned int index) + { + if (m_size <= 1) + { + avg = v; + return true; + } + + m_sum[index] += v; + + if (m_avgIndex == m_size - 1) + { + avg = m_sum[index]/m_size; + return true; + } + else + { + return false; + } + } + + bool storeAndGetSum(T& sum, T v, unsigned int index) + { + if (m_size <= 1) + { + sum = v; + return true; + } + + m_sum[index] += v; + + if (m_avgIndex < m_size - 1) + { + sum = m_sum[index]; + return true; + } + else + { + return false; + } + } + + bool nextAverage() + { + if (m_avgIndex == m_size - 1) + { + m_avgIndex = 0; + std::fill(m_sum, m_sum+m_width, 0); + return true; + } + else + { + m_avgIndex++; + return false; + } + } + +private: + T *m_sum; + unsigned int m_sumSize; + unsigned int m_width; + unsigned int m_size; + unsigned int m_avgIndex; +}; + + + +#endif /* SDRBASE_UTIL_FIXEDAVERAGE2D_H_ */ diff --git a/sdrgui/dsp/spectrumvis.cpp b/sdrgui/dsp/spectrumvis.cpp index 27eb7a5c0..f3d30aef7 100644 --- a/sdrgui/dsp/spectrumvis.cpp +++ b/sdrgui/dsp/spectrumvis.cpp @@ -26,11 +26,12 @@ SpectrumVis::SpectrumVis(Real scalef, GLSpectrum* glSpectrum) : m_scalef(scalef), m_glSpectrum(glSpectrum), m_averageNb(0), + m_averagingMode(AvgModeMoving), m_ofs(0), m_mutex(QMutex::Recursive) { setObjectName("SpectrumVis"); - handleConfigure(1024, 0, 0, FFTWindow::BlackmanHarris); + handleConfigure(1024, 0, 0, AvgModeMoving, FFTWindow::BlackmanHarris); } SpectrumVis::~SpectrumVis() @@ -38,9 +39,14 @@ SpectrumVis::~SpectrumVis() delete m_fft; } -void SpectrumVis::configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, unsigned int averagingNb, FFTWindow::Function window) +void SpectrumVis::configure(MessageQueue* msgQueue, + int fftSize, + int overlapPercent, + unsigned int averagingNb, + int averagingMode, + FFTWindow::Function window) { - MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(fftSize, overlapPercent, averagingNb, window); + MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(fftSize, overlapPercent, averagingNb, averagingMode, window); msgQueue->push(cmd); } @@ -105,40 +111,92 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV Real v; std::size_t halfSize = m_fftSize / 2; - if ( positiveOnly ) + if (m_averagingMode == AvgModeMoving) { - for (std::size_t i = 0; i < halfSize; i++) - { - c = fftOut[i]; - v = c.real() * c.real() + c.imag() * c.imag(); - v = m_average.storeAndGetAvg(v, i); - v = m_mult * log2f(v) + m_ofs; - m_logPowerSpectrum[i * 2] = v; - m_logPowerSpectrum[i * 2 + 1] = v; - } + if ( positiveOnly ) + { + for (std::size_t i = 0; i < halfSize; i++) + { + c = fftOut[i]; + v = c.real() * c.real() + c.imag() * c.imag(); + v = m_movingAverage.storeAndGetAvg(v, i); + v = m_mult * log2f(v) + m_ofs; + m_logPowerSpectrum[i * 2] = v; + m_logPowerSpectrum[i * 2 + 1] = v; + } + } + else + { + for (std::size_t i = 0; i < halfSize; i++) + { + c = fftOut[i + halfSize]; + v = c.real() * c.real() + c.imag() * c.imag(); + v = m_movingAverage.storeAndGetAvg(v, i+halfSize); + v = m_mult * log2f(v) + m_ofs; + m_logPowerSpectrum[i] = v; + + c = fftOut[i]; + v = c.real() * c.real() + c.imag() * c.imag(); + v = m_movingAverage.storeAndGetAvg(v, i); + v = m_mult * log2f(v) + m_ofs; + m_logPowerSpectrum[i + halfSize] = v; + } + } + + // send new data to visualisation + m_glSpectrum->newSpectrum(m_logPowerSpectrum, m_fftSize); + m_movingAverage.nextAverage(); } - else + else if (m_averagingMode == AvgModeFixed) { - for (std::size_t i = 0; i < halfSize; i++) - { - c = fftOut[i + halfSize]; - v = c.real() * c.real() + c.imag() * c.imag(); - v = m_average.storeAndGetAvg(v, i+halfSize); - v = m_mult * log2f(v) + m_ofs; - m_logPowerSpectrum[i] = v; + double avg; - c = fftOut[i]; - v = c.real() * c.real() + c.imag() * c.imag(); - v = m_average.storeAndGetAvg(v, i); - v = m_mult * log2f(v) + m_ofs; - m_logPowerSpectrum[i + halfSize] = v; - } + if ( positiveOnly ) + { + for (std::size_t i = 0; i < halfSize; i++) + { + c = fftOut[i]; + v = c.real() * c.real() + c.imag() * c.imag(); + + if (m_fixedAverage.storeAndGetAvg(avg, v, i)) + { + avg = m_mult * log2f(avg) + m_ofs; + m_logPowerSpectrum[i * 2] = avg; + m_logPowerSpectrum[i * 2 + 1] = avg; + } + } + } + else + { + for (std::size_t i = 0; i < halfSize; i++) + { + c = fftOut[i + halfSize]; + v = c.real() * c.real() + c.imag() * c.imag(); + + if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize)) + { // result available + avg = m_mult * log2f(avg) + m_ofs; + m_logPowerSpectrum[i] = avg; + } + + c = fftOut[i]; + v = c.real() * c.real() + c.imag() * c.imag(); + + if (m_fixedAverage.storeAndGetAvg(avg, v, i)) + { // result available + avg = m_mult * log2f(avg) + m_ofs; + m_logPowerSpectrum[i + halfSize] = avg; + } + } + } + + if (m_fixedAverage.nextAverage()) + { // result available + // send new data to visualisation + m_glSpectrum->newSpectrum(m_logPowerSpectrum, m_fftSize); + } } - // send new data to visualisation - m_glSpectrum->newSpectrum(m_logPowerSpectrum, m_fftSize); - m_average.nextAverage(); - // advance buffer respecting the fft overlap factor std::copy(m_fftBuffer.begin() + m_refillSize, m_fftBuffer.end(), m_fftBuffer.begin()); @@ -173,7 +231,11 @@ bool SpectrumVis::handleMessage(const Message& message) if (MsgConfigureSpectrumVis::match(message)) { MsgConfigureSpectrumVis& conf = (MsgConfigureSpectrumVis&) message; - handleConfigure(conf.getFFTSize(), conf.getOverlapPercent(), conf.getAverageNb(), conf.getWindow()); + handleConfigure(conf.getFFTSize(), + conf.getOverlapPercent(), + conf.getAverageNb(), + conf.getAveragingMode(), + conf.getWindow()); return true; } else @@ -182,7 +244,11 @@ bool SpectrumVis::handleMessage(const Message& message) } } -void SpectrumVis::handleConfigure(int fftSize, int overlapPercent, unsigned int averageNb, FFTWindow::Function window) +void SpectrumVis::handleConfigure(int fftSize, + int overlapPercent, + unsigned int averageNb, + AveragingMode averagingMode, + FFTWindow::Function window) { QMutexLocker mutexLocker(&m_mutex); @@ -214,7 +280,9 @@ void SpectrumVis::handleConfigure(int fftSize, int overlapPercent, unsigned int m_overlapSize = (m_fftSize * m_overlapPercent) / 100; m_refillSize = m_fftSize - m_overlapSize; m_fftBufferFill = m_overlapSize; - m_average.resize(fftSize, averageNb); + m_movingAverage.resize(fftSize, averageNb); + m_fixedAverage.resize(fftSize, averageNb); m_averageNb = averageNb; + m_averagingMode = averagingMode; m_ofs = 20.0f * log10f(1.0f / m_fftSize); } diff --git a/sdrgui/dsp/spectrumvis.h b/sdrgui/dsp/spectrumvis.h index 8faa38647..e2b81c2ca 100644 --- a/sdrgui/dsp/spectrumvis.h +++ b/sdrgui/dsp/spectrumvis.h @@ -8,6 +8,7 @@ #include "export.h" #include "util/message.h" #include "util/movingaverage2d.h" +#include "util/fixedaverage2d.h" class GLSpectrum; class MessageQueue; @@ -15,34 +16,49 @@ class MessageQueue; class SDRGUI_API SpectrumVis : public BasebandSampleSink { public: + enum AveragingMode + { + AvgModeMoving, + AvgModeFixed + }; + class MsgConfigureSpectrumVis : public Message { MESSAGE_CLASS_DECLARATION public: - MsgConfigureSpectrumVis(int fftSize, int overlapPercent, unsigned int averageNb, FFTWindow::Function window) : + MsgConfigureSpectrumVis(int fftSize, int overlapPercent, unsigned int averageNb, int averagingMode, FFTWindow::Function window) : Message(), m_fftSize(fftSize), m_overlapPercent(overlapPercent), m_averageNb(averageNb), m_window(window) - { } + { + m_averagingMode = averagingMode < 0 ? AvgModeMoving : averagingMode > 1 ? AvgModeFixed : (SpectrumVis::AveragingMode) averagingMode; + } int getFFTSize() const { return m_fftSize; } int getOverlapPercent() const { return m_overlapPercent; } unsigned int getAverageNb() const { return m_averageNb; } + SpectrumVis::AveragingMode getAveragingMode() const { return m_averagingMode; } FFTWindow::Function getWindow() const { return m_window; } private: int m_fftSize; int m_overlapPercent; unsigned int m_averageNb; + SpectrumVis::AveragingMode m_averagingMode; FFTWindow::Function m_window; }; SpectrumVis(Real scalef, GLSpectrum* glSpectrum = 0); virtual ~SpectrumVis(); - void configure(MessageQueue* msgQueue, int fftSize, int overlapPercent, unsigned int averagingNb, FFTWindow::Function window); + void configure(MessageQueue* msgQueue, + int fftSize, + int overlapPercent, + unsigned int averagingNb, + int averagingMode, + FFTWindow::Function window); virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); void feedTriggered(const SampleVector::const_iterator& triggerPoint, const SampleVector::const_iterator& end, bool positiveOnly); @@ -66,15 +82,21 @@ private: Real m_scalef; GLSpectrum* m_glSpectrum; - MovingAverage2D m_average; + MovingAverage2D m_movingAverage; + FixedAverage2D m_fixedAverage; unsigned int m_averageNb; + AveragingMode m_averagingMode; Real m_ofs; static const Real m_mult; QMutex m_mutex; - void handleConfigure(int fftSize, int overlapPercent, unsigned int averageNb, FFTWindow::Function window); + void handleConfigure(int fftSize, + int overlapPercent, + unsigned int averageNb, + AveragingMode averagingMode, + FFTWindow::Function window); }; #endif // INCLUDE_SPECTRUMVIS_H diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp index f3706a109..6933fcf75 100644 --- a/sdrgui/gui/glspectrumgui.cpp +++ b/sdrgui/gui/glspectrumgui.cpp @@ -191,7 +191,12 @@ void GLSpectrumGUI::applySettings() m_glSpectrum->setDisplayGridIntensity(m_displayGridIntensity); if (m_spectrumVis) { - m_spectrumVis->configure(m_messageQueue, m_fftSize, m_fftOverlap, m_averagingNb, (FFTWindow::Function)m_fftWindow); + m_spectrumVis->configure(m_messageQueue, + m_fftSize, + m_fftOverlap, + m_averagingNb, + m_averagingMode, + (FFTWindow::Function)m_fftWindow); } } @@ -199,7 +204,12 @@ void GLSpectrumGUI::on_fftWindow_currentIndexChanged(int index) { m_fftWindow = index; if(m_spectrumVis != 0) { - m_spectrumVis->configure(m_messageQueue, m_fftSize, m_fftOverlap, m_averagingNb, (FFTWindow::Function)m_fftWindow); + m_spectrumVis->configure(m_messageQueue, + m_fftSize, + m_fftOverlap, + m_averagingNb, + m_averagingMode, + (FFTWindow::Function)m_fftWindow); } } @@ -207,12 +217,26 @@ void GLSpectrumGUI::on_fftSize_currentIndexChanged(int index) { m_fftSize = 1 << (7 + index); if(m_spectrumVis != 0) { - m_spectrumVis->configure(m_messageQueue, m_fftSize, m_fftOverlap, m_averagingNb, (FFTWindow::Function)m_fftWindow); + m_spectrumVis->configure(m_messageQueue, + m_fftSize, + m_fftOverlap, + m_averagingNb, + m_averagingMode, + (FFTWindow::Function)m_fftWindow); } } -void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index __attribute__((unused))) +void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index) { + m_averagingMode = index < 0 ? AvgModeMoving : index > 1 ? AvgModeFixed : (AveragingMode) index; + if(m_spectrumVis != 0) { + m_spectrumVis->configure(m_messageQueue, + m_fftSize, + m_fftOverlap, + m_averagingNb, + m_averagingMode, + (FFTWindow::Function)m_fftWindow); + } } void GLSpectrumGUI::on_averaging_currentIndexChanged(int index) @@ -220,7 +244,12 @@ void GLSpectrumGUI::on_averaging_currentIndexChanged(int index) m_averagingIndex = index; m_averagingNb = getAveragingValue(index); if(m_spectrumVis != 0) { - m_spectrumVis->configure(m_messageQueue, m_fftSize, m_fftOverlap, m_averagingNb, (FFTWindow::Function)m_fftWindow); + m_spectrumVis->configure(m_messageQueue, + m_fftSize, + m_fftOverlap, + m_averagingNb, + m_averagingMode, + (FFTWindow::Function)m_fftWindow); } }