From 22693ac613cf6433183f66dd93806df0c7e03f8a Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 12 Oct 2018 08:47:14 +0200 Subject: [PATCH] Spectrum: added max function in the 'averaging' modes --- debian/changelog | 6 ++ sdrbase/util/fixedaverage2d.h | 24 ++++---- sdrbase/util/max2d.h | 107 ++++++++++++++++++++++++++++++++++ sdrgui/dsp/spectrumvis.cpp | 76 +++++++++++++++++++----- sdrgui/dsp/spectrumvis.h | 21 ++++--- sdrgui/gui/glspectrumgui.cpp | 10 ++-- sdrgui/gui/glspectrumgui.h | 3 +- sdrgui/gui/glspectrumgui.ui | 5 ++ 8 files changed, 212 insertions(+), 40 deletions(-) create mode 100644 sdrbase/util/max2d.h diff --git a/debian/changelog b/debian/changelog index bf3560720..10534c313 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +sdrangel (4.2.2-1) unstable; urgency=medium + + * Spectrum: option to get max over a number of FFTs. Implements issue #207 + + -- Edouard Griffiths, F4EXB Sun, 14 Oct 2018 21:14:18 +0200 + sdrangel (4.2.1-1) unstable; urgency=medium * FileRecord improvement with robust header and some fixes. Fixes issue #206 diff --git a/sdrbase/util/fixedaverage2d.h b/sdrbase/util/fixedaverage2d.h index 192dcfe6a..c9f7b0ffa 100644 --- a/sdrbase/util/fixedaverage2d.h +++ b/sdrbase/util/fixedaverage2d.h @@ -24,7 +24,7 @@ template class FixedAverage2D { public: - FixedAverage2D() : m_sum(0), m_sumSize(0), m_width(0), m_size(0), m_avgIndex(0) {} + FixedAverage2D() : m_sum(0), m_maxSize(0), m_width(0), m_size(0), m_maxIndex(0) {} ~FixedAverage2D() { @@ -35,20 +35,20 @@ public: void resize(unsigned int width, unsigned int size) { - if (width > m_sumSize) + if (width > m_maxSize) { - m_sumSize = width; + m_maxSize = width; if (m_sum) { delete[] m_sum; } - m_sum = new T[m_sumSize]; + m_sum = new T[m_maxSize]; } m_width = width; m_size = size; std::fill(m_sum, m_sum+m_width, 0); - m_avgIndex = 0; + m_maxIndex = 0; } bool storeAndGetAvg(T& avg, T v, unsigned int index) @@ -61,7 +61,7 @@ public: m_sum[index] += v; - if (m_avgIndex == m_size - 1) + if (m_maxIndex == m_size - 1) { avg = m_sum[index]/m_size; return true; @@ -82,7 +82,7 @@ public: m_sum[index] += v; - if (m_avgIndex < m_size - 1) + if (m_maxIndex < m_size - 1) { sum = m_sum[index]; return true; @@ -99,25 +99,25 @@ public: return true; } - if (m_avgIndex == m_size - 1) + if (m_maxIndex == m_size - 1) { - m_avgIndex = 0; + m_maxIndex = 0; std::fill(m_sum, m_sum+m_width, 0); return true; } else { - m_avgIndex++; + m_maxIndex++; return false; } } private: T *m_sum; - unsigned int m_sumSize; + unsigned int m_maxSize; unsigned int m_width; unsigned int m_size; - unsigned int m_avgIndex; + unsigned int m_maxIndex; }; diff --git a/sdrbase/util/max2d.h b/sdrbase/util/max2d.h new file mode 100644 index 000000000..58be966c9 --- /dev/null +++ b/sdrbase/util/max2d.h @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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_MAX2D_H_ +#define SDRBASE_UTIL_MAX2D_H_ + +#include + +template +class Max2D +{ +public: + Max2D() : m_max(0), m_maxSize(0), m_width(0), m_size(0), m_maxIndex(0) {} + + ~Max2D() + { + if (m_max) { + delete[] m_max; + } + } + + void resize(unsigned int width, unsigned int size) + { + if (width > m_maxSize) + { + m_maxSize = width; + if (m_max) { + delete[] m_max; + } + m_max = new T[m_maxSize]; + } + + m_width = width; + m_size = size; + + std::fill(m_max, m_max+m_width, 0); + m_maxIndex = 0; + } + + bool storeAndGetMax(T& max, T v, unsigned int index) + { + if (m_size <= 1) + { + max = v; + return true; + } + + if (m_maxIndex == 0) + { + m_max[index] = v; + return false; + } + else if (m_maxIndex == m_size - 1) + { + m_max[index] = std::max(m_max[index], v); + max = m_max[index]; + return true; + } + else + { + m_max[index] = std::max(m_max[index], v); + return false; + } + } + + bool nextMax() + { + if (m_size <= 1) { + return true; + } + + if (m_maxIndex == m_size - 1) + { + m_maxIndex = 0; + std::fill(m_max, m_max+m_width, 0); + return true; + } + else + { + m_maxIndex++; + return false; + } + } + +private: + T *m_max; + unsigned int m_maxSize; + unsigned int m_width; + unsigned int m_size; + unsigned int m_maxIndex; +}; + +#endif /* SDRBASE_UTIL_MAX2D_H_ */ diff --git a/sdrgui/dsp/spectrumvis.cpp b/sdrgui/dsp/spectrumvis.cpp index 7bc7f5426..0389cdc91 100644 --- a/sdrgui/dsp/spectrumvis.cpp +++ b/sdrgui/dsp/spectrumvis.cpp @@ -26,7 +26,7 @@ SpectrumVis::SpectrumVis(Real scalef, GLSpectrum* glSpectrum) : m_scalef(scalef), m_glSpectrum(glSpectrum), m_averageNb(0), - m_averagingMode(AvgModeNone), + m_avgMode(AvgModeNone), m_linear(false), m_ofs(0), m_powFFTDiv(1.0), @@ -114,7 +114,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV Real v; std::size_t halfSize = m_fftSize / 2; - if (m_averagingMode == AvgModeNone) + if (m_avgMode == AvgModeNone) { if ( positiveOnly ) { @@ -146,7 +146,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV // send new data to visualisation m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); } - else if (m_averagingMode == AvgModeMoving) + else if (m_avgMode == AvgModeMovingAvg) { if ( positiveOnly ) { @@ -182,7 +182,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); m_movingAverage.nextAverage(); } - else if (m_averagingMode == AvgModeFixed) + else if (m_avgMode == AvgModeFixedAvg) { double avg; @@ -195,7 +195,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV if (m_fixedAverage.storeAndGetAvg(avg, v, i)) { - avg = m_linear ? v/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; + avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; m_powerSpectrum[i * 2] = avg; m_powerSpectrum[i * 2 + 1] = avg; } @@ -210,7 +210,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize)) { // result available - avg = m_linear ? v/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; + avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; m_powerSpectrum[i] = avg; } @@ -219,16 +219,61 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV if (m_fixedAverage.storeAndGetAvg(avg, v, i)) { // result available - avg = m_linear ? v/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; + avg = m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; m_powerSpectrum[i + halfSize] = avg; } } } - if (m_fixedAverage.nextAverage()) - { // result available - // send new data to visualisation - m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); + if (m_fixedAverage.nextAverage()) { // result available + m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation + } + } + else if (m_avgMode == AvgModeMax) + { + double max; + + 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_max.storeAndGetMax(max, v, i)) + { + max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; + m_powerSpectrum[i * 2] = max; + m_powerSpectrum[i * 2 + 1] = max; + } + } + } + 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_max.storeAndGetMax(max, v, i+halfSize)) + { // result available + max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; + m_powerSpectrum[i] = max; + } + + c = fftOut[i]; + v = c.real() * c.real() + c.imag() * c.imag(); + + if (m_max.storeAndGetMax(max, v, i)) + { // result available + max = m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; + m_powerSpectrum[i + halfSize] = max; + } + } + } + + if (m_max.nextMax()) { // result available + m_glSpectrum->newSpectrum(m_powerSpectrum, m_fftSize); // send new data to visualisation } } @@ -269,7 +314,7 @@ bool SpectrumVis::handleMessage(const Message& message) handleConfigure(conf.getFFTSize(), conf.getOverlapPercent(), conf.getAverageNb(), - conf.getAveragingMode(), + conf.getAvgMode(), conf.getWindow(), conf.getLinear()); return true; @@ -283,10 +328,12 @@ bool SpectrumVis::handleMessage(const Message& message) void SpectrumVis::handleConfigure(int fftSize, int overlapPercent, unsigned int averageNb, - AveragingMode averagingMode, + AvgMode averagingMode, FFTWindow::Function window, bool linear) { +// qDebug("SpectrumVis::handleConfigure, fftSize: %d overlapPercent: %d averageNb: %u averagingMode: %d window: %d linear: %s", +// fftSize, overlapPercent, averageNb, (int) averagingMode, (int) window, linear ? "true" : "false"); QMutexLocker mutexLocker(&m_mutex); if (fftSize > MAX_FFT_SIZE) @@ -319,8 +366,9 @@ void SpectrumVis::handleConfigure(int fftSize, m_fftBufferFill = m_overlapSize; m_movingAverage.resize(fftSize, averageNb); m_fixedAverage.resize(fftSize, averageNb); + m_max.resize(fftSize, averageNb); m_averageNb = averageNb; - m_averagingMode = averagingMode; + m_avgMode = averagingMode; m_linear = linear; m_ofs = 20.0f * log10f(1.0f / m_fftSize); m_powFFTDiv = m_fftSize*m_fftSize; diff --git a/sdrgui/dsp/spectrumvis.h b/sdrgui/dsp/spectrumvis.h index a86a5c0fd..b01c6459d 100644 --- a/sdrgui/dsp/spectrumvis.h +++ b/sdrgui/dsp/spectrumvis.h @@ -9,6 +9,7 @@ #include "util/message.h" #include "util/movingaverage2d.h" #include "util/fixedaverage2d.h" +#include "util/max2d.h" class GLSpectrum; class MessageQueue; @@ -16,11 +17,12 @@ class MessageQueue; class SDRGUI_API SpectrumVis : public BasebandSampleSink { public: - enum AveragingMode + enum AvgMode { AvgModeNone, - AvgModeMoving, - AvgModeFixed + AvgModeMovingAvg, + AvgModeFixedAvg, + AvgModeMax }; class MsgConfigureSpectrumVis : public Message { @@ -31,7 +33,7 @@ public: int fftSize, int overlapPercent, unsigned int averageNb, - int averagingMode, + int preProcessMode, FFTWindow::Function window, bool linear) : Message(), @@ -41,13 +43,13 @@ public: m_window(window), m_linear(linear) { - m_averagingMode = averagingMode < 0 ? AvgModeNone : averagingMode > 2 ? AvgModeFixed : (SpectrumVis::AveragingMode) averagingMode; + m_avgMode = preProcessMode < 0 ? AvgModeNone : preProcessMode > 3 ? AvgModeMax : (SpectrumVis::AvgMode) preProcessMode; } 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; } + SpectrumVis::AvgMode getAvgMode() const { return m_avgMode; } FFTWindow::Function getWindow() const { return m_window; } bool getLinear() const { return m_linear; } @@ -55,7 +57,7 @@ public: int m_fftSize; int m_overlapPercent; unsigned int m_averageNb; - SpectrumVis::AveragingMode m_averagingMode; + SpectrumVis::AvgMode m_avgMode; FFTWindow::Function m_window; bool m_linear; }; @@ -95,8 +97,9 @@ private: GLSpectrum* m_glSpectrum; MovingAverage2D m_movingAverage; FixedAverage2D m_fixedAverage; + Max2D m_max; unsigned int m_averageNb; - AveragingMode m_averagingMode; + AvgMode m_avgMode; bool m_linear; Real m_ofs; @@ -108,7 +111,7 @@ private: void handleConfigure(int fftSize, int overlapPercent, unsigned int averageNb, - AveragingMode averagingMode, + AvgMode averagingMode, FFTWindow::Function window, bool linear); }; diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp index 62a200437..d0ba90b1d 100644 --- a/sdrgui/gui/glspectrumgui.cpp +++ b/sdrgui/gui/glspectrumgui.cpp @@ -141,7 +141,7 @@ bool GLSpectrumGUI::deserialize(const QByteArray& data) Real waterfallShare; d.readReal(18, &waterfallShare, 0.66); d.readS32(19, &tmp, 0); - m_averagingMode = tmp < 0 ? AvgModeNone : tmp > 2 ? AvgModeFixed : (AveragingMode) tmp; + m_averagingMode = tmp < 0 ? AvgModeNone : tmp > 3 ? AvgModeMax : (AveragingMode) tmp; d.readS32(20, &tmp, 0); m_averagingIndex = getAveragingIndex(tmp); m_averagingNb = getAveragingValue(m_averagingIndex); @@ -244,7 +244,7 @@ void GLSpectrumGUI::on_fftSize_currentIndexChanged(int index) void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index) { - m_averagingMode = index < 0 ? AvgModeNone : index > 2 ? AvgModeFixed : (AveragingMode) index; + m_averagingMode = index < 0 ? AvgModeNone : index > 3 ? AvgModeMax : (AveragingMode) index; if(m_spectrumVis != 0) { m_spectrumVis->configure(m_messageQueueToVis, @@ -258,7 +258,7 @@ void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index) if (m_glSpectrum != 0) { - if (m_averagingMode == AvgModeFixed) { + if ((m_averagingMode == AvgModeFixed) || (m_averagingMode == AvgModeMax)) { m_glSpectrum->setTimingRate(m_averagingNb == 0 ? 1 : m_averagingNb); } else { m_glSpectrum->setTimingRate(1); @@ -283,8 +283,10 @@ void GLSpectrumGUI::on_averaging_currentIndexChanged(int index) if (m_glSpectrum != 0) { - if (m_averagingMode == AvgModeFixed) { + if ((m_averagingMode == AvgModeFixed) || (m_averagingMode == AvgModeMax)) { m_glSpectrum->setTimingRate(m_averagingNb == 0 ? 1 : m_averagingNb); + } else { + m_glSpectrum->setTimingRate(1); } } diff --git a/sdrgui/gui/glspectrumgui.h b/sdrgui/gui/glspectrumgui.h index 13d305231..9cf02a20d 100644 --- a/sdrgui/gui/glspectrumgui.h +++ b/sdrgui/gui/glspectrumgui.h @@ -22,7 +22,8 @@ public: { AvgModeNone, AvgModeMoving, - AvgModeFixed + AvgModeFixed, + AvgModeMax }; explicit GLSpectrumGUI(QWidget* parent = NULL); diff --git a/sdrgui/gui/glspectrumgui.ui b/sdrgui/gui/glspectrumgui.ui index 277c560ed..a874c7068 100644 --- a/sdrgui/gui/glspectrumgui.ui +++ b/sdrgui/gui/glspectrumgui.ui @@ -565,6 +565,11 @@ Fix + + + Max + +