From d5f153ff751f52388effb4b82b09aa8fda8cbb0d Mon Sep 17 00:00:00 2001 From: f4exb Date: Wed, 4 Jul 2018 23:11:28 +0200 Subject: [PATCH] Spectrum: added log/linear control --- plugins/channelrx/demodbfm/bfmdemodgui.cpp | 9 ++++++- plugins/channelrx/udpsrc/udpsrcgui.cpp | 8 +++++- plugins/channeltx/udpsink/udpsinkgui.cpp | 8 +++++- sdrgui/dsp/spectrumvis.cpp | 5 ++-- sdrgui/dsp/spectrumvis.h | 16 ++++++++--- sdrgui/gui/glspectrumgui.cpp | 30 +++++++++++++++++---- sdrgui/gui/glspectrumgui.h | 2 ++ sdrgui/gui/glspectrumgui.ui | 20 +++++++++++++- sdrgui/resources/linear.png | Bin 0 -> 458 bytes sdrgui/resources/linear.xcf | Bin 0 -> 14052 bytes sdrgui/resources/logarithmic.png | Bin 0 -> 542 bytes sdrgui/resources/logarithmic.xcf | Bin 0 -> 13768 bytes sdrgui/resources/res.qrc | 2 ++ 13 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 sdrgui/resources/linear.png create mode 100644 sdrgui/resources/linear.xcf create mode 100644 sdrgui/resources/logarithmic.png create mode 100644 sdrgui/resources/logarithmic.xcf diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index 87dfc9ac8..2e0f4c42a 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -359,7 +359,14 @@ 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, 0, FFTWindow::BlackmanHarris); + m_spectrumVis->configure( + m_spectrumVis->getInputMessageQueue(), + 64, // FFT size + 10, // overlapping % + 0, // number of averaging samples + 0, // no averaging + FFTWindow::BlackmanHarris, + false); // logarithmic scale 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 2a645c287..a3332091f 100644 --- a/plugins/channelrx/udpsrc/udpsrcgui.cpp +++ b/plugins/channelrx/udpsrc/udpsrcgui.cpp @@ -187,7 +187,13 @@ 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, 0, FFTWindow::BlackmanHarris); + m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), + 64, // FFT size + 10, // overlapping % + 0, // number of averaging samples + 0, // no averaging + FFTWindow::BlackmanHarris, + false); // logarithmic scale 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 88e33c399..1d7b8da74 100644 --- a/plugins/channeltx/udpsink/udpsinkgui.cpp +++ b/plugins/channeltx/udpsink/udpsinkgui.cpp @@ -142,7 +142,13 @@ 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, 0, FFTWindow::BlackmanHarris); + m_spectrumVis->configure(m_spectrumVis->getInputMessageQueue(), + 64, // FFT size + 10, // overlapping % + 0, // number of averaging samples + 0, // no averaging + FFTWindow::BlackmanHarris, + false); // logarithmic scale ui->glSpectrum->connectTimer(MainWindow::getInstance()->getMasterTimer()); connect(&MainWindow::getInstance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); diff --git a/sdrgui/dsp/spectrumvis.cpp b/sdrgui/dsp/spectrumvis.cpp index 2c1542ec8..ae386182e 100644 --- a/sdrgui/dsp/spectrumvis.cpp +++ b/sdrgui/dsp/spectrumvis.cpp @@ -44,9 +44,10 @@ void SpectrumVis::configure(MessageQueue* msgQueue, int overlapPercent, unsigned int averagingNb, int averagingMode, - FFTWindow::Function window) + FFTWindow::Function window, + bool linear) { - MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(fftSize, overlapPercent, averagingNb, averagingMode, window); + MsgConfigureSpectrumVis* cmd = new MsgConfigureSpectrumVis(fftSize, overlapPercent, averagingNb, averagingMode, window, linear); msgQueue->push(cmd); } diff --git a/sdrgui/dsp/spectrumvis.h b/sdrgui/dsp/spectrumvis.h index e6832627a..de1c35236 100644 --- a/sdrgui/dsp/spectrumvis.h +++ b/sdrgui/dsp/spectrumvis.h @@ -27,12 +27,19 @@ public: MESSAGE_CLASS_DECLARATION public: - MsgConfigureSpectrumVis(int fftSize, int overlapPercent, unsigned int averageNb, int averagingMode, FFTWindow::Function window) : + MsgConfigureSpectrumVis( + int fftSize, + int overlapPercent, + unsigned int averageNb, + int averagingMode, + FFTWindow::Function window, + bool linear) : Message(), m_fftSize(fftSize), m_overlapPercent(overlapPercent), m_averageNb(averageNb), - m_window(window) + m_window(window), + m_linear(linear) { m_averagingMode = averagingMode < 0 ? AvgModeNone : averagingMode > 2 ? AvgModeFixed : (SpectrumVis::AveragingMode) averagingMode; } @@ -42,6 +49,7 @@ public: unsigned int getAverageNb() const { return m_averageNb; } SpectrumVis::AveragingMode getAveragingMode() const { return m_averagingMode; } FFTWindow::Function getWindow() const { return m_window; } + bool getLinear() const { return m_linear; } private: int m_fftSize; @@ -49,6 +57,7 @@ public: unsigned int m_averageNb; SpectrumVis::AveragingMode m_averagingMode; FFTWindow::Function m_window; + bool m_linear; }; SpectrumVis(Real scalef, GLSpectrum* glSpectrum = 0); @@ -59,7 +68,8 @@ public: int overlapPercent, unsigned int averagingNb, int averagingMode, - FFTWindow::Function window); + FFTWindow::Function window, + bool m_linear); 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); diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp index a13a49c40..e9040f870 100644 --- a/sdrgui/gui/glspectrumgui.cpp +++ b/sdrgui/gui/glspectrumgui.cpp @@ -200,7 +200,8 @@ void GLSpectrumGUI::applySettings() m_fftOverlap, m_averagingNb, m_averagingMode, - (FFTWindow::Function)m_fftWindow); + (FFTWindow::Function)m_fftWindow, + m_linear); } setAveragingToolitp(); @@ -215,7 +216,8 @@ void GLSpectrumGUI::on_fftWindow_currentIndexChanged(int index) m_fftOverlap, m_averagingNb, m_averagingMode, - (FFTWindow::Function)m_fftWindow); + (FFTWindow::Function)m_fftWindow, + m_linear); } } @@ -228,7 +230,8 @@ void GLSpectrumGUI::on_fftSize_currentIndexChanged(int index) m_fftOverlap, m_averagingNb, m_averagingMode, - (FFTWindow::Function)m_fftWindow); + (FFTWindow::Function)m_fftWindow, + m_linear); } setAveragingToolitp(); } @@ -243,7 +246,8 @@ void GLSpectrumGUI::on_averagingMode_currentIndexChanged(int index) m_fftOverlap, m_averagingNb, m_averagingMode, - (FFTWindow::Function)m_fftWindow); + (FFTWindow::Function)m_fftWindow, + m_linear); } if (m_glSpectrum != 0) @@ -267,7 +271,8 @@ void GLSpectrumGUI::on_averaging_currentIndexChanged(int index) m_fftOverlap, m_averagingNb, m_averagingMode, - (FFTWindow::Function)m_fftWindow); + (FFTWindow::Function)m_fftWindow, + m_linear); } if (m_glSpectrum != 0) @@ -280,6 +285,21 @@ void GLSpectrumGUI::on_averaging_currentIndexChanged(int index) setAveragingToolitp(); } +void GLSpectrumGUI::on_linscale_toggled(bool checked) +{ + m_linear = checked; + + if(m_spectrumVis != 0) { + m_spectrumVis->configure(m_messageQueueToVis, + m_fftSize, + m_fftOverlap, + m_averagingNb, + m_averagingMode, + (FFTWindow::Function)m_fftWindow, + m_linear); + } +} + void GLSpectrumGUI::on_refLevel_currentIndexChanged(int index) { m_refLevel = 0 - index * 5; diff --git a/sdrgui/gui/glspectrumgui.h b/sdrgui/gui/glspectrumgui.h index 773274595..13d305231 100644 --- a/sdrgui/gui/glspectrumgui.h +++ b/sdrgui/gui/glspectrumgui.h @@ -63,6 +63,7 @@ private: int m_averagingIndex; int m_averagingMaxScale; //!< Max power of 10 multiplier to 2,5,10 base ex: 2 -> 2,5,10,20,50,100,200,500,1000 unsigned int m_averagingNb; + bool m_linear; //!< linear else logarithmic scale void applySettings(); int getAveragingIndex(int averaging) const; @@ -85,6 +86,7 @@ private slots: void on_traceIntensity_valueChanged(int index); void on_averagingMode_currentIndexChanged(int index); void on_averaging_currentIndexChanged(int index); + void on_linscale_toggled(bool checked); void on_waterfall_toggled(bool checked); void on_histogram_toggled(bool checked); diff --git a/sdrgui/gui/glspectrumgui.ui b/sdrgui/gui/glspectrumgui.ui index ed90ebf08..6942e96bc 100644 --- a/sdrgui/gui/glspectrumgui.ui +++ b/sdrgui/gui/glspectrumgui.ui @@ -6,7 +6,7 @@ 0 0 - 331 + 342 59 @@ -633,6 +633,24 @@ + + + + Logarithmic / Linear scale selection + + + + + + + :/logarithmic.png + :/linear.png:/logarithmic.png + + + true + + + diff --git a/sdrgui/resources/linear.png b/sdrgui/resources/linear.png new file mode 100644 index 0000000000000000000000000000000000000000..115bcaf8a5a6dc7d35b21549ae9799dc39a16011 GIT binary patch literal 458 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYYs>cdx@v7EBhmM7C|dLzedOTKq1-8kcblJ{M_8syb>Unfx)>bHL)Z$ zMWH;iBtya7(>EYRFO{8vfl<%X#WBR=_}Xjxy=5H*7#`TxC#0%#n9aKMu5|rnx9$c7 zC6BHf8H#2Hj~zM`_9pku-+UgaO1nUn#t4 zZTS$NzP3Pw=la7&o;|Bp)!+UYbShCZl`HhM^c6;*2RB~->be-lDD|V=;h`kE#K}X; z3x$5fze!!TwE6M@`Hj)_@6T#xJ&3L0{=;~C#ikFEb6D%z^(89G-z`6 z5B)FL|4C=h{GqgtY5&3JQ??ut-`VrN@&5rYwSS!U%=?c8?TxziX!Q@#AEIJWrKi~T z$%QgapW%P-ai#G0#`pv?;roZ?OWiwrr2K~|^7Nw&)U;@YtIK=ll- xxvMY!J@!a!^2-X{69KjJzqwyHa_K|RDQ+z@iT^>gnp|vd$@?2>{!%zgPeO literal 0 HcmV?d00001 diff --git a/sdrgui/resources/linear.xcf b/sdrgui/resources/linear.xcf new file mode 100644 index 0000000000000000000000000000000000000000..53d08ebdf95007a52616fdbc74696971d4ed4482 GIT binary patch literal 14052 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 ZpK8Y7zrXC68veh+teYBsJ}@=pzX4#_BNhMv literal 0 HcmV?d00001 diff --git a/sdrgui/resources/logarithmic.png b/sdrgui/resources/logarithmic.png new file mode 100644 index 0000000000000000000000000000000000000000..32bb9bd1a8d19ad4151493ef00451f6f3430cd54 GIT binary patch literal 542 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYYs>cdx@v7EBhmM7C{SZ!K~xofI_mFArU3c`MJ5Nc_lzD1A}u>YGO%h zib8p2Nrr;Er*A-tUMf2S17nk?i(`n#@x4>+^<4r5j@EykXV2MrLQAB>X_Hi|#xxI; zMS}9K?yJMJj)s5G6r+&$URiv8rsex{F^d~K znGT%cI(jrByIi5k%7HJX?i!ODAJ4D4%rxGHbxIE=6)g~#V0_jfxqzpK>97Omi*JiV z6Bt*Rs&*9Zh-aGCV4CnPx`MglbLjW1d&v%$xtM;KY~a-SyVtYXI00|a;|x-A7oZAFxP!83s#xS zx}QCw@W0dzZKL{ILM)s`2^V*7&PwxISSz-xj6>qPo~^>f)WrEk|8KH~9sjgNX$7u<2zYJGmSy0Kpn6iuG4elF{r5}E)q9@dHg literal 0 HcmV?d00001 diff --git a/sdrgui/resources/logarithmic.xcf b/sdrgui/resources/logarithmic.xcf new file mode 100644 index 0000000000000000000000000000000000000000..81e75579723fd3bc22fbbd3acbda11035ed0eb21 GIT binary patch literal 13768 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 + linear.png + logarithmic.png pin_last.png sweep.png minusrx.png