From 6641355fbdd21eaff2a2587fd3627495bf88775a Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 10 Feb 2021 08:34:42 +0100 Subject: [PATCH] Spectrum GUI autoscaling. Implements #771 --- sdrbase/dsp/spectrumvis.cpp | 21 +++++++++++++-- sdrbase/dsp/spectrumvis.h | 5 +++- sdrgui/gui/glspectrumgui.cpp | 50 +++++++++++++++++++++++++++++++++--- sdrgui/gui/glspectrumgui.h | 1 + sdrgui/gui/glspectrumgui.ui | 48 +++++++++++++++++----------------- 5 files changed, 94 insertions(+), 31 deletions(-) diff --git a/sdrbase/dsp/spectrumvis.cpp b/sdrbase/dsp/spectrumvis.cpp index 8cc41c460..ecb56e72f 100644 --- a/sdrbase/dsp/spectrumvis.cpp +++ b/sdrbase/dsp/spectrumvis.cpp @@ -33,7 +33,7 @@ #define MAX_FFT_SIZE 4096 #ifndef LINUX -inline double log2f(double n) +inline double log2(double n) { return log(n) / log(2.0); } @@ -45,7 +45,7 @@ MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureWSpectrumOpenClose, Message) MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgConfigureWSpectrum, Message) MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgStartStop, Message) -const Real SpectrumVis::m_mult = (10.0f / log2f(10.0f)); +const Real SpectrumVis::m_mult = (10.0f / log2(10.0f)); SpectrumVis::SpectrumVis(Real scalef) : BasebandSampleSink(), @@ -54,6 +54,7 @@ SpectrumVis::SpectrumVis(Real scalef) : m_fftEngineSequence(0), m_fftBuffer(MAX_FFT_SIZE), m_powerSpectrum(MAX_FFT_SIZE), + m_psd(MAX_FFT_SIZE), m_fftBufferFill(0), m_needMoreSamples(false), m_scalef(scalef), @@ -132,6 +133,7 @@ void SpectrumVis::feed(const Complex *begin, unsigned int length) } v = c.real() * c.real() + c.imag() * c.imag(); + m_psd[i] = v/m_powFFTDiv; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; m_powerSpectrum[i] = v; } @@ -167,6 +169,7 @@ void SpectrumVis::feed(const Complex *begin, unsigned int length) v = c.real() * c.real() + c.imag() * c.imag(); v = m_movingAverage.storeAndGetAvg(v, i); + m_psd[i] = v/m_powFFTDiv; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; m_powerSpectrum[i] = v; } @@ -209,6 +212,7 @@ void SpectrumVis::feed(const Complex *begin, unsigned int length) // result available if (m_fixedAverage.storeAndGetAvg(avg, v, i)) { + m_psd[i] = avg/m_powFFTDiv; avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; m_powerSpectrum[i] = avg; } @@ -254,6 +258,7 @@ void SpectrumVis::feed(const Complex *begin, unsigned int length) // result available if (m_max.storeAndGetMax(max, v, i)) { + m_psd[i] = max/m_powFFTDiv; max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; m_powerSpectrum[i] = max; } @@ -340,6 +345,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV { c = fftOut[i]; v = c.real() * c.real() + c.imag() * c.imag(); + m_psd[i] = v/m_powFFTDiv; m_specMax = v > m_specMax ? v : m_specMax; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; m_powerSpectrum[i * 2] = v; @@ -352,12 +358,14 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV { c = fftOut[i + halfSize]; v = c.real() * c.real() + c.imag() * c.imag(); + m_psd[i] = v/m_powFFTDiv; m_specMax = v > m_specMax ? v : m_specMax; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; m_powerSpectrum[i] = v; c = fftOut[i]; v = c.real() * c.real() + c.imag() * c.imag(); + m_psd[i + halfSize] = v/m_powFFTDiv; m_specMax = v > m_specMax ? v : m_specMax; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; m_powerSpectrum[i + halfSize] = v; @@ -394,6 +402,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV c = fftOut[i]; v = c.real() * c.real() + c.imag() * c.imag(); v = m_movingAverage.storeAndGetAvg(v, i); + m_psd[i] = v/m_powFFTDiv; m_specMax = v > m_specMax ? v : m_specMax; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; m_powerSpectrum[i * 2] = v; @@ -407,6 +416,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV c = fftOut[i + halfSize]; v = c.real() * c.real() + c.imag() * c.imag(); v = m_movingAverage.storeAndGetAvg(v, i+halfSize); + m_psd[i] = v/m_powFFTDiv; m_specMax = v > m_specMax ? v : m_specMax; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; m_powerSpectrum[i] = v; @@ -414,6 +424,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV c = fftOut[i]; v = c.real() * c.real() + c.imag() * c.imag(); v = m_movingAverage.storeAndGetAvg(v, i); + m_psd[i + halfSize] = v/m_powFFTDiv; m_specMax = v > m_specMax ? v : m_specMax; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; m_powerSpectrum[i + halfSize] = v; @@ -456,6 +467,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV // result available if (m_fixedAverage.storeAndGetAvg(avg, v, i)) { + m_psd[i] = avg/m_powFFTDiv; specMax = avg > specMax ? avg : specMax; avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; m_powerSpectrum[i * 2] = avg; @@ -473,6 +485,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV // result available if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize)) { + m_psd[i] = avg/m_powFFTDiv; specMax = avg > specMax ? avg : specMax; avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; m_powerSpectrum[i] = avg; @@ -484,6 +497,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV // result available if (m_fixedAverage.storeAndGetAvg(avg, v, i)) { + m_psd[i + halfSize] = avg/m_powFFTDiv; specMax = avg > specMax ? avg : specMax; avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; m_powerSpectrum[i + halfSize] = avg; @@ -531,6 +545,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV // result available if (m_max.storeAndGetMax(max, v, i)) { + m_psd[i] = max/m_powFFTDiv; specMax = max > specMax ? max : specMax; max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; m_powerSpectrum[i * 2] = max; @@ -548,6 +563,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV // result available if (m_max.storeAndGetMax(max, v, i+halfSize)) { + m_psd[i] = max/m_powFFTDiv; specMax = max > specMax ? max : specMax; max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; m_powerSpectrum[i] = max; @@ -559,6 +575,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV // result available if (m_max.storeAndGetMax(max, v, i)) { + m_psd[i + halfSize] = max/m_powFFTDiv; specMax = max > specMax ? max : specMax; max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; m_powerSpectrum[i + halfSize] = max; diff --git a/sdrbase/dsp/spectrumvis.h b/sdrbase/dsp/spectrumvis.h index ac26d4d7f..10cd73c0f 100644 --- a/sdrbase/dsp/spectrumvis.h +++ b/sdrbase/dsp/spectrumvis.h @@ -124,6 +124,8 @@ public: void configureWSSpectrum(const QString& address, uint16_t port); const GLSpectrumSettings& getSettings() const { return m_settings; } Real getSpecMax() const { return m_specMax / m_powFFTDiv; } + void getPowerSpectrumCopy(std::vector& copy) { copy.assign(m_powerSpectrum.begin(), m_powerSpectrum.end()); } + void getPSDCopy(std::vector& copy) { copy.assign(m_psd.begin(), m_psd.begin() + m_settings.m_fftSize); } virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly); virtual void feed(const Complex *begin, unsigned int length); //!< direct FFT feed @@ -187,7 +189,8 @@ private: unsigned int m_fftEngineSequence; std::vector m_fftBuffer; - std::vector m_powerSpectrum; + std::vector m_powerSpectrum; //!< displayable power spectrum + std::vector m_psd; //!< real PSD GLSpectrumSettings m_settings; int m_overlapSize; diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp index 7090de2be..825378f16 100644 --- a/sdrgui/gui/glspectrumgui.cpp +++ b/sdrgui/gui/glspectrumgui.cpp @@ -29,6 +29,7 @@ #include "gui/crightclickenabler.h" #include "gui/wsspectrumsettingsdialog.h" #include "util/simpleserializer.h" +#include "util/db.h" #include "ui_glspectrumgui.h" GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) : @@ -48,6 +49,7 @@ GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) : ); ui->refLevel->setStyleSheet(levelStyle); ui->levelRange->setStyleSheet(levelStyle); + ui->fftOverlap->setStyleSheet(levelStyle); // ui->refLevel->findChild()->setStyleSheet("color: white; background-color: rgb(79, 79, 79); border: 1px solid gray; border-radius: 4px; "); // ui->refLevel->setStyleSheet("background-color: rgb(79, 79, 79);"); @@ -143,7 +145,6 @@ void GLSpectrumGUI::displaySettings() } ui->fftOverlap->setValue(m_settings.m_fftOverlap); - ui->fftOverlapText->setText(tr("%1").arg(m_settings.m_fftOverlap)); setMaximumOverlap(); ui->averaging->setCurrentIndex(m_settings.m_averagingIndex); ui->averagingMode->setCurrentIndex((int) m_settings.m_averagingMode); @@ -230,12 +231,51 @@ void GLSpectrumGUI::on_fftOverlap_valueChanged(int value) { qDebug("GLSpectrumGUI::on_fftOverlap_valueChanged: %d", value); m_settings.m_fftOverlap = value; - ui->fftOverlapText->setText(tr("%1").arg(m_settings.m_fftOverlap)); setMaximumOverlap(); applySettings(); setAveragingToolitp(); } +void GLSpectrumGUI::on_autoscale_clicked(bool checked) +{ + (void) checked; + + if (!m_spectrumVis) { + return; + } + + std::vector psd; + m_spectrumVis->getPSDCopy(psd); + int avgRange = m_settings.m_fftSize / 32; + + if (psd.size() < (unsigned int) avgRange) { + return; + } + + std::sort(psd.begin(), psd.end()); + float maxSum = 0.0f, minSum = 0.0f; + + for (int i = 0; i < avgRange; i++) + { + minSum += psd[i]; + maxSum += psd[psd.size() - i-1]; + } + + float minAvg = minSum / avgRange; + float maxAvg = maxSum / avgRange; + int minLvl = CalcDb::dbPower(minAvg*2); + int maxLvl = CalcDb::dbPower(maxAvg*10); + + m_settings.m_refLevel = maxLvl; + m_settings.m_powerRange = maxLvl - minLvl; + ui->refLevel->setValue(m_settings.m_refLevel); + ui->levelRange->setValue(m_settings.m_powerRange); + // qDebug("GLSpectrumGUI::on_autoscale_clicked: max: %d min %d max: %e min: %e", + // maxLvl, minLvl, maxAvg, minAvg); + + applySettings(); +} + void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index) { qDebug("GLSpectrumGUI::on_averagingMode_currentIndexChanged: %d", index); @@ -519,9 +559,11 @@ void GLSpectrumGUI::setFFTSize(int log2FFTSize) void GLSpectrumGUI::setMaximumOverlap() { - ui->fftOverlap->setMaximum((m_settings.m_fftSize/2)-1); + int halfSize = m_settings.m_fftSize/2; + ui->fftOverlap->setMaximum((halfSize)-1); int value = ui->fftOverlap->value(); - ui->fftOverlapText->setText(tr("%1").arg(value)); + ui->fftOverlap->setValue(value); + ui->fftOverlap->setToolTip(tr("FFT overlap %1 %").arg((value/(float)halfSize)*100.0f)); if (m_glSpectrum) { m_glSpectrum->setFFTOverlap(value); diff --git a/sdrgui/gui/glspectrumgui.h b/sdrgui/gui/glspectrumgui.h index d5edfbc79..758c9204e 100644 --- a/sdrgui/gui/glspectrumgui.h +++ b/sdrgui/gui/glspectrumgui.h @@ -86,6 +86,7 @@ private slots: void on_fftWindow_currentIndexChanged(int index); void on_fftSize_currentIndexChanged(int index); void on_fftOverlap_valueChanged(int value); + void on_autoscale_clicked(bool checked); void on_refLevel_valueChanged(int value); void on_levelRange_valueChanged(int value); void on_decay_valueChanged(int index); diff --git a/sdrgui/gui/glspectrumgui.ui b/sdrgui/gui/glspectrumgui.ui index d958b1a7d..b3ff51e56 100644 --- a/sdrgui/gui/glspectrumgui.ui +++ b/sdrgui/gui/glspectrumgui.ui @@ -162,41 +162,25 @@ - - - - 24 - 24 - - - - FFT overlap - - - 63 - - - 1 - - - - - + - 30 + 60 0 FFT overlap - - 0000 - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + 0 + + + 63 + @@ -387,6 +371,22 @@ 3 + + + + + 24 + 24 + + + + Autoscale max level and range + + + A + + +