mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-09-09 18:27:47 -04:00
Spectrum overlap fixes. Spectrum time and power zomming. Implements #779
This commit is contained in:
parent
076a4f6306
commit
043a76faf8
@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
MESSAGE_CLASS_DEFINITION(GLSpectrum::MsgReportSampleRate, Message)
|
MESSAGE_CLASS_DEFINITION(GLSpectrum::MsgReportSampleRate, Message)
|
||||||
MESSAGE_CLASS_DEFINITION(GLSpectrum::MsgReportWaterfallShare, 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;
|
const float GLSpectrum::m_maxFrequencyZoom = 10.0f;
|
||||||
|
|
||||||
@ -1315,20 +1317,20 @@ void GLSpectrum::applyChanges()
|
|||||||
m_waterfallHeight = 0;
|
m_waterfallHeight = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_invertedWaterfall)
|
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
|
|
||||||
{
|
{
|
||||||
histogramTop = m_topMargin;
|
histogramTop = m_topMargin;
|
||||||
m_histogramHeight = height() - m_topMargin - m_waterfallHeight - m_frequencyScaleHeight - m_bottomMargin;
|
m_histogramHeight = height() - m_topMargin - m_waterfallHeight - m_frequencyScaleHeight - m_bottomMargin;
|
||||||
waterfallTop = histogramTop + m_histogramHeight + m_frequencyScaleHeight + 1;
|
waterfallTop = histogramTop + m_histogramHeight + m_frequencyScaleHeight + 1;
|
||||||
frequencyScaleTop = histogramTop + m_histogramHeight + 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);
|
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 scaleDiv = ((float)m_sampleRate / (float)m_timingRate) * (m_ssbSpectrum ? 2 : 1);
|
||||||
float halfFFTSize = m_fftSize / 2;
|
float halfFFTSize = m_fftSize / 2;
|
||||||
scaleDiv *= halfFFTSize / (halfFFTSize - m_fftOverlap);
|
|
||||||
|
if (halfFFTSize > m_fftOverlap) {
|
||||||
|
scaleDiv *= halfFFTSize / (halfFFTSize - m_fftOverlap);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_invertedWaterfall) {
|
if (!m_invertedWaterfall) {
|
||||||
m_timeScale.setRange(m_timingRate > 1 ? Unit::TimeHMS : Unit::Time, (m_waterfallHeight * m_fftSize) / scaleDiv, 0);
|
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 scaleDiv = ((float)m_sampleRate / (float)m_timingRate) * (m_ssbSpectrum ? 2 : 1);
|
||||||
float halfFFTSize = m_fftSize / 2;
|
float halfFFTSize = m_fftSize / 2;
|
||||||
scaleDiv *= halfFFTSize / (halfFFTSize - m_fftOverlap);
|
|
||||||
|
if (halfFFTSize > m_fftOverlap) {
|
||||||
|
scaleDiv *= halfFFTSize / (halfFFTSize - m_fftOverlap);
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_invertedWaterfall) {
|
if (!m_invertedWaterfall) {
|
||||||
m_timeScale.setRange(m_timingRate > 1 ? Unit::TimeHMS : Unit::Time, (m_waterfallHeight * m_fftSize) / scaleDiv, 0);
|
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)
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
|
||||||
const QPointF& p = event->position();
|
const QPointF& p = event->position();
|
||||||
@ -2286,30 +2294,63 @@ void GLSpectrum::frequencyZoom(QWheelEvent *event)
|
|||||||
const QPointF& p = event->pos();
|
const QPointF& p = event->pos();
|
||||||
#endif
|
#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) {
|
if (event->delta() > 0) // zoom in
|
||||||
m_frequencyZoomFactor += 0.5f;
|
{
|
||||||
} else {
|
if (m_frequencyZoomFactor < m_maxFrequencyZoom) {
|
||||||
return;
|
m_frequencyZoomFactor += 0.5f;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (m_frequencyZoomFactor > 1.0f) {
|
||||||
|
m_frequencyZoomFactor -= 0.5f;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frequencyZoom(pwx);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_frequencyZoomFactor > 1.0f) {
|
float pwyh, pwyw;
|
||||||
m_frequencyZoomFactor -= 0.5f;
|
|
||||||
} else {
|
if (m_invertedWaterfall) // histo on top
|
||||||
return;
|
{
|
||||||
|
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
|
void GLSpectrum::frequencyZoom(float pw)
|
||||||
pw = pw < 0.0f ? 0.0f : pw > 1.0f ? 1.0 : pw;
|
{
|
||||||
m_frequencyZoomPos += (pw - 0.5f) * (1.0f / m_frequencyZoomFactor);
|
m_frequencyZoomPos += (pw - 0.5f) * (1.0f / m_frequencyZoomFactor);
|
||||||
float lim = 0.5f / m_frequencyZoomFactor;
|
float lim = 0.5f / m_frequencyZoomFactor;
|
||||||
m_frequencyZoomPos = m_frequencyZoomPos < lim ? lim : m_frequencyZoomPos > 1 - lim ? 1 - lim : m_frequencyZoomPos;
|
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();
|
updateFFTLimits();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2331,6 +2372,47 @@ void GLSpectrum::frequencyPan(QMouseEvent *event)
|
|||||||
updateFFTLimits();
|
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()
|
void GLSpectrum::resetFrequencyZoom()
|
||||||
{
|
{
|
||||||
m_frequencyZoomFactor = 1.0f;
|
m_frequencyZoomFactor = 1.0f;
|
||||||
@ -2421,7 +2503,7 @@ void GLSpectrum::channelMarkerMove(QWheelEvent *event, int mul)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
frequencyZoom(event);
|
zoom(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSpectrum::enterEvent(QEvent* event)
|
void GLSpectrum::enterEvent(QEvent* event)
|
||||||
|
@ -75,6 +75,39 @@ public:
|
|||||||
Real m_waterfallShare;
|
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);
|
GLSpectrum(QWidget* parent = nullptr);
|
||||||
virtual ~GLSpectrum();
|
virtual ~GLSpectrum();
|
||||||
|
|
||||||
@ -349,8 +382,11 @@ private:
|
|||||||
void mouseReleaseEvent(QMouseEvent* event);
|
void mouseReleaseEvent(QMouseEvent* event);
|
||||||
void wheelEvent(QWheelEvent*);
|
void wheelEvent(QWheelEvent*);
|
||||||
void channelMarkerMove(QWheelEvent*, int mul);
|
void channelMarkerMove(QWheelEvent*, int mul);
|
||||||
void frequencyZoom(QWheelEvent*);
|
void zoom(QWheelEvent*);
|
||||||
|
void frequencyZoom(float pw);
|
||||||
void frequencyPan(QMouseEvent*);
|
void frequencyPan(QMouseEvent*);
|
||||||
|
void timeZoom(bool zoomInElseOut);
|
||||||
|
void powerZoom(float pw, bool zoomInElseOut);
|
||||||
void resetFrequencyZoom();
|
void resetFrequencyZoom();
|
||||||
void updateFFTLimits();
|
void updateFFTLimits();
|
||||||
void setFrequencyScale();
|
void setFrequencyScale();
|
||||||
|
@ -630,6 +630,29 @@ bool GLSpectrumGUI::handleMessage(const Message& message)
|
|||||||
{
|
{
|
||||||
const GLSpectrum::MsgReportWaterfallShare& report = (const GLSpectrum::MsgReportWaterfallShare&) message;
|
const GLSpectrum::MsgReportWaterfallShare& report = (const GLSpectrum::MsgReportWaterfallShare&) message;
|
||||||
m_settings.m_waterfallShare = report.getWaterfallShare();
|
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))
|
else if (SpectrumVis::MsgStartStop::match(message))
|
||||||
{
|
{
|
||||||
|
@ -973,15 +973,31 @@ Any change in the spectrum settings is not reflected in the markers. You have to
|
|||||||
|
|
||||||
<h4>Mouse scroll wheel</h4>
|
<h4>Mouse scroll wheel</h4>
|
||||||
|
|
||||||
|
<h5>Channel moving</h5>
|
||||||
|
|
||||||
When the mouse is over the center line of a channel:
|
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
|
- 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 Ctrl it will move the channel by +/- 100 Hz
|
||||||
- combined with Shift it will move the channel by +/- 1 kHz
|
- 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).
|
<h5>Frequency zooming</h5>
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
<h5>Power zooming</h5>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
<h5>Time zooming</h5>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
<h3>7. Status</h3>
|
<h3>7. Status</h3>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user