1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-09-29 16:26:47 -04:00

Spectrum GUI autoscaling. Implements #771

This commit is contained in:
f4exb 2021-02-10 08:34:42 +01:00
parent 7827800d13
commit 6641355fbd
5 changed files with 94 additions and 31 deletions

View File

@ -33,7 +33,7 @@
#define MAX_FFT_SIZE 4096 #define MAX_FFT_SIZE 4096
#ifndef LINUX #ifndef LINUX
inline double log2f(double n) inline double log2(double n)
{ {
return log(n) / log(2.0); 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::MsgConfigureWSpectrum, Message)
MESSAGE_CLASS_DEFINITION(SpectrumVis::MsgStartStop, 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) : SpectrumVis::SpectrumVis(Real scalef) :
BasebandSampleSink(), BasebandSampleSink(),
@ -54,6 +54,7 @@ SpectrumVis::SpectrumVis(Real scalef) :
m_fftEngineSequence(0), m_fftEngineSequence(0),
m_fftBuffer(MAX_FFT_SIZE), m_fftBuffer(MAX_FFT_SIZE),
m_powerSpectrum(MAX_FFT_SIZE), m_powerSpectrum(MAX_FFT_SIZE),
m_psd(MAX_FFT_SIZE),
m_fftBufferFill(0), m_fftBufferFill(0),
m_needMoreSamples(false), m_needMoreSamples(false),
m_scalef(scalef), 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(); 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; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v; 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 = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i); 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; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v; m_powerSpectrum[i] = v;
} }
@ -209,6 +212,7 @@ void SpectrumVis::feed(const Complex *begin, unsigned int length)
// result available // result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i)) 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; avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i] = avg; m_powerSpectrum[i] = avg;
} }
@ -254,6 +258,7 @@ void SpectrumVis::feed(const Complex *begin, unsigned int length)
// result available // result available
if (m_max.storeAndGetMax(max, v, i)) 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; max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i] = max; m_powerSpectrum[i] = max;
} }
@ -340,6 +345,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
{ {
c = fftOut[i]; c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag(); v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax; m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i * 2] = v; m_powerSpectrum[i * 2] = v;
@ -352,12 +358,14 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
{ {
c = fftOut[i + halfSize]; c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag(); v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax; m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v; m_powerSpectrum[i] = v;
c = fftOut[i]; c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag(); v = c.real() * c.real() + c.imag() * c.imag();
m_psd[i + halfSize] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax; m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i + halfSize] = v; m_powerSpectrum[i + halfSize] = v;
@ -394,6 +402,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
c = fftOut[i]; c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag(); v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i); v = m_movingAverage.storeAndGetAvg(v, i);
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax; m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i * 2] = v; m_powerSpectrum[i * 2] = v;
@ -407,6 +416,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
c = fftOut[i + halfSize]; c = fftOut[i + halfSize];
v = c.real() * c.real() + c.imag() * c.imag(); v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i+halfSize); v = m_movingAverage.storeAndGetAvg(v, i+halfSize);
m_psd[i] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax; m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i] = v; m_powerSpectrum[i] = v;
@ -414,6 +424,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
c = fftOut[i]; c = fftOut[i];
v = c.real() * c.real() + c.imag() * c.imag(); v = c.real() * c.real() + c.imag() * c.imag();
v = m_movingAverage.storeAndGetAvg(v, i); v = m_movingAverage.storeAndGetAvg(v, i);
m_psd[i + halfSize] = v/m_powFFTDiv;
m_specMax = v > m_specMax ? v : m_specMax; m_specMax = v > m_specMax ? v : m_specMax;
v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs; v = m_settings.m_linear ? v/m_powFFTDiv : m_mult * log2f(v) + m_ofs;
m_powerSpectrum[i + halfSize] = v; m_powerSpectrum[i + halfSize] = v;
@ -456,6 +467,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
// result available // result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i)) if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{ {
m_psd[i] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax; specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i * 2] = avg; m_powerSpectrum[i * 2] = avg;
@ -473,6 +485,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
// result available // result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize)) if (m_fixedAverage.storeAndGetAvg(avg, v, i+halfSize))
{ {
m_psd[i] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax; specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i] = avg; m_powerSpectrum[i] = avg;
@ -484,6 +497,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
// result available // result available
if (m_fixedAverage.storeAndGetAvg(avg, v, i)) if (m_fixedAverage.storeAndGetAvg(avg, v, i))
{ {
m_psd[i + halfSize] = avg/m_powFFTDiv;
specMax = avg > specMax ? avg : specMax; specMax = avg > specMax ? avg : specMax;
avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs; avg = m_settings.m_linear ? avg/m_powFFTDiv : m_mult * log2f(avg) + m_ofs;
m_powerSpectrum[i + halfSize] = avg; m_powerSpectrum[i + halfSize] = avg;
@ -531,6 +545,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
// result available // result available
if (m_max.storeAndGetMax(max, v, i)) if (m_max.storeAndGetMax(max, v, i))
{ {
m_psd[i] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax; specMax = max > specMax ? max : specMax;
max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i * 2] = max; m_powerSpectrum[i * 2] = max;
@ -548,6 +563,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
// result available // result available
if (m_max.storeAndGetMax(max, v, i+halfSize)) if (m_max.storeAndGetMax(max, v, i+halfSize))
{ {
m_psd[i] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax; specMax = max > specMax ? max : specMax;
max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i] = max; m_powerSpectrum[i] = max;
@ -559,6 +575,7 @@ void SpectrumVis::feed(const SampleVector::const_iterator& cbegin, const SampleV
// result available // result available
if (m_max.storeAndGetMax(max, v, i)) if (m_max.storeAndGetMax(max, v, i))
{ {
m_psd[i + halfSize] = max/m_powFFTDiv;
specMax = max > specMax ? max : specMax; specMax = max > specMax ? max : specMax;
max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs; max = m_settings.m_linear ? max/m_powFFTDiv : m_mult * log2f(max) + m_ofs;
m_powerSpectrum[i + halfSize] = max; m_powerSpectrum[i + halfSize] = max;

View File

@ -124,6 +124,8 @@ public:
void configureWSSpectrum(const QString& address, uint16_t port); void configureWSSpectrum(const QString& address, uint16_t port);
const GLSpectrumSettings& getSettings() const { return m_settings; } const GLSpectrumSettings& getSettings() const { return m_settings; }
Real getSpecMax() const { return m_specMax / m_powFFTDiv; } Real getSpecMax() const { return m_specMax / m_powFFTDiv; }
void getPowerSpectrumCopy(std::vector<Real>& copy) { copy.assign(m_powerSpectrum.begin(), m_powerSpectrum.end()); }
void getPSDCopy(std::vector<Real>& 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 SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool positiveOnly);
virtual void feed(const Complex *begin, unsigned int length); //!< direct FFT feed virtual void feed(const Complex *begin, unsigned int length); //!< direct FFT feed
@ -187,7 +189,8 @@ private:
unsigned int m_fftEngineSequence; unsigned int m_fftEngineSequence;
std::vector<Complex> m_fftBuffer; std::vector<Complex> m_fftBuffer;
std::vector<Real> m_powerSpectrum; std::vector<Real> m_powerSpectrum; //!< displayable power spectrum
std::vector<Real> m_psd; //!< real PSD
GLSpectrumSettings m_settings; GLSpectrumSettings m_settings;
int m_overlapSize; int m_overlapSize;

View File

@ -29,6 +29,7 @@
#include "gui/crightclickenabler.h" #include "gui/crightclickenabler.h"
#include "gui/wsspectrumsettingsdialog.h" #include "gui/wsspectrumsettingsdialog.h"
#include "util/simpleserializer.h" #include "util/simpleserializer.h"
#include "util/db.h"
#include "ui_glspectrumgui.h" #include "ui_glspectrumgui.h"
GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) : GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) :
@ -48,6 +49,7 @@ GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) :
); );
ui->refLevel->setStyleSheet(levelStyle); ui->refLevel->setStyleSheet(levelStyle);
ui->levelRange->setStyleSheet(levelStyle); ui->levelRange->setStyleSheet(levelStyle);
ui->fftOverlap->setStyleSheet(levelStyle);
// ui->refLevel->findChild<QLineEdit*>()->setStyleSheet("color: white; background-color: rgb(79, 79, 79); border: 1px solid gray; border-radius: 4px; "); // ui->refLevel->findChild<QLineEdit*>()->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);"); // ui->refLevel->setStyleSheet("background-color: rgb(79, 79, 79);");
@ -143,7 +145,6 @@ void GLSpectrumGUI::displaySettings()
} }
ui->fftOverlap->setValue(m_settings.m_fftOverlap); ui->fftOverlap->setValue(m_settings.m_fftOverlap);
ui->fftOverlapText->setText(tr("%1").arg(m_settings.m_fftOverlap));
setMaximumOverlap(); setMaximumOverlap();
ui->averaging->setCurrentIndex(m_settings.m_averagingIndex); ui->averaging->setCurrentIndex(m_settings.m_averagingIndex);
ui->averagingMode->setCurrentIndex((int) m_settings.m_averagingMode); 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); qDebug("GLSpectrumGUI::on_fftOverlap_valueChanged: %d", value);
m_settings.m_fftOverlap = value; m_settings.m_fftOverlap = value;
ui->fftOverlapText->setText(tr("%1").arg(m_settings.m_fftOverlap));
setMaximumOverlap(); setMaximumOverlap();
applySettings(); applySettings();
setAveragingToolitp(); setAveragingToolitp();
} }
void GLSpectrumGUI::on_autoscale_clicked(bool checked)
{
(void) checked;
if (!m_spectrumVis) {
return;
}
std::vector<Real> 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) void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index)
{ {
qDebug("GLSpectrumGUI::on_averagingMode_currentIndexChanged: %d", index); qDebug("GLSpectrumGUI::on_averagingMode_currentIndexChanged: %d", index);
@ -519,9 +559,11 @@ void GLSpectrumGUI::setFFTSize(int log2FFTSize)
void GLSpectrumGUI::setMaximumOverlap() 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(); 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) { if (m_glSpectrum) {
m_glSpectrum->setFFTOverlap(value); m_glSpectrum->setFFTOverlap(value);

View File

@ -86,6 +86,7 @@ private slots:
void on_fftWindow_currentIndexChanged(int index); void on_fftWindow_currentIndexChanged(int index);
void on_fftSize_currentIndexChanged(int index); void on_fftSize_currentIndexChanged(int index);
void on_fftOverlap_valueChanged(int value); void on_fftOverlap_valueChanged(int value);
void on_autoscale_clicked(bool checked);
void on_refLevel_valueChanged(int value); void on_refLevel_valueChanged(int value);
void on_levelRange_valueChanged(int value); void on_levelRange_valueChanged(int value);
void on_decay_valueChanged(int index); void on_decay_valueChanged(int index);

View File

@ -162,41 +162,25 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QDial" name="fftOverlap"> <widget class="QSpinBox" name="fftOverlap">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>FFT overlap</string>
</property>
<property name="maximum">
<number>63</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fftOverlapText">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>30</width> <width>60</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>FFT overlap</string> <string>FFT overlap</string>
</property> </property>
<property name="text">
<string>0000</string>
</property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>63</number>
</property>
</widget> </widget>
</item> </item>
<item> <item>
@ -387,6 +371,22 @@
<property name="spacing"> <property name="spacing">
<number>3</number> <number>3</number>
</property> </property>
<item>
<widget class="QPushButton" name="autoscale">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Autoscale max level and range</string>
</property>
<property name="text">
<string>A</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QSpinBox" name="refLevel"> <widget class="QSpinBox" name="refLevel">
<property name="minimumSize"> <property name="minimumSize">