From 043a76faf849866eb218f1d641ab1042324214b9 Mon Sep 17 00:00:00 2001 From: f4exb Date: Fri, 19 Feb 2021 13:42:05 +0100 Subject: [PATCH] Spectrum overlap fixes. Spectrum time and power zomming. Implements #779 --- sdrgui/gui/glspectrum.cpp | 130 ++++++++++++++++++++++++++++------- sdrgui/gui/glspectrum.h | 38 +++++++++- sdrgui/gui/glspectrumgui.cpp | 23 +++++++ sdrgui/readme.md | 20 +++++- 4 files changed, 184 insertions(+), 27 deletions(-) diff --git a/sdrgui/gui/glspectrum.cpp b/sdrgui/gui/glspectrum.cpp index 92debc41d..8202645bc 100644 --- a/sdrgui/gui/glspectrum.cpp +++ b/sdrgui/gui/glspectrum.cpp @@ -33,6 +33,8 @@ MESSAGE_CLASS_DEFINITION(GLSpectrum::MsgReportSampleRate, Message) MESSAGE_CLASS_DEFINITION(GLSpectrum::MsgReportWaterfallShare, Message) +MESSAGE_CLASS_DEFINITION(GLSpectrum::MsgReportFFTOverlap, Message) +MESSAGE_CLASS_DEFINITION(GLSpectrum::MsgReportPowerScale, Message) const float GLSpectrum::m_maxFrequencyZoom = 10.0f; @@ -1315,20 +1317,20 @@ void GLSpectrum::applyChanges() m_waterfallHeight = 0; } - if (!m_invertedWaterfall) - { - waterfallTop = m_topMargin; - frequencyScaleTop = waterfallTop + m_waterfallHeight + 1; - histogramTop = waterfallTop + m_waterfallHeight + m_frequencyScaleHeight + 1; - m_histogramHeight = height() - m_topMargin - m_waterfallHeight - m_frequencyScaleHeight - m_bottomMargin; - } - else + if (m_invertedWaterfall) { histogramTop = m_topMargin; m_histogramHeight = height() - m_topMargin - m_waterfallHeight - m_frequencyScaleHeight - m_bottomMargin; waterfallTop = histogramTop + m_histogramHeight + m_frequencyScaleHeight + 1; frequencyScaleTop = histogramTop + m_histogramHeight + 1; } + else + { + waterfallTop = m_topMargin; + frequencyScaleTop = waterfallTop + m_waterfallHeight + 1; + histogramTop = waterfallTop + m_waterfallHeight + m_frequencyScaleHeight + 1; + m_histogramHeight = height() - m_topMargin - m_waterfallHeight - m_frequencyScaleHeight - m_bottomMargin; + } m_timeScale.setSize(m_waterfallHeight); @@ -1336,7 +1338,10 @@ void GLSpectrum::applyChanges() { float scaleDiv = ((float)m_sampleRate / (float)m_timingRate) * (m_ssbSpectrum ? 2 : 1); float halfFFTSize = m_fftSize / 2; - scaleDiv *= halfFFTSize / (halfFFTSize - m_fftOverlap); + + if (halfFFTSize > m_fftOverlap) { + scaleDiv *= halfFFTSize / (halfFFTSize - m_fftOverlap); + } if (!m_invertedWaterfall) { m_timeScale.setRange(m_timingRate > 1 ? Unit::TimeHMS : Unit::Time, (m_waterfallHeight * m_fftSize) / scaleDiv, 0); @@ -1437,7 +1442,10 @@ void GLSpectrum::applyChanges() { float scaleDiv = ((float)m_sampleRate / (float)m_timingRate) * (m_ssbSpectrum ? 2 : 1); float halfFFTSize = m_fftSize / 2; - scaleDiv *= halfFFTSize / (halfFFTSize - m_fftOverlap); + + if (halfFFTSize > m_fftOverlap) { + scaleDiv *= halfFFTSize / (halfFFTSize - m_fftOverlap); + } if (!m_invertedWaterfall) { m_timeScale.setRange(m_timingRate > 1 ? Unit::TimeHMS : Unit::Time, (m_waterfallHeight * m_fftSize) / scaleDiv, 0); @@ -2278,7 +2286,7 @@ void GLSpectrum::wheelEvent(QWheelEvent *event) } } -void GLSpectrum::frequencyZoom(QWheelEvent *event) +void GLSpectrum::zoom(QWheelEvent *event) { #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) const QPointF& p = event->position(); @@ -2286,30 +2294,63 @@ void GLSpectrum::frequencyZoom(QWheelEvent *event) const QPointF& p = event->pos(); #endif - if (event->delta() > 0) // zoom in + float pwx = (p.x() - m_leftMargin) / (width() - m_leftMargin - m_rightMargin); // x position in window + + if ((pwx >= 0.0f) && (pwx <= 1.0f)) { - if (m_frequencyZoomFactor < m_maxFrequencyZoom) { - m_frequencyZoomFactor += 0.5f; - } else { - return; + if (event->delta() > 0) // zoom in + { + if (m_frequencyZoomFactor < m_maxFrequencyZoom) { + m_frequencyZoomFactor += 0.5f; + } else { + return; + } } + else + { + if (m_frequencyZoomFactor > 1.0f) { + m_frequencyZoomFactor -= 0.5f; + } else { + return; + } + } + + frequencyZoom(pwx); } else { - if (m_frequencyZoomFactor > 1.0f) { - m_frequencyZoomFactor -= 0.5f; - } else { - return; + float pwyh, pwyw; + + if (m_invertedWaterfall) // histo on top + { + pwyh = (p.y() - m_topMargin) / m_histogramHeight; + pwyw = (p.y() - m_topMargin - m_histogramHeight - m_frequencyScaleHeight) / m_waterfallHeight; + } + else // waterfall on top + { + pwyw = (p.y() - m_topMargin) / m_waterfallHeight; + pwyh = (p.y() - m_topMargin - m_waterfallHeight - m_frequencyScaleHeight) / m_histogramHeight; + } + + //qDebug("GLSpectrum::zoom: pwyh: %f pwyw: %f", pwyh, pwyw); + + if ((pwyw >= 0.0f) && (pwyw <= 1.0f)) { + timeZoom(event->delta() > 0); + } + + if ((pwyh >= 0.0f) && (pwyh <= 1.0f) && !m_linear) { + powerZoom(pwyh, event->delta() > 0); } } +} - float pw = (p.x() - m_leftMargin) / (width() - m_leftMargin - m_rightMargin); // position in window - pw = pw < 0.0f ? 0.0f : pw > 1.0f ? 1.0 : pw; +void GLSpectrum::frequencyZoom(float pw) +{ m_frequencyZoomPos += (pw - 0.5f) * (1.0f / m_frequencyZoomFactor); float lim = 0.5f / m_frequencyZoomFactor; m_frequencyZoomPos = m_frequencyZoomPos < lim ? lim : m_frequencyZoomPos > 1 - lim ? 1 - lim : m_frequencyZoomPos; - qDebug("GLSpectrum::spectrumZoom: pw: %f p: %f z: %f", pw, m_frequencyZoomPos, m_frequencyZoomFactor); + qDebug("GLSpectrum::frequencyZoom: pw: %f p: %f z: %f", pw, m_frequencyZoomPos, m_frequencyZoomFactor); updateFFTLimits(); } @@ -2331,6 +2372,47 @@ void GLSpectrum::frequencyPan(QMouseEvent *event) updateFFTLimits(); } +void GLSpectrum::timeZoom(bool zoomInElseOut) +{ + if ((m_fftOverlap == 0) && !zoomInElseOut) { + return; + } + + if (zoomInElseOut && (m_fftOverlap == m_fftSize/2 - 1)) { + return; + } + + m_fftOverlap = m_fftOverlap + (zoomInElseOut ? 1 : -1); + m_changesPending = true; + + if (m_messageQueueToGUI) + { + MsgReportFFTOverlap *msg = new MsgReportFFTOverlap(m_fftOverlap); + m_messageQueueToGUI->push(msg); + } +} + +void GLSpectrum::powerZoom(float pw, bool zoomInElseOut) +{ + m_powerRange = m_powerRange + (zoomInElseOut ? -2 : 2); + + if (pw > 2.0/3.0) { // bottom + m_referenceLevel = m_referenceLevel + (zoomInElseOut ? -2 : 2); + } else if (pw > 1.0/3.0) { // middle + m_referenceLevel = m_referenceLevel + (zoomInElseOut ? -1 : 1); + } // top + + m_powerRange = m_powerRange < 1 ? 1 : m_powerRange > 100 ? 100 : m_powerRange; + m_referenceLevel = m_referenceLevel < -110 ? -110 : m_referenceLevel > 0 ? 0 : m_referenceLevel; + m_changesPending = true; + + if (m_messageQueueToGUI) + { + MsgReportPowerScale *msg = new MsgReportPowerScale(m_referenceLevel, m_powerRange); + m_messageQueueToGUI->push(msg); + } +} + void GLSpectrum::resetFrequencyZoom() { m_frequencyZoomFactor = 1.0f; @@ -2421,7 +2503,7 @@ void GLSpectrum::channelMarkerMove(QWheelEvent *event, int mul) } } - frequencyZoom(event); + zoom(event); } void GLSpectrum::enterEvent(QEvent* event) diff --git a/sdrgui/gui/glspectrum.h b/sdrgui/gui/glspectrum.h index 43f7c33f1..032ae3843 100644 --- a/sdrgui/gui/glspectrum.h +++ b/sdrgui/gui/glspectrum.h @@ -75,6 +75,39 @@ public: Real m_waterfallShare; }; + class MsgReportFFTOverlap : public Message { + MESSAGE_CLASS_DECLARATION + + public: + MsgReportFFTOverlap(int overlap) : + Message(), + m_overlap(overlap) + {} + + int getOverlap() const { return m_overlap; } + + private: + int m_overlap; + }; + + class MsgReportPowerScale : public Message { + MESSAGE_CLASS_DECLARATION + + public: + MsgReportPowerScale(int refLevel, int range) : + Message(), + m_refLevel(refLevel), + m_range(range) + {} + + Real getRefLevel() const { return m_refLevel; } + Real getRange() const { return m_range; } + + private: + Real m_refLevel; + Real m_range; + }; + GLSpectrum(QWidget* parent = nullptr); virtual ~GLSpectrum(); @@ -349,8 +382,11 @@ private: void mouseReleaseEvent(QMouseEvent* event); void wheelEvent(QWheelEvent*); void channelMarkerMove(QWheelEvent*, int mul); - void frequencyZoom(QWheelEvent*); + void zoom(QWheelEvent*); + void frequencyZoom(float pw); void frequencyPan(QMouseEvent*); + void timeZoom(bool zoomInElseOut); + void powerZoom(float pw, bool zoomInElseOut); void resetFrequencyZoom(); void updateFFTLimits(); void setFrequencyScale(); diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp index 888bf5b7c..ab1dc19fa 100644 --- a/sdrgui/gui/glspectrumgui.cpp +++ b/sdrgui/gui/glspectrumgui.cpp @@ -630,6 +630,29 @@ bool GLSpectrumGUI::handleMessage(const Message& message) { const GLSpectrum::MsgReportWaterfallShare& report = (const GLSpectrum::MsgReportWaterfallShare&) message; m_settings.m_waterfallShare = report.getWaterfallShare(); + return true; + } + else if (GLSpectrum::MsgReportFFTOverlap::match(message)) + { + const GLSpectrum::MsgReportFFTOverlap& report = (const GLSpectrum::MsgReportFFTOverlap&) message; + m_settings.m_fftOverlap = report.getOverlap(); + ui->fftOverlap->blockSignals(true); + ui->fftOverlap->setValue(m_settings.m_fftOverlap); + ui->fftOverlap->blockSignals(false); + return true; + } + else if (GLSpectrum::MsgReportPowerScale::match(message)) + { + const GLSpectrum::MsgReportPowerScale& report = (const GLSpectrum::MsgReportPowerScale&) message; + m_settings.m_refLevel = report.getRefLevel(); + m_settings.m_powerRange = report.getRange(); + ui->refLevel->blockSignals(true); + ui->levelRange->blockSignals(true); + ui->refLevel->setValue(m_settings.m_refLevel); + ui->levelRange->setValue(m_settings.m_powerRange); + ui->levelRange->blockSignals(false); + ui->refLevel->blockSignals(false); + return true; } else if (SpectrumVis::MsgStartStop::match(message)) { diff --git a/sdrgui/readme.md b/sdrgui/readme.md index 6a2aa8249..4f539f5dc 100644 --- a/sdrgui/readme.md +++ b/sdrgui/readme.md @@ -973,15 +973,31 @@ Any change in the spectrum settings is not reflected in the markers. You have to

Mouse scroll wheel

+
Channel moving
+ When the mouse is over the center line of a channel: - scrolling will move the channel by +/- 10 Hz at each scroll up/down respectively - combined with Ctrl it will move the channel by +/- 100 Hz - combined with Shift it will move the channel by +/- 1 kHz -When the mouse is not over the center line of a channel it will zoom in/out along X (frequency) axis by a 0.5 step at each scroll up/down respectively between 1x (no zoom) and 10x. Note that in order to zoom on the center line of a channel you may move the mouse pointer in the top margin (center line moving is not active there but zooming is). +
Frequency zooming
-When zooming is active use Alt + left click to move the center frequency to the clicked point. +When the mouse is in the spectrum area but not over the center line of a channel it will zoom in/out along X (frequency) axis by a 0.5 step at each scroll up/down respectively between 1x (no zoom) and 10x. Note that in order to zoom on the center line of a channel you may move the mouse pointer in the top margin (center line moving is not active there but zooming is). + +When frequency zooming is active use Alt + left click to move the center frequency to the clicked point. + +
Power zooming
+ +When the mouse is inside the power scale (spectrum) the power range is decreased by 2 (zoom in) or increased by 2 (zoom in) at each wheel step forward or backward respectively. The behavior of the reference level depends on where in the scale is the mouse pointer: + + - in the top third: the reference level is maintained thus the reference level at the top stays the same + - in the middle third: the reference level is decreased by 1 (zoom in) or increased by 1 (zoom out) at each wheel step forward or backward thus the level in the middle stays the same + - in the bottom third: the reference level is decreased by 2 (zoom in) or increased by 2 (zoom out) at each wheel step forward or backward thus the level at the bottom stays the same + +
Time zooming
+ +When the mouse is inside the time scale (waterfall) the overlap is increased by 1 (zoom in) or decreased by 1 (zoom out) at each wheel step forward or backward respectively. Overlap is bounded by 0 and half of the FFT size minus one.

7. Status