1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 09:48:45 -05:00

Merge pull request #1454 from srcejon/measurements_widget

Measurements widget
This commit is contained in:
Edouard Griffiths 2022-09-29 21:52:27 +02:00 committed by GitHub
commit 947eee4b29
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 6862 additions and 5304 deletions

View File

@ -69,11 +69,15 @@ void SpectrumSettings::resetToDefaults()
m_colorMap = "Angel"; m_colorMap = "Angel";
m_spectrumStyle = Line; m_spectrumStyle = Line;
m_measurement = MeasurementNone; m_measurement = MeasurementNone;
m_measurementCenterFrequencyOffset = 0;
m_measurementBandwidth = 10000; m_measurementBandwidth = 10000;
m_measurementChSpacing = 10000; m_measurementChSpacing = 10000;
m_measurementAdjChBandwidth = 10000; m_measurementAdjChBandwidth = 10000;
m_measurementHarmonics = 5; m_measurementHarmonics = 5;
m_measurementPeaks = 5;
m_measurementHighlight = true; m_measurementHighlight = true;
m_measurementsPosition = PositionBelow;
m_measurementPrecision = 1;
} }
QByteArray SpectrumSettings::serialize() const QByteArray SpectrumSettings::serialize() const
@ -120,6 +124,10 @@ QByteArray SpectrumSettings::serialize() const
s.writeS32(39, m_measurementHarmonics); s.writeS32(39, m_measurementHarmonics);
// 41, 42 used below // 41, 42 used below
s.writeBool(42, m_measurementHighlight); s.writeBool(42, m_measurementHighlight);
s.writeS32(43, m_measurementPeaks);
s.writeS32(44, (int)m_measurementsPosition);
s.writeS32(45, m_measurementPrecision);
s.writeS32(46, m_measurementCenterFrequencyOffset);
s.writeS32(100, m_histogramMarkers.size()); s.writeS32(100, m_histogramMarkers.size());
for (int i = 0; i < m_histogramMarkers.size(); i++) { for (int i = 0; i < m_histogramMarkers.size(); i++) {
@ -227,6 +235,10 @@ bool SpectrumSettings::deserialize(const QByteArray& data)
d.readS32(38, &m_measurementAdjChBandwidth, 10000); d.readS32(38, &m_measurementAdjChBandwidth, 10000);
d.readS32(39, &m_measurementHarmonics, 5); d.readS32(39, &m_measurementHarmonics, 5);
d.readBool(42, &m_measurementHighlight, true); d.readBool(42, &m_measurementHighlight, true);
d.readS32(43, &m_measurementPeaks, 5);
d.readS32(44, (int*)&m_measurementsPosition, (int)PositionBelow);
d.readS32(45, &m_measurementPrecision, 1);
d.readS32(46, &m_measurementCenterFrequencyOffset, 0);
int histogramMarkersSize; int histogramMarkersSize;
d.readS32(100, &histogramMarkersSize, 0); d.readS32(100, &histogramMarkersSize, 0);

View File

@ -73,15 +73,17 @@ public:
enum Measurement enum Measurement
{ {
MeasurementNone, MeasurementNone,
MeasurementPeak, MeasurementPeaks,
MeasurementChannelPower, MeasurementChannelPower,
MeasurementAdjacentChannelPower, MeasurementAdjacentChannelPower,
MeasurementSNR, MeasurementSNR
MeasurementSNFR, };
MeasurementTHD,
MeasurementTHDPN, enum MeasurementsPosition {
MeasurementSINAD, PositionAbove,
MeasurementSFDR PositionBelow,
PositionLeft,
PositionRight
}; };
int m_fftSize; int m_fftSize;
@ -123,11 +125,16 @@ public:
QString m_colorMap; QString m_colorMap;
SpectrumStyle m_spectrumStyle; SpectrumStyle m_spectrumStyle;
Measurement m_measurement; Measurement m_measurement;
int m_measurementCenterFrequencyOffset;
int m_measurementBandwidth; int m_measurementBandwidth;
int m_measurementChSpacing; int m_measurementChSpacing;
int m_measurementAdjChBandwidth; int m_measurementAdjChBandwidth;
int m_measurementHarmonics; int m_measurementHarmonics;
int m_measurementPeaks;
bool m_measurementHighlight; bool m_measurementHighlight;
MeasurementsPosition m_measurementsPosition;
int m_measurementPrecision;
static const int m_log2FFTSizeMin = 6; // 64 static const int m_log2FFTSizeMin = 6; // 64
static const int m_log2FFTSizeMax = 15; // 32k static const int m_log2FFTSizeMax = 15; // 32k

View File

@ -51,6 +51,7 @@ set(sdrgui_SOURCES
gui/glshadertvarray.cpp gui/glshadertvarray.cpp
gui/glspectrum.cpp gui/glspectrum.cpp
gui/glspectrumgui.cpp gui/glspectrumgui.cpp
gui/glspectrumview.cpp
gui/graphicsdialog.cpp gui/graphicsdialog.cpp
gui/graphicsviewzoom.cpp gui/graphicsviewzoom.cpp
gui/httpdownloadmanagergui.cpp gui/httpdownloadmanagergui.cpp
@ -69,6 +70,8 @@ set(sdrgui_SOURCES
gui/sdrangelsplash.cpp gui/sdrangelsplash.cpp
gui/spectrumcalibrationpointsdialog.cpp gui/spectrumcalibrationpointsdialog.cpp
gui/spectrummarkersdialog.cpp gui/spectrummarkersdialog.cpp
gui/spectrummeasurementsdialog.cpp
gui/spectrummeasurements.cpp
gui/tickedslider.cpp gui/tickedslider.cpp
gui/timedelegate.cpp gui/timedelegate.cpp
gui/transverterbutton.cpp gui/transverterbutton.cpp
@ -154,6 +157,7 @@ set(sdrgui_HEADERS
gui/glshadertextured.h gui/glshadertextured.h
gui/glspectrum.h gui/glspectrum.h
gui/glspectrumgui.h gui/glspectrumgui.h
gui/glspectrumview.h
gui/graphicsdialog.h gui/graphicsdialog.h
gui/graphicsviewzoom.h gui/graphicsviewzoom.h
gui/httpdownloadmanagergui.h gui/httpdownloadmanagergui.h
@ -173,6 +177,8 @@ set(sdrgui_HEADERS
gui/sdrangelsplash.h gui/sdrangelsplash.h
gui/spectrumcalibrationpointsdialog.h gui/spectrumcalibrationpointsdialog.h
gui/spectrummarkersdialog.h gui/spectrummarkersdialog.h
gui/spectrummeasurementsdialog.h
gui/spectrummeasurements.h
gui/tickedslider.h gui/tickedslider.h
gui/timedelegate.h gui/timedelegate.h
gui/transverterbutton.h gui/transverterbutton.h
@ -239,6 +245,7 @@ set(sdrgui_FORMS
gui/samplingdevicecontrol.ui gui/samplingdevicecontrol.ui
gui/samplingdevicedialog.ui gui/samplingdevicedialog.ui
gui/spectrummarkersdialog.ui gui/spectrummarkersdialog.ui
gui/spectrummeasurementsdialog.ui
gui/spectrumcalibrationpointsdialog.ui gui/spectrumcalibrationpointsdialog.ui
gui/myposdialog.ui gui/myposdialog.ui
gui/transverterdialog.ui gui/transverterdialog.ui

View File

@ -23,6 +23,7 @@
#include "dsp/dspdevicesourceengine.h" #include "dsp/dspdevicesourceengine.h"
#include "dsp/dspdevicesinkengine.h" #include "dsp/dspdevicesinkengine.h"
#include "gui/glspectrum.h" #include "gui/glspectrum.h"
#include "gui/glspectrumview.h"
#include "gui/glspectrumgui.h" #include "gui/glspectrumgui.h"
// #include "gui/channelwindow.h" // #include "gui/channelwindow.h"
#include "gui/workspace.h" #include "gui/workspace.h"
@ -42,7 +43,7 @@
DeviceUISet::DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet) DeviceUISet::DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet)
{ {
m_spectrum = new GLSpectrum; m_spectrum = new GLSpectrum();
m_spectrum->setIsDeviceSpectrum(true); m_spectrum->setIsDeviceSpectrum(true);
m_spectrumVis = deviceSet->m_spectrumVis; m_spectrumVis = deviceSet->m_spectrumVis;
m_spectrumVis->setGLSpectrum(m_spectrum); m_spectrumVis->setGLSpectrum(m_spectrum);

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,5 @@
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 F4EXB // // Copyright (C) 2022 Jon Beniston, M7RCE //
// written by Edouard Griffiths //
// //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// // // //
// This program is free software; you can redistribute it and/or modify // // This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by // // it under the terms of the GNU General Public License as published by //
@ -19,465 +15,102 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // // along with this program. If not, see <http://www.gnu.org/licenses/>. //
/////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_GLSPECTRUM_H #ifndef SDRGUI_GLSPECTRUM_H_
#define INCLUDE_GLSPECTRUM_H #define SDRGUI_GLSPECTRUM_H_
#include <QWidget>
#include <QTimer>
#include <QMutex>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QMatrix4x4>
#include <QPoint>
#include <QOpenGLWidget>
#include <QOpenGLDebugLogger>
#include "gui/scaleengine.h"
#include "gui/glshadersimple.h"
#include "gui/glshadertextured.h"
#include "gui/glshadercolormap.h"
#include "dsp/glspectruminterface.h"
#include "gui/glshaderspectrogram.h"
#include "dsp/spectrummarkers.h"
#include "dsp/channelmarker.h"
#include "dsp/spectrumsettings.h"
#include "export.h" #include "export.h"
#include "util/incrementalarray.h" #include "glspectrumview.h"
#include "util/message.h"
#include "util/colormap.h"
class QOpenGLShaderProgram; class QSplitter;
class MessageQueue; class SpectrumMeasurements;
class SpectrumVis;
class QOpenGLDebugLogger;
class SDRGUI_API GLSpectrum : public QOpenGLWidget, public GLSpectrumInterface { // Combines GLSpectrumView with SpectrumMeasurements in a QSplitter
class SDRGUI_API GLSpectrum : public QWidget, public GLSpectrumInterface {
Q_OBJECT Q_OBJECT
public: public:
class MsgReportSampleRate : public Message { GLSpectrum(QWidget *parent = nullptr);
MESSAGE_CLASS_DECLARATION GLSpectrumView *getSpectrumView() const { return m_spectrum; }
SpectrumMeasurements *getMeasurements() const { return m_measurements; }
void setMeasurementsVisible(bool visible);
void setMeasurementsPosition(SpectrumSettings::MeasurementsPosition position);
public: void setCenterFrequency(qint64 frequency) { m_spectrum->setCenterFrequency(frequency); }
MsgReportSampleRate(quint32 sampleRate) : qint64 getCenterFrequency() const { return m_spectrum->getCenterFrequency(); }
Message(), float getPowerMax() const { return m_spectrum->getPowerMax(); }
m_sampleRate(sampleRate) float getTimeMax() const { return m_spectrum->getTimeMax(); }
{} void setSampleRate(qint32 sampleRate) { m_spectrum->setSampleRate(sampleRate); }
void setTimingRate(qint32 timingRate) { m_spectrum->setTimingRate(timingRate); }
quint32 getSampleRate() const { return m_sampleRate; } void setFFTOverlap(int overlap) { m_spectrum->setFFTOverlap(overlap); }
void setReferenceLevel(Real referenceLevel) { m_spectrum->setReferenceLevel(referenceLevel); }
private: void setPowerRange(Real powerRange){ m_spectrum->setPowerRange(powerRange); }
quint32 m_sampleRate; void setDecay(int decay) { m_spectrum->setDecay(decay); }
}; void setDecayDivisor(int decayDivisor) { m_spectrum->setDecayDivisor(decayDivisor); }
void setHistoStroke(int stroke) { m_spectrum->setHistoStroke(stroke); }
class MsgReportWaterfallShare : public Message { void setDisplayWaterfall(bool display) { m_spectrum->setDisplayWaterfall(display); }
MESSAGE_CLASS_DECLARATION void setDisplay3DSpectrogram(bool display) { m_spectrum->setDisplay3DSpectrogram(display); }
void set3DSpectrogramStyle(SpectrumSettings::SpectrogramStyle style) { m_spectrum->set3DSpectrogramStyle(style); }
public: void setColorMapName(const QString &colorMapName) { m_spectrum->setColorMapName(colorMapName); }
MsgReportWaterfallShare(Real waterfallShare) : void setSpectrumStyle(SpectrumSettings::SpectrumStyle style) { m_spectrum->setSpectrumStyle(style); }
Message(), void setSsbSpectrum(bool ssbSpectrum) { m_spectrum->setSsbSpectrum(ssbSpectrum); }
m_waterfallShare(waterfallShare) void setLsbDisplay(bool lsbDisplay) { m_spectrum->setLsbDisplay(lsbDisplay); }
{} void setInvertedWaterfall(bool inv) { m_spectrum->setInvertedWaterfall(inv); }
void setDisplayMaxHold(bool display) { m_spectrum->setDisplayMaxHold(display); }
Real getWaterfallShare() const { return m_waterfallShare; } void setDisplayCurrent(bool display) { m_spectrum->setDisplayCurrent(display); }
void setDisplayHistogram(bool display) { m_spectrum->setDisplayHistogram(display); }
private: void setDisplayGrid(bool display) { m_spectrum->setDisplayGrid(display); }
Real m_waterfallShare; void setDisplayGridIntensity(int intensity) { m_spectrum->setDisplayGridIntensity(intensity); }
}; void setDisplayTraceIntensity(int intensity) { m_spectrum->setDisplayTraceIntensity(intensity); }
void setLinear(bool linear) { m_spectrum->setLinear(linear); }
class MsgReportFFTOverlap : public Message { void setUseCalibration(bool useCalibration) { m_spectrum->setUseCalibration(useCalibration); }
MESSAGE_CLASS_DECLARATION void setMeasurementParams(SpectrumSettings::Measurement measurement,
int centerFrequencyOffset, int bandwidth, int chSpacing, int adjChBandwidth,
public: int harmonics, int peaks, bool highlight, int precision)
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;
};
class MsgReportCalibrationShift : public Message {
MESSAGE_CLASS_DECLARATION
public:
MsgReportCalibrationShift(Real calibrationShiftdB) :
Message(),
m_calibrationShiftdB(calibrationShiftdB)
{}
Real getCalibrationShiftdB() const { return m_calibrationShiftdB; }
private:
Real m_calibrationShiftdB;
};
GLSpectrum(QWidget* parent = nullptr);
virtual ~GLSpectrum();
void setCenterFrequency(qint64 frequency);
qint64 getCenterFrequency() const { return m_centerFrequency; }
float getPowerMax() const;
float getTimeMax() const;
void setSampleRate(qint32 sampleRate);
void setTimingRate(qint32 timingRate);
void setFFTOverlap(int overlap);
void setReferenceLevel(Real referenceLevel);
void setPowerRange(Real powerRange);
void setDecay(int decay);
void setDecayDivisor(int decayDivisor);
void setHistoStroke(int stroke);
void setDisplayWaterfall(bool display);
void setDisplay3DSpectrogram(bool display);
void set3DSpectrogramStyle(SpectrumSettings::SpectrogramStyle style);
void setColorMapName(const QString &colorMapName);
void setSpectrumStyle(SpectrumSettings::SpectrumStyle style);
void setSsbSpectrum(bool ssbSpectrum);
void setLsbDisplay(bool lsbDisplay);
void setInvertedWaterfall(bool inv);
void setDisplayMaxHold(bool display);
void setDisplayCurrent(bool display);
void setDisplayHistogram(bool display);
void setDisplayGrid(bool display);
void setDisplayGridIntensity(int intensity);
void setDisplayTraceIntensity(int intensity);
void setLinear(bool linear);
void setUseCalibration(bool useCalibration);
void setMeasurementParams(SpectrumSettings::Measurement measurement, int bandwidth,
int chSpacing, int adjChBandwidth,
int harmonics, bool highlight);
qint32 getSampleRate() const { return m_sampleRate; }
void addChannelMarker(ChannelMarker* channelMarker);
void removeChannelMarker(ChannelMarker* channelMarker);
void setMessageQueueToGUI(MessageQueue* messageQueue) { m_messageQueueToGUI = messageQueue; }
virtual void newSpectrum(const Real* spectrum, int nbBins, int fftSize);
void clearSpectrumHistogram();
Real getWaterfallShare() const { return m_waterfallShare; }
void setWaterfallShare(Real waterfallShare);
void setFPSPeriodMs(int fpsPeriodMs);
void setDisplayedStream(bool sourceOrSink, int streamIndex)
{ {
m_displaySourceOrSink = sourceOrSink; m_spectrum->setMeasurementParams(measurement, centerFrequencyOffset, bandwidth, chSpacing, adjChBandwidth, harmonics, peaks, highlight, precision);
m_displayStreamIndex = streamIndex;
} }
void setSpectrumVis(SpectrumVis *spectrumVis) { m_spectrumVis = spectrumVis; } qint32 getSampleRate() const { return m_spectrum->getSampleRate(); }
SpectrumVis *getSpectrumVis() { return m_spectrumVis; } void addChannelMarker(ChannelMarker* channelMarker) { m_spectrum->addChannelMarker(channelMarker); }
const QList<SpectrumHistogramMarker>& getHistogramMarkers() const { return m_histogramMarkers; } void removeChannelMarker(ChannelMarker* channelMarker) { m_spectrum->removeChannelMarker(channelMarker); }
QList<SpectrumHistogramMarker>& getHistogramMarkers() { return m_histogramMarkers; } void setMessageQueueToGUI(MessageQueue* messageQueue) { m_spectrum->setMessageQueueToGUI(messageQueue); }
void setHistogramMarkers(const QList<SpectrumHistogramMarker>& histogramMarkers); void newSpectrum(const Real* spectrum, int nbBins, int fftSize) { m_spectrum->newSpectrum(spectrum, nbBins, fftSize); }
const QList<SpectrumWaterfallMarker>& getWaterfallMarkers() const { return m_waterfallMarkers; } void clearSpectrumHistogram() { m_spectrum->clearSpectrumHistogram(); }
QList<SpectrumWaterfallMarker>& getWaterfallMarkers() { return m_waterfallMarkers; } Real getWaterfallShare() const { return m_spectrum->getWaterfallShare(); }
void setWaterfallMarkers(const QList<SpectrumWaterfallMarker>& waterfallMarkers); void setWaterfallShare(Real waterfallShare) { m_spectrum->setWaterfallShare(waterfallShare); }
const QList<SpectrumAnnotationMarker>& getAnnotationMarkers() const { return m_annotationMarkers; } void setFPSPeriodMs(int fpsPeriodMs) { m_spectrum->setFPSPeriodMs(fpsPeriodMs); }
QList<SpectrumAnnotationMarker>& getAnnotationMarkers() { return m_annotationMarkers; } void setDisplayedStream(bool sourceOrSink, int streamIndex) { m_spectrum->setDisplayedStream(sourceOrSink, streamIndex); }
void setAnnotationMarkers(const QList<SpectrumAnnotationMarker>& annotationMarkers); void setSpectrumVis(SpectrumVis *spectrumVis) { m_spectrum->setSpectrumVis(spectrumVis); }
void updateHistogramMarkers(); SpectrumVis *getSpectrumVis() { return m_spectrum->getSpectrumVis(); }
void updateWaterfallMarkers(); const QList<SpectrumHistogramMarker>& getHistogramMarkers() const { return m_spectrum->getHistogramMarkers(); }
void updateAnnotationMarkers(); QList<SpectrumHistogramMarker>& getHistogramMarkers() { return m_spectrum->getHistogramMarkers(); }
void updateMarkersDisplay(); void setHistogramMarkers(const QList<SpectrumHistogramMarker>& histogramMarkers) { m_spectrum->setHistogramMarkers(histogramMarkers); }
void updateCalibrationPoints(); const QList<SpectrumWaterfallMarker>& getWaterfallMarkers() const { return m_spectrum->getWaterfallMarkers(); }
SpectrumSettings::MarkersDisplay& getMarkersDisplay() { return m_markersDisplay; } QList<SpectrumWaterfallMarker>& getWaterfallMarkers() { return m_spectrum->getWaterfallMarkers(); }
void setMarkersDisplay(SpectrumSettings::MarkersDisplay markersDisplay); void setWaterfallMarkers(const QList<SpectrumWaterfallMarker>& waterfallMarkers) { m_spectrum->setWaterfallMarkers(waterfallMarkers); }
QList<SpectrumCalibrationPoint>& getCalibrationPoints() { return m_calibrationPoints; } const QList<SpectrumAnnotationMarker>& getAnnotationMarkers() const { return m_spectrum->getAnnotationMarkers(); }
void setCalibrationPoints(const QList<SpectrumCalibrationPoint>& calibrationPoints); QList<SpectrumAnnotationMarker>& getAnnotationMarkers() { return m_spectrum->getAnnotationMarkers(); }
SpectrumSettings::CalibrationInterpolationMode& getCalibrationInterpMode() { return m_calibrationInterpMode; } void setAnnotationMarkers(const QList<SpectrumAnnotationMarker>& annotationMarkers) { m_spectrum->setAnnotationMarkers(annotationMarkers); };
void setCalibrationInterpMode(SpectrumSettings::CalibrationInterpolationMode mode); void updateHistogramMarkers() { m_spectrum->updateHistogramMarkers(); }
void setIsDeviceSpectrum(bool isDeviceSpectrum) { m_isDeviceSpectrum = isDeviceSpectrum; } void updateWaterfallMarkers() { m_spectrum->updateWaterfallMarkers(); }
bool isDeviceSpectrum() const { return m_isDeviceSpectrum; } void updateAnnotationMarkers() { m_spectrum->updateAnnotationMarkers();}
void updateMarkersDisplay() { m_spectrum->updateMarkersDisplay(); }
void updateCalibrationPoints() { m_spectrum->updateCalibrationPoints(); }
SpectrumSettings::MarkersDisplay& getMarkersDisplay() { return m_spectrum->getMarkersDisplay(); }
void setMarkersDisplay(SpectrumSettings::MarkersDisplay markersDisplay) { m_spectrum->setMarkersDisplay(markersDisplay); }
QList<SpectrumCalibrationPoint>& getCalibrationPoints() { return m_spectrum->getCalibrationPoints(); }
void setCalibrationPoints(const QList<SpectrumCalibrationPoint>& calibrationPoints) { m_spectrum->setCalibrationPoints(calibrationPoints); }
SpectrumSettings::CalibrationInterpolationMode& getCalibrationInterpMode() { return m_spectrum->getCalibrationInterpMode(); }
void setCalibrationInterpMode(SpectrumSettings::CalibrationInterpolationMode mode) { m_spectrum->setCalibrationInterpMode(mode); }
void setIsDeviceSpectrum(bool isDeviceSpectrum) { m_spectrum->setIsDeviceSpectrum(isDeviceSpectrum); }
bool isDeviceSpectrum() const { return m_spectrum->isDeviceSpectrum(); }
private: private:
struct ChannelMarkerState { QSplitter *m_splitter;
ChannelMarker* m_channelMarker; GLSpectrumView *m_spectrum;
QMatrix4x4 m_glMatrixWaterfall; SpectrumMeasurements *m_measurements;
QMatrix4x4 m_glMatrixDsbWaterfall;
QMatrix4x4 m_glMatrixFreqScale;
QMatrix4x4 m_glMatrixDsbFreqScale;
QMatrix4x4 m_glMatrixHistogram;
QMatrix4x4 m_glMatrixDsbHistogram;
QRectF m_rect;
ChannelMarkerState(ChannelMarker* channelMarker) :
m_channelMarker(channelMarker)
{ }
};
QList<ChannelMarkerState*> m_channelMarkerStates;
enum CursorState {
CSNormal,
CSSplitter,
CSSplitterMoving,
CSChannel,
CSChannelMoving
};
QList<SpectrumHistogramMarker> m_histogramMarkers;
QList<SpectrumWaterfallMarker> m_waterfallMarkers;
QList<SpectrumAnnotationMarker> m_annotationMarkers;
QList<SpectrumAnnotationMarker*> m_sortedAnnotationMarkers;
QList<SpectrumAnnotationMarker*> m_visibleAnnotationMarkers;
SpectrumSettings::MarkersDisplay m_markersDisplay;
QList<SpectrumCalibrationPoint> m_calibrationPoints;
CursorState m_cursorState;
int m_cursorChannel;
SpectrumVis* m_spectrumVis;
QTimer m_timer;
int m_fpsPeriodMs;
QMutex m_mutex;
bool m_mouseInside;
bool m_changesPending;
qint64 m_centerFrequency;
Real m_referenceLevel;
Real m_powerRange;
bool m_linear;
int m_decay;
quint32 m_sampleRate;
quint32 m_timingRate;
int m_fftOverlap;
int m_fftSize; //!< FFT size in number of bins
int m_nbBins; //!< Number of visible FFT bins (zoom support)
bool m_displayGrid;
int m_displayGridIntensity;
int m_displayTraceIntensity;
bool m_invertedWaterfall;
std::vector<Real> m_maxHold;
bool m_displayMaxHold;
const Real *m_currentSpectrum;
bool m_displayCurrent;
Real m_waterfallShare;
int m_leftMargin;
int m_rightMargin;
int m_topMargin;
int m_frequencyScaleHeight;
int m_infoHeight;
int m_histogramHeight;
int m_waterfallHeight;
int m_bottomMargin;
QFont m_textOverlayFont;
QPixmap m_leftMarginPixmap;
QPixmap m_frequencyPixmap;
QPixmap m_infoPixmap;
ScaleEngine m_timeScale;
ScaleEngine m_powerScale;
ScaleEngine m_frequencyScale;
QRectF m_histogramRect;
QRect m_frequencyScaleRect;
QRectF m_waterfallRect;
QRect m_infoRect;
QMatrix4x4 m_glFrequencyScaleBoxMatrix;
QMatrix4x4 m_glLeftScaleBoxMatrix;
QMatrix4x4 m_glInfoBoxMatrix;
QString m_peakFrequencyMaxStr;
QString m_peakPowerMaxStr;
QString m_peakPowerUnits;
QRgb m_waterfallPalette[240];
QImage* m_waterfallBuffer;
int m_waterfallBufferPos;
int m_waterfallTextureHeight;
int m_waterfallTexturePos;
QMatrix4x4 m_glWaterfallBoxMatrix;
bool m_displayWaterfall;
bool m_ssbSpectrum;
bool m_lsbDisplay;
QImage* m_3DSpectrogramBuffer;
int m_3DSpectrogramBufferPos;
int m_3DSpectrogramTextureHeight;
int m_3DSpectrogramTexturePos;
bool m_display3DSpectrogram;
bool m_rotate3DSpectrogram; //!< Set when mouse is pressed in 3D spectrogram area for rotation when mouse is moved
bool m_pan3DSpectrogram;
bool m_scaleZ3DSpectrogram;
QPointF m_mousePrevLocalPos; //!< Position of the mouse for last event
int m_3DSpectrogramBottom;
QPixmap m_spectrogramTimePixmap;
QPixmap m_spectrogramPowerPixmap;
SpectrumSettings::SpectrogramStyle m_3DSpectrogramStyle;
QString m_colorMapName;
SpectrumSettings::SpectrumStyle m_spectrumStyle;
const float *m_colorMap;
bool m_scrollFrequency;
qint64 m_scrollStartCenterFreq;
QRgb m_histogramPalette[240];
QImage* m_histogramBuffer;
quint8* m_histogram; //!< Spectrum phosphor matrix of FFT width and PSD height scaled to 100. values [0..239]
int m_decayDivisor;
int m_decayDivisorCount;
int m_histogramStroke;
QMatrix4x4 m_glHistogramSpectrumMatrix;
QMatrix4x4 m_glHistogramBoxMatrix;
bool m_displayHistogram;
bool m_displayChanged;
bool m_displaySourceOrSink;
int m_displayStreamIndex;
float m_frequencyZoomFactor;
float m_frequencyZoomPos;
static const float m_maxFrequencyZoom;
static const float m_annotationMarkerHeight;
GLShaderSimple m_glShaderSimple;
GLShaderTextured m_glShaderLeftScale;
GLShaderTextured m_glShaderFrequencyScale;
GLShaderTextured m_glShaderWaterfall;
GLShaderTextured m_glShaderHistogram;
GLShaderColorMap m_glShaderColorMap;
GLShaderTextured m_glShaderTextOverlay;
GLShaderTextured m_glShaderInfo;
GLShaderSpectrogram m_glShaderSpectrogram;
GLShaderTextured m_glShaderSpectrogramTimeScale;
GLShaderTextured m_glShaderSpectrogramPowerScale;
int m_matrixLoc;
int m_colorLoc;
bool m_useCalibration;
Real m_calibrationGain;
Real m_calibrationShiftdB;
SpectrumSettings::CalibrationInterpolationMode m_calibrationInterpMode;
IncrementalArray<GLfloat> m_q3TickTime;
IncrementalArray<GLfloat> m_q3TickFrequency;
IncrementalArray<GLfloat> m_q3TickPower;
IncrementalArray<GLfloat> m_q3FFT;
IncrementalArray<GLfloat> m_q3ColorMap;
MessageQueue *m_messageQueueToGUI;
QOpenGLDebugLogger *m_openGLLogger;
bool m_isDeviceSpectrum;
SpectrumSettings::Measurement m_measurement;
int m_measurementBandwidth;
int m_measurementChSpacing;
int m_measurementAdjChBandwidth;
int m_measurementHarmonics;
bool m_measurementHighlight;
static const QVector4D m_measurementLightMarkerColor;
static const QVector4D m_measurementDarkMarkerColor;
void updateWaterfall(const Real *spectrum);
void update3DSpectrogram(const Real *spectrum);
void updateHistogram(const Real *spectrum);
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
void drawPowerBandMarkers(float max, float min, const QVector4D &color);
void drawBandwidthMarkers(int64_t centerFrequency, int bandwidth, const QVector4D &color);
void drawPeakMarkers(int64_t startFrequency, int64_t endFrequency, const QVector4D &color);
void drawSpectrumMarkers();
void drawAnnotationMarkers();
void measurePeak();
void measureChannelPower();
void measureAdjacentChannelPower();
void measureSNR();
void measureSFDR();
float calcChannelPower(int64_t centerFrequency, int channelBandwidth) const;
float calPower(float power) const;
int findPeakBin() const;
void findPeak(float &power, float &frequency) const;
void peakWidth(int center, int &left, int &right, int maxLeft, int maxRight) const;
int frequencyToBin(int64_t frequency) const;
int64_t binToFrequency(int bin) const;
void stopDrag();
void applyChanges();
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void wheelEvent(QWheelEvent*);
void channelMarkerMove(QWheelEvent*, int mul);
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();
void setPowerScale(int height);
void getFrequencyZoom(int64_t& centerFrequency, int& frequencySpan);
bool pointInWaterfallOrSpectrogram(const QPointF &point) const;
bool pointInHistogram(const QPointF &point) const;
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
static QString displayFull(int64_t value);
static QString displayScaled(int64_t value, char type, int precision, bool showMult);
static QString displayScaledF(float value, char type, int precision, bool showMult);
static QString displayPower(float value, char type, int precision);
int getPrecision(int value);
void drawTextRight(const QString &text, const QString &value, const QString &max, const QString &units);
void drawTextsRight(const QStringList &text, const QStringList &value, const QStringList &max, const QStringList &units);
void drawTextOverlay( //!< Draws a text overlay
const QString& text,
const QColor& color,
const QFont& font,
float shiftX,
float shiftY,
bool leftHalf,
bool topHalf,
const QRectF& glRect);
void formatTextInfo(QString& info);
void updateSortedAnnotationMarkers();
static bool annotationDisplayLessThan(const SpectrumAnnotationMarker *m1, const SpectrumAnnotationMarker *m2)
{
if (m1->m_bandwidth == m2->m_bandwidth) {
return m1->m_startFrequency < m2->m_startFrequency;
} else {
return m1->m_bandwidth > m2->m_bandwidth; // larger bandwidths should come first for display (lower layer)
}
}
static bool calibrationPointsLessThan(const SpectrumCalibrationPoint& m1, const SpectrumCalibrationPoint& m2)
{
return m1.m_frequency < m2.m_frequency;
}
private slots:
void cleanup();
void tick();
void channelMarkerChanged();
void channelMarkerDestroyed(QObject* object);
void openGLDebug(const QOpenGLDebugMessage &debugMessage);
bool eventFilter(QObject *object, QEvent *event);
signals:
// Emitted when user tries to scroll to frequency currently out of range
void requestCenterFrequency(qint64 frequency);
}; };
#endif // INCLUDE_GLSPECTRUM_H #endif // SDRGUI_GLSPECTRUM_H_

View File

@ -28,10 +28,13 @@
#include "dsp/fftwindow.h" #include "dsp/fftwindow.h"
#include "dsp/spectrumvis.h" #include "dsp/spectrumvis.h"
#include "gui/glspectrum.h" #include "gui/glspectrum.h"
#include "gui/glspectrumview.h"
#include "gui/crightclickenabler.h" #include "gui/crightclickenabler.h"
#include "gui/wsspectrumsettingsdialog.h" #include "gui/wsspectrumsettingsdialog.h"
#include "gui/spectrummarkersdialog.h" #include "gui/spectrummarkersdialog.h"
#include "gui/spectrumcalibrationpointsdialog.h" #include "gui/spectrumcalibrationpointsdialog.h"
#include "gui/spectrummeasurementsdialog.h"
#include "gui/spectrummeasurements.h"
#include "gui/flowlayout.h" #include "gui/flowlayout.h"
#include "util/colormap.h" #include "util/colormap.h"
#include "util/simpleserializer.h" #include "util/simpleserializer.h"
@ -51,7 +54,6 @@ GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) :
ui->setupUi(this); ui->setupUi(this);
// Use the custom flow layout for the 3 main horizontal layouts (lines) // Use the custom flow layout for the 3 main horizontal layouts (lines)
ui->verticalLayout->removeItem(ui->Line7Layout);
ui->verticalLayout->removeItem(ui->Line6Layout); ui->verticalLayout->removeItem(ui->Line6Layout);
ui->verticalLayout->removeItem(ui->Line5Layout); ui->verticalLayout->removeItem(ui->Line5Layout);
ui->verticalLayout->removeItem(ui->Line4Layout); ui->verticalLayout->removeItem(ui->Line4Layout);
@ -65,11 +67,10 @@ GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) :
flowLayout->addItem(ui->Line4Layout); flowLayout->addItem(ui->Line4Layout);
flowLayout->addItem(ui->Line5Layout); flowLayout->addItem(ui->Line5Layout);
flowLayout->addItem(ui->Line6Layout); flowLayout->addItem(ui->Line6Layout);
flowLayout->addItem(ui->Line7Layout);
ui->verticalLayout->addItem(flowLayout); ui->verticalLayout->addItem(flowLayout);
on_linscale_toggled(false); on_linscale_toggled(false);
on_measurement_currentIndexChanged(0); //displayMeasurementGUI();
QString levelStyle = QString( QString levelStyle = QString(
"QSpinBox {background-color: rgb(79, 79, 79);}" "QSpinBox {background-color: rgb(79, 79, 79);}"
@ -229,18 +230,13 @@ void GLSpectrumGUI::displaySettings()
ui->calibration->setChecked(m_settings.m_useCalibration); ui->calibration->setChecked(m_settings.m_useCalibration);
displayGotoMarkers(); displayGotoMarkers();
ui->measurement->setCurrentIndex((int) m_settings.m_measurement);
ui->highlight->setChecked(m_settings.m_measurementHighlight);
ui->bandwidth->setValue(m_settings.m_measurementBandwidth);
ui->chSpacing->setValue(m_settings.m_measurementChSpacing);
ui->adjChBandwidth->setValue(m_settings.m_measurementAdjChBandwidth);
ui->harmonics->setValue(m_settings.m_measurementHarmonics);
ui->fftWindow->blockSignals(false); ui->fftWindow->blockSignals(false);
ui->averaging->blockSignals(false); ui->averaging->blockSignals(false);
ui->averagingMode->blockSignals(false); ui->averagingMode->blockSignals(false);
ui->linscale->blockSignals(false); ui->linscale->blockSignals(false);
blockApplySettings(false); blockApplySettings(false);
updateMeasurements();
} }
void GLSpectrumGUI::displayGotoMarkers() void GLSpectrumGUI::displayGotoMarkers()
@ -342,15 +338,6 @@ void GLSpectrumGUI::applySpectrumSettings()
m_glSpectrum->setMarkersDisplay(m_settings.m_markersDisplay); m_glSpectrum->setMarkersDisplay(m_settings.m_markersDisplay);
m_glSpectrum->setCalibrationPoints(m_settings.m_calibrationPoints); m_glSpectrum->setCalibrationPoints(m_settings.m_calibrationPoints);
m_glSpectrum->setCalibrationInterpMode(m_settings.m_calibrationInterpMode); m_glSpectrum->setCalibrationInterpMode(m_settings.m_calibrationInterpMode);
m_glSpectrum->setMeasurementParams(
m_settings.m_measurement,
m_settings.m_measurementBandwidth,
m_settings.m_measurementChSpacing,
m_settings.m_measurementAdjChBandwidth,
m_settings.m_measurementHarmonics,
m_settings.m_measurementHighlight
);
} }
void GLSpectrumGUI::on_fftWindow_currentIndexChanged(int index) void GLSpectrumGUI::on_fftWindow_currentIndexChanged(int index)
@ -860,7 +847,7 @@ void GLSpectrumGUI::setMaximumOverlap()
bool GLSpectrumGUI::handleMessage(const Message& message) bool GLSpectrumGUI::handleMessage(const Message& message)
{ {
if (GLSpectrum::MsgReportSampleRate::match(message)) if (GLSpectrumView::MsgReportSampleRate::match(message))
{ {
setAveragingToolitp(); setAveragingToolitp();
setFFTSizeToolitp(); setFFTSizeToolitp();
@ -886,24 +873,24 @@ bool GLSpectrumGUI::handleMessage(const Message& message)
ui->wsSpectrum->blockSignals(false); ui->wsSpectrum->blockSignals(false);
return true; return true;
} }
else if (GLSpectrum::MsgReportWaterfallShare::match(message)) else if (GLSpectrumView::MsgReportWaterfallShare::match(message))
{ {
const GLSpectrum::MsgReportWaterfallShare& report = (const GLSpectrum::MsgReportWaterfallShare&) message; const GLSpectrumView::MsgReportWaterfallShare& report = (const GLSpectrumView::MsgReportWaterfallShare&) message;
m_settings.m_waterfallShare = report.getWaterfallShare(); m_settings.m_waterfallShare = report.getWaterfallShare();
return true; return true;
} }
else if (GLSpectrum::MsgReportFFTOverlap::match(message)) else if (GLSpectrumView::MsgReportFFTOverlap::match(message))
{ {
const GLSpectrum::MsgReportFFTOverlap& report = (const GLSpectrum::MsgReportFFTOverlap&) message; const GLSpectrumView::MsgReportFFTOverlap& report = (const GLSpectrumView::MsgReportFFTOverlap&) message;
m_settings.m_fftOverlap = report.getOverlap(); m_settings.m_fftOverlap = report.getOverlap();
ui->fftOverlap->blockSignals(true); ui->fftOverlap->blockSignals(true);
ui->fftOverlap->setValue(m_settings.m_fftOverlap); ui->fftOverlap->setValue(m_settings.m_fftOverlap);
ui->fftOverlap->blockSignals(false); ui->fftOverlap->blockSignals(false);
return true; return true;
} }
else if (GLSpectrum::MsgReportPowerScale::match(message)) else if (GLSpectrumView::MsgReportPowerScale::match(message))
{ {
const GLSpectrum::MsgReportPowerScale& report = (const GLSpectrum::MsgReportPowerScale&) message; const GLSpectrumView::MsgReportPowerScale& report = (const GLSpectrumView::MsgReportPowerScale&) message;
m_settings.m_refLevel = report.getRefLevel(); m_settings.m_refLevel = report.getRefLevel();
m_settings.m_powerRange = report.getRange(); m_settings.m_powerRange = report.getRange();
ui->refLevel->blockSignals(true); ui->refLevel->blockSignals(true);
@ -914,9 +901,9 @@ bool GLSpectrumGUI::handleMessage(const Message& message)
ui->refLevel->blockSignals(false); ui->refLevel->blockSignals(false);
return true; return true;
} }
else if (GLSpectrum::MsgReportCalibrationShift::match(message)) else if (GLSpectrumView::MsgReportCalibrationShift::match(message))
{ {
const GLSpectrum::MsgReportCalibrationShift& report = (GLSpectrum::MsgReportCalibrationShift&) message; const GLSpectrumView::MsgReportCalibrationShift& report = (GLSpectrumView::MsgReportCalibrationShift&) message;
m_calibrationShiftdB = report.getCalibrationShiftdB(); m_calibrationShiftdB = report.getCalibrationShiftdB();
ui->refLevel->blockSignals(true); ui->refLevel->blockSignals(true);
ui->refLevel->setValue(m_settings.m_refLevel + m_calibrationShiftdB); ui->refLevel->setValue(m_settings.m_refLevel + m_calibrationShiftdB);
@ -1027,58 +1014,35 @@ void GLSpectrumGUI::updateCalibrationPoints()
} }
} }
void GLSpectrumGUI::on_measurement_currentIndexChanged(int index) void GLSpectrumGUI::on_measure_clicked(bool checked)
{ {
m_settings.m_measurement = (SpectrumSettings::Measurement)index; SpectrumMeasurementsDialog measurementsDialog(
m_glSpectrum,
&m_settings,
this
);
bool highlight = (m_settings.m_measurement >= SpectrumSettings::MeasurementChannelPower); connect(&measurementsDialog, &SpectrumMeasurementsDialog::updateMeasurements, this, &GLSpectrumGUI::updateMeasurements);
ui->highlight->setVisible(highlight);
bool bw = (m_settings.m_measurement == SpectrumSettings::MeasurementChannelPower) measurementsDialog.exec();
|| (m_settings.m_measurement == SpectrumSettings::MeasurementAdjacentChannelPower);
ui->bandwidthLabel->setVisible(bw);
ui->bandwidth->setVisible(bw);
bool adj = m_settings.m_measurement == SpectrumSettings::MeasurementAdjacentChannelPower;
ui->chSpacingLabel->setVisible(adj);
ui->chSpacing->setVisible(adj);
ui->adjChBandwidthLabel->setVisible(adj);
ui->adjChBandwidth->setVisible(adj);
bool harmonics = (m_settings.m_measurement >= SpectrumSettings::MeasurementSNR)
&& (m_settings.m_measurement <= SpectrumSettings::MeasurementSINAD);
ui->harmonicsLabel->setVisible(harmonics);
ui->harmonics->setVisible(harmonics);
applySettings();
} }
void GLSpectrumGUI::on_highlight_toggled(bool checked) void GLSpectrumGUI::updateMeasurements()
{ {
m_settings.m_measurementHighlight = checked; if (m_glSpectrum)
applySettings(); {
} m_glSpectrum->setMeasurementsVisible(m_settings.m_measurement != SpectrumSettings::MeasurementNone);
m_glSpectrum->setMeasurementsPosition(m_settings.m_measurementsPosition);
void GLSpectrumGUI::on_bandwidth_valueChanged(int value) m_glSpectrum->setMeasurementParams(
{ m_settings.m_measurement,
m_settings.m_measurementBandwidth = value; m_settings.m_measurementCenterFrequencyOffset,
applySettings(); m_settings.m_measurementBandwidth,
} m_settings.m_measurementChSpacing,
m_settings.m_measurementAdjChBandwidth,
void GLSpectrumGUI::on_chSpacing_valueChanged(int value) m_settings.m_measurementHarmonics,
{ m_settings.m_measurementPeaks,
m_settings.m_measurementChSpacing = value; m_settings.m_measurementHighlight,
applySettings(); m_settings.m_measurementPrecision
} );
}
void GLSpectrumGUI::on_adjChBandwidth_valueChanged(int value)
{
m_settings.m_measurementAdjChBandwidth = value;
applySettings();
}
void GLSpectrumGUI::on_harmonics_valueChanged(int value)
{
m_settings.m_measurementHarmonics = value;
applySettings();
} }

View File

@ -123,12 +123,7 @@ private slots:
void on_calibration_toggled(bool checked); void on_calibration_toggled(bool checked);
void on_gotoMarker_currentIndexChanged(int index); void on_gotoMarker_currentIndexChanged(int index);
void on_measurement_currentIndexChanged(int index); void on_measure_clicked(bool checked);
void on_highlight_toggled(bool checked);
void on_bandwidth_valueChanged(int value);
void on_chSpacing_valueChanged(int value);
void on_adjChBandwidth_valueChanged(int value);
void on_harmonics_valueChanged(int value);
void handleInputMessages(); void handleInputMessages();
void openWebsocketSpectrumSettingsDialog(const QPoint& p); void openWebsocketSpectrumSettingsDialog(const QPoint& p);
@ -139,6 +134,7 @@ private slots:
void updateAnnotationMarkers(); void updateAnnotationMarkers();
void updateMarkersDisplay(); void updateMarkersDisplay();
void updateCalibrationPoints(); void updateCalibrationPoints();
void updateMeasurements();
signals: signals:
// Emitted when user selects an annotation marker // Emitted when user selects an annotation marker

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>630</width> <width>630</width>
<height>274</height> <height>228</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
@ -1073,6 +1073,26 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QPushButton" name="measure">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Open spectrum measurements dialog</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/ruler.png</normaloff>:/ruler.png</iconset>
</property>
<property name="checkable">
<bool>false</bool>
</property>
</widget>
</item>
<item> <item>
<widget class="ButtonSwitch" name="calibration"> <widget class="ButtonSwitch" name="calibration">
<property name="toolTip"> <property name="toolTip">
@ -1132,185 +1152,6 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="Line7Layout">
<item>
<widget class="QComboBox" name="measurement">
<property name="toolTip">
<string>Measurement</string>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Peak</string>
</property>
</item>
<item>
<property name="text">
<string>Ch Power</string>
</property>
</item>
<item>
<property name="text">
<string>Adj Ch</string>
</property>
</item>
<item>
<property name="text">
<string>SNR</string>
</property>
</item>
<item>
<property name="text">
<string>SNFR</string>
</property>
</item>
<item>
<property name="text">
<string>THD</string>
</property>
</item>
<item>
<property name="text">
<string>THD+N</string>
</property>
</item>
<item>
<property name="text">
<string>SINAD</string>
</property>
</item>
<item>
<property name="text">
<string>SFDR</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="highlight">
<property name="toolTip">
<string>Highlight measurement</string>
</property>
<property name="text">
<string>Max Hold</string>
</property>
<property name="icon">
<iconset resource="../resources/res.qrc">
<normaloff>:/carrier.png</normaloff>:/carrier.png</iconset>
</property>
<property name="iconSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="bandwidthLabel">
<property name="text">
<string>B/W</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="bandwidth">
<property name="toolTip">
<string>Measurement bandwidth (Hz)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100000000</number>
</property>
<property name="singleStep">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="chSpacingLabel">
<property name="text">
<string>Spacing</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="chSpacing">
<property name="toolTip">
<string>Channel spacing (Hz)</string>
</property>
<property name="maximum">
<number>100000000</number>
</property>
<property name="singleStep">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="adjChBandwidthLabel">
<property name="text">
<string>Adj. Ch. B/W</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="adjChBandwidth">
<property name="toolTip">
<string>Adjacent channel bandwidth (Hz)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100000000</number>
</property>
<property name="singleStep">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="harmonicsLabel">
<property name="text">
<string>Harmonics</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="harmonics">
<property name="toolTip">
<string>Number of harmonics</string>
</property>
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>

File diff suppressed because it is too large Load Diff

497
sdrgui/gui/glspectrumview.h Normal file
View File

@ -0,0 +1,497 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// OpenGL interface modernization. //
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_GLSPECTRUMVIEW_H
#define INCLUDE_GLSPECTRUMVIEW_H
#include <QTimer>
#include <QMutex>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QMatrix4x4>
#include <QPoint>
#include <QOpenGLWidget>
#include <QOpenGLDebugLogger>
#include "gui/scaleengine.h"
#include "gui/glshadersimple.h"
#include "gui/glshadertextured.h"
#include "gui/glshadercolormap.h"
#include "dsp/glspectruminterface.h"
#include "gui/glshaderspectrogram.h"
#include "dsp/spectrummarkers.h"
#include "dsp/channelmarker.h"
#include "dsp/spectrumsettings.h"
#include "export.h"
#include "util/incrementalarray.h"
#include "util/message.h"
#include "util/colormap.h"
class QOpenGLShaderProgram;
class MessageQueue;
class SpectrumVis;
class QOpenGLDebugLogger;
class SpectrumMeasurements;
class SDRGUI_API GLSpectrumView : public QOpenGLWidget, public GLSpectrumInterface {
Q_OBJECT
public:
class MsgReportSampleRate : public Message {
MESSAGE_CLASS_DECLARATION
public:
MsgReportSampleRate(quint32 sampleRate) :
Message(),
m_sampleRate(sampleRate)
{}
quint32 getSampleRate() const { return m_sampleRate; }
private:
quint32 m_sampleRate;
};
class MsgReportWaterfallShare : public Message {
MESSAGE_CLASS_DECLARATION
public:
MsgReportWaterfallShare(Real waterfallShare) :
Message(),
m_waterfallShare(waterfallShare)
{}
Real getWaterfallShare() const { return m_waterfallShare; }
private:
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;
};
class MsgReportCalibrationShift : public Message {
MESSAGE_CLASS_DECLARATION
public:
MsgReportCalibrationShift(Real calibrationShiftdB) :
Message(),
m_calibrationShiftdB(calibrationShiftdB)
{}
Real getCalibrationShiftdB() const { return m_calibrationShiftdB; }
private:
Real m_calibrationShiftdB;
};
GLSpectrumView(QWidget* parent = nullptr);
virtual ~GLSpectrumView();
void setCenterFrequency(qint64 frequency);
qint64 getCenterFrequency() const { return m_centerFrequency; }
float getPowerMax() const;
float getTimeMax() const;
void setSampleRate(qint32 sampleRate);
void setTimingRate(qint32 timingRate);
void setFFTOverlap(int overlap);
void setReferenceLevel(Real referenceLevel);
void setPowerRange(Real powerRange);
void setDecay(int decay);
void setDecayDivisor(int decayDivisor);
void setHistoStroke(int stroke);
void setDisplayWaterfall(bool display);
void setDisplay3DSpectrogram(bool display);
void set3DSpectrogramStyle(SpectrumSettings::SpectrogramStyle style);
void setColorMapName(const QString &colorMapName);
void setSpectrumStyle(SpectrumSettings::SpectrumStyle style);
void setSsbSpectrum(bool ssbSpectrum);
void setLsbDisplay(bool lsbDisplay);
void setInvertedWaterfall(bool inv);
void setDisplayMaxHold(bool display);
void setDisplayCurrent(bool display);
void setDisplayHistogram(bool display);
void setDisplayGrid(bool display);
void setDisplayGridIntensity(int intensity);
void setDisplayTraceIntensity(int intensity);
void setLinear(bool linear);
void setUseCalibration(bool useCalibration);
void setMeasurements(SpectrumMeasurements *measurements) { m_measurements = measurements; }
void setMeasurementParams(SpectrumSettings::Measurement measurement,
int centerFrequencyOffset, int bandwidth, int chSpacing, int adjChBandwidth,
int harmonics, int peaks, bool highlight, int precision);
qint32 getSampleRate() const { return m_sampleRate; }
void addChannelMarker(ChannelMarker* channelMarker);
void removeChannelMarker(ChannelMarker* channelMarker);
void setMessageQueueToGUI(MessageQueue* messageQueue) { m_messageQueueToGUI = messageQueue; }
virtual void newSpectrum(const Real* spectrum, int nbBins, int fftSize);
void clearSpectrumHistogram();
Real getWaterfallShare() const { return m_waterfallShare; }
void setWaterfallShare(Real waterfallShare);
void setFPSPeriodMs(int fpsPeriodMs);
void setDisplayedStream(bool sourceOrSink, int streamIndex)
{
m_displaySourceOrSink = sourceOrSink;
m_displayStreamIndex = streamIndex;
}
void setSpectrumVis(SpectrumVis *spectrumVis) { m_spectrumVis = spectrumVis; }
SpectrumVis *getSpectrumVis() { return m_spectrumVis; }
const QList<SpectrumHistogramMarker>& getHistogramMarkers() const { return m_histogramMarkers; }
QList<SpectrumHistogramMarker>& getHistogramMarkers() { return m_histogramMarkers; }
void setHistogramMarkers(const QList<SpectrumHistogramMarker>& histogramMarkers);
const QList<SpectrumWaterfallMarker>& getWaterfallMarkers() const { return m_waterfallMarkers; }
QList<SpectrumWaterfallMarker>& getWaterfallMarkers() { return m_waterfallMarkers; }
void setWaterfallMarkers(const QList<SpectrumWaterfallMarker>& waterfallMarkers);
const QList<SpectrumAnnotationMarker>& getAnnotationMarkers() const { return m_annotationMarkers; }
QList<SpectrumAnnotationMarker>& getAnnotationMarkers() { return m_annotationMarkers; }
void setAnnotationMarkers(const QList<SpectrumAnnotationMarker>& annotationMarkers);
void updateHistogramMarkers();
void updateWaterfallMarkers();
void updateAnnotationMarkers();
void updateMarkersDisplay();
void updateCalibrationPoints();
SpectrumSettings::MarkersDisplay& getMarkersDisplay() { return m_markersDisplay; }
void setMarkersDisplay(SpectrumSettings::MarkersDisplay markersDisplay);
QList<SpectrumCalibrationPoint>& getCalibrationPoints() { return m_calibrationPoints; }
void setCalibrationPoints(const QList<SpectrumCalibrationPoint>& calibrationPoints);
SpectrumSettings::CalibrationInterpolationMode& getCalibrationInterpMode() { return m_calibrationInterpMode; }
void setCalibrationInterpMode(SpectrumSettings::CalibrationInterpolationMode mode);
void setIsDeviceSpectrum(bool isDeviceSpectrum) { m_isDeviceSpectrum = isDeviceSpectrum; }
bool isDeviceSpectrum() const { return m_isDeviceSpectrum; }
private:
struct ChannelMarkerState {
ChannelMarker* m_channelMarker;
QMatrix4x4 m_glMatrixWaterfall;
QMatrix4x4 m_glMatrixDsbWaterfall;
QMatrix4x4 m_glMatrixFreqScale;
QMatrix4x4 m_glMatrixDsbFreqScale;
QMatrix4x4 m_glMatrixHistogram;
QMatrix4x4 m_glMatrixDsbHistogram;
QRectF m_rect;
ChannelMarkerState(ChannelMarker* channelMarker) :
m_channelMarker(channelMarker)
{ }
};
QList<ChannelMarkerState*> m_channelMarkerStates;
enum CursorState {
CSNormal,
CSSplitter,
CSSplitterMoving,
CSChannel,
CSChannelMoving
};
QList<SpectrumHistogramMarker> m_histogramMarkers;
QList<SpectrumWaterfallMarker> m_waterfallMarkers;
QList<SpectrumAnnotationMarker> m_annotationMarkers;
QList<SpectrumAnnotationMarker*> m_sortedAnnotationMarkers;
QList<SpectrumAnnotationMarker*> m_visibleAnnotationMarkers;
SpectrumSettings::MarkersDisplay m_markersDisplay;
QList<SpectrumCalibrationPoint> m_calibrationPoints;
CursorState m_cursorState;
int m_cursorChannel;
SpectrumVis* m_spectrumVis;
QTimer m_timer;
int m_fpsPeriodMs;
QMutex m_mutex;
bool m_mouseInside;
bool m_changesPending;
qint64 m_centerFrequency;
Real m_referenceLevel;
Real m_powerRange;
bool m_linear;
int m_decay;
quint32 m_sampleRate;
quint32 m_timingRate;
int m_fftOverlap;
int m_fftSize; //!< FFT size in number of bins
int m_nbBins; //!< Number of visible FFT bins (zoom support)
bool m_displayGrid;
int m_displayGridIntensity;
int m_displayTraceIntensity;
bool m_invertedWaterfall;
std::vector<Real> m_maxHold;
bool m_displayMaxHold;
const Real *m_currentSpectrum;
bool m_displayCurrent;
Real m_waterfallShare;
int m_leftMargin;
int m_rightMargin;
int m_topMargin;
int m_frequencyScaleHeight;
int m_infoHeight;
int m_histogramHeight;
int m_waterfallHeight;
int m_bottomMargin;
QFont m_textOverlayFont;
QPixmap m_leftMarginPixmap;
QPixmap m_frequencyPixmap;
QPixmap m_infoPixmap;
ScaleEngine m_timeScale;
ScaleEngine m_powerScale;
ScaleEngine m_frequencyScale;
QRectF m_histogramRect;
QRect m_frequencyScaleRect;
QRectF m_waterfallRect;
QRect m_infoRect;
QMatrix4x4 m_glFrequencyScaleBoxMatrix;
QMatrix4x4 m_glLeftScaleBoxMatrix;
QMatrix4x4 m_glInfoBoxMatrix;
QString m_peakFrequencyMaxStr;
QString m_peakPowerMaxStr;
QString m_peakPowerUnits;
QRgb m_waterfallPalette[240];
QImage* m_waterfallBuffer;
int m_waterfallBufferPos;
int m_waterfallTextureHeight;
int m_waterfallTexturePos;
QMatrix4x4 m_glWaterfallBoxMatrix;
bool m_displayWaterfall;
bool m_ssbSpectrum;
bool m_lsbDisplay;
QImage* m_3DSpectrogramBuffer;
int m_3DSpectrogramBufferPos;
int m_3DSpectrogramTextureHeight;
int m_3DSpectrogramTexturePos;
bool m_display3DSpectrogram;
bool m_rotate3DSpectrogram; //!< Set when mouse is pressed in 3D spectrogram area for rotation when mouse is moved
bool m_pan3DSpectrogram;
bool m_scaleZ3DSpectrogram;
QPointF m_mousePrevLocalPos; //!< Position of the mouse for last event
int m_3DSpectrogramBottom;
QPixmap m_spectrogramTimePixmap;
QPixmap m_spectrogramPowerPixmap;
SpectrumSettings::SpectrogramStyle m_3DSpectrogramStyle;
QString m_colorMapName;
SpectrumSettings::SpectrumStyle m_spectrumStyle;
const float *m_colorMap;
bool m_scrollFrequency;
qint64 m_scrollStartCenterFreq;
QRgb m_histogramPalette[240];
QImage* m_histogramBuffer;
quint8* m_histogram; //!< Spectrum phosphor matrix of FFT width and PSD height scaled to 100. values [0..239]
int m_decayDivisor;
int m_decayDivisorCount;
int m_histogramStroke;
QMatrix4x4 m_glHistogramSpectrumMatrix;
QMatrix4x4 m_glHistogramBoxMatrix;
bool m_displayHistogram;
bool m_displayChanged;
bool m_displaySourceOrSink;
int m_displayStreamIndex;
float m_frequencyZoomFactor;
float m_frequencyZoomPos;
static const float m_maxFrequencyZoom;
static const float m_annotationMarkerHeight;
GLShaderSimple m_glShaderSimple;
GLShaderTextured m_glShaderLeftScale;
GLShaderTextured m_glShaderFrequencyScale;
GLShaderTextured m_glShaderWaterfall;
GLShaderTextured m_glShaderHistogram;
GLShaderColorMap m_glShaderColorMap;
GLShaderTextured m_glShaderTextOverlay;
GLShaderTextured m_glShaderInfo;
GLShaderSpectrogram m_glShaderSpectrogram;
GLShaderTextured m_glShaderSpectrogramTimeScale;
GLShaderTextured m_glShaderSpectrogramPowerScale;
int m_matrixLoc;
int m_colorLoc;
bool m_useCalibration;
Real m_calibrationGain;
Real m_calibrationShiftdB;
SpectrumSettings::CalibrationInterpolationMode m_calibrationInterpMode;
IncrementalArray<GLfloat> m_q3TickTime;
IncrementalArray<GLfloat> m_q3TickFrequency;
IncrementalArray<GLfloat> m_q3TickPower;
IncrementalArray<GLfloat> m_q3FFT;
IncrementalArray<GLfloat> m_q3ColorMap;
MessageQueue *m_messageQueueToGUI;
QOpenGLDebugLogger *m_openGLLogger;
bool m_isDeviceSpectrum;
SpectrumMeasurements *m_measurements;
SpectrumSettings::Measurement m_measurement;
int m_measurementCenterFrequencyOffset;
int m_measurementBandwidth;
int m_measurementChSpacing;
int m_measurementAdjChBandwidth;
int m_measurementHarmonics;
int m_measurementPeaks;
bool m_measurementHighlight;
int m_measurementPrecision;
static const QVector4D m_measurementLightMarkerColor;
static const QVector4D m_measurementDarkMarkerColor;
void updateWaterfall(const Real *spectrum);
void update3DSpectrogram(const Real *spectrum);
void updateHistogram(const Real *spectrum);
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
void drawPowerBandMarkers(float max, float min, const QVector4D &color);
void drawBandwidthMarkers(int64_t centerFrequency, int bandwidth, const QVector4D &color);
void drawPeakMarkers(int64_t startFrequency, int64_t endFrequency, const QVector4D &color);
void drawSpectrumMarkers();
void drawAnnotationMarkers();
void measurePeak();
void measurePeaks();
void measureChannelPower();
void measureAdjacentChannelPower();
void measureSNR();
void measureSFDR();
float calcChannelPower(int64_t centerFrequency, int channelBandwidth) const;
float calPower(float power) const;
int findPeakBin(const Real *spectrum) const;
void findPeak(float &power, float &frequency) const;
void peakWidth(const Real *spectrum, int center, int &left, int &right, int maxLeft, int maxRight) const;
int frequencyToBin(int64_t frequency) const;
int64_t binToFrequency(int bin) const;
void stopDrag();
void applyChanges();
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void wheelEvent(QWheelEvent*);
void channelMarkerMove(QWheelEvent*, int mul);
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();
void setPowerScale(int height);
void getFrequencyZoom(int64_t& centerFrequency, int& frequencySpan);
bool pointInWaterfallOrSpectrogram(const QPointF &point) const;
bool pointInHistogram(const QPointF &point) const;
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
static QString displayFull(int64_t value);
static QString displayScaled(int64_t value, char type, int precision, bool showMult);
static QString displayScaledF(float value, char type, int precision, bool showMult);
static QString displayPower(float value, char type, int precision);
int getPrecision(int value);
void drawTextRight(const QString &text, const QString &value, const QString &max, const QString &units);
void drawTextsRight(const QStringList &text, const QStringList &value, const QStringList &max, const QStringList &units);
void drawTextOverlayCentered(
const QString& text,
const QColor& color,
const QFont& font,
float shiftX,
float shiftY,
const QRectF& glRect);
void drawTextOverlay( //!< Draws a text overlay
const QString& text,
const QColor& color,
const QFont& font,
float shiftX,
float shiftY,
bool leftHalf,
bool topHalf,
const QRectF& glRect);
void formatTextInfo(QString& info);
void updateSortedAnnotationMarkers();
static bool annotationDisplayLessThan(const SpectrumAnnotationMarker *m1, const SpectrumAnnotationMarker *m2)
{
if (m1->m_bandwidth == m2->m_bandwidth) {
return m1->m_startFrequency < m2->m_startFrequency;
} else {
return m1->m_bandwidth > m2->m_bandwidth; // larger bandwidths should come first for display (lower layer)
}
}
static bool calibrationPointsLessThan(const SpectrumCalibrationPoint& m1, const SpectrumCalibrationPoint& m2)
{
return m1.m_frequency < m2.m_frequency;
}
private slots:
void cleanup();
void tick();
void channelMarkerChanged();
void channelMarkerDestroyed(QObject* object);
void openGLDebug(const QOpenGLDebugMessage &debugMessage);
bool eventFilter(QObject *object, QEvent *event);
signals:
// Emitted when user tries to scroll to frequency currently out of range
void requestCenterFrequency(qint64 frequency);
};
#endif // INCLUDE_GLSPECTRUMVIEW_H

View File

@ -0,0 +1,625 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QTableWidgetItem>
#include <QHeaderView>
#include <QVBoxLayout>
#include <QMenu>
#include <QAction>
#include <QClipboard>
#include <QGuiApplication>
#include <QDebug>
#include <QPainter>
#include "gui/spectrummeasurements.h"
#include <QStyledItemDelegate>
class SDRGUI_API UnitsDelegate : public QStyledItemDelegate {
public:
UnitsDelegate(QObject *parent = nullptr);
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QString s = text(index);
return QSize(width(s, option.fontMetrics) + 2, option.fontMetrics.height());
}
int width(const QString &s, const QFontMetrics &fm) const
{
int left = s.size() > 0 ? fm.leftBearing(s[0]) : 0;
int right = s.size() > 0 ? fm.rightBearing(s[s.size()-1]) : 0;
return fm.horizontalAdvance(s) + left + right;
}
QString text(const QModelIndex &index) const
{
QString units = index.data(UNITS_ROLE).toString();
QString s;
if (units == "Hz")
{
s = formatEngineering(index.data().toLongLong());
}
else
{
int precision = index.data(PRECISION_ROLE).toInt();
double d = index.data().toDouble();
s = QString::number(d, 'f', precision);
}
return s + units;
}
enum Roles {
UNITS_ROLE = Qt::UserRole,
PRECISION_ROLE,
SPEC_ROLE
};
protected:
QString formatEngineering(int64_t value) const;
};
UnitsDelegate::UnitsDelegate(QObject *parent) :
QStyledItemDelegate(parent)
{
}
QString UnitsDelegate::formatEngineering(int64_t value) const
{
if (value == 0) {
return "0";
}
int64_t absValue = std::abs(value);
QString digits = QString::number(absValue);
int cnt = digits.size();
QString point = QLocale::system().decimalPoint();
QString group = QLocale::system().groupSeparator();
int i;
for (i = cnt - 3; i >= 4; i -= 3)
{
digits = digits.insert(i, group);
}
if (absValue >= 1000) {
digits = digits.insert(i, point);
}
if (cnt > 9) {
digits = digits.append("G");
} else if (cnt > 6) {
digits = digits.append("M");
} else if (cnt > 3) {
digits = digits.append("k");
}
if (value < 0) {
digits = digits.insert(0, "-");
}
return digits;
}
void UnitsDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QFontMetrics fm = painter->fontMetrics();
QString s = text(index);
int sWidth = width(s, fm);
while ((sWidth > option.rect.width()) && !s.isEmpty())
{
s = s.mid(1);
sWidth = width(s, fm);
}
int y = option.rect.y() + (option.rect.height()) - ((option.rect.height() - fm.ascent()) / 2); // Align center vertically
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
painter->setPen(opt.palette.color(cg, QPalette::Text));
painter->drawText(option.rect.x() + option.rect.width() - 1 - sWidth, y, s);
}
const QStringList SpectrumMeasurements::m_measurementColumns = {
"Current",
"Mean",
"Min",
"Max",
"Range",
"Std Dev",
"Count",
"Spec",
"Fails",
""
};
const QStringList SpectrumMeasurements::m_tooltips = {
"Current value",
"Mean average of values",
"Minimum value",
"Maximum value",
"Range of values (Max-Min)",
"Standard deviation",
"Count of values",
"Specification for value.\n\nE.g. <-100.5, >34.5 or =10.2",
"Count of values that failed to meet specification",
""
};
SpectrumMeasurements::SpectrumMeasurements(QWidget *parent) :
QWidget(parent),
m_measurement(SpectrumSettings::MeasurementPeaks),
m_precision(1),
m_table(nullptr),
m_peakTable(nullptr)
{
m_textBrush.setColor(Qt::white); // Should get this from the style sheet?
m_redBrush.setColor(Qt::red);
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
QVBoxLayout *layout = new QVBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
setLayout(layout);
}
void SpectrumMeasurements::createMeasurementsTable(const QStringList &rows, const QStringList &units)
{
m_table = new QTableWidget();
m_table->horizontalHeader()->setSectionsMovable(true);
m_table->verticalHeader()->setSectionsMovable(true);
m_table->setColumnCount(m_measurementColumns.size());
for (int i = 0; i < m_measurementColumns.size(); i++)
{
QTableWidgetItem *item = new QTableWidgetItem(m_measurementColumns[i]);
item->setToolTip(m_tooltips[i]);
m_table->setHorizontalHeaderItem(i, item);
}
// Cell context menu
m_table->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_table, &QTableWidget::customContextMenuRequested, this, &SpectrumMeasurements::tableContextMenu);
m_table->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_table->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
// Fill up space at end of rows
m_table->horizontalHeader()->setSectionResizeMode(COL_EMPTY, QHeaderView::Stretch);
m_table->setRowCount(rows.size());
for (int i = 0; i < rows.size(); i++)
{
m_table->setVerticalHeaderItem(i, new QTableWidgetItem(rows[i]));
for (int j = 0; j < m_measurementColumns.size(); j++)
{
QTableWidgetItem *item = new QTableWidgetItem();
item->setFlags(Qt::ItemIsEnabled);
item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
if (j < COL_COUNT)
{
item->setData(UnitsDelegate::UNITS_ROLE, units[i]);
item->setData(UnitsDelegate::PRECISION_ROLE, m_precision);
}
else if (j == COL_SPEC)
{
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable);
}
m_table->setItem(i, j, item);
}
Measurement m;
m.m_units = units[i];
m_measurements.append(m);
}
resizeMeasurementsTable();
for (int i = 0; i < COL_COUNT; i++) {
m_table->setItemDelegateForColumn(i, new UnitsDelegate());
}
createTableMenus();
}
void SpectrumMeasurements::createPeakTable(int peaks)
{
m_peakTable = new QTableWidget();
m_peakTable->horizontalHeader()->setSectionsMovable(true);
QStringList columns = QStringList{"Frequency", "Power", ""};
m_peakTable->setColumnCount(columns.size());
m_peakTable->setRowCount(peaks);
for (int i = 0; i < columns.size(); i++) {
m_peakTable->setHorizontalHeaderItem(i, new QTableWidgetItem(columns[i]));
}
for (int i = 0; i < peaks; i++)
{
for (int j = 0; j < 3; j++)
{
QTableWidgetItem *item = new QTableWidgetItem();
item->setFlags(Qt::ItemIsEnabled);
if (j == COL_FREQUENCY) {
item->setData(UnitsDelegate::UNITS_ROLE, "Hz");
} else if (j == COL_POWER) {
item->setData(UnitsDelegate::UNITS_ROLE, " dB");
item->setData(UnitsDelegate::PRECISION_ROLE, m_precision);
}
m_peakTable->setItem(i, j, item);
}
}
resizePeakTable();
m_peakTable->setItemDelegateForColumn(COL_FREQUENCY, new UnitsDelegate());
m_peakTable->setItemDelegateForColumn(COL_POWER, new UnitsDelegate());
// Fill up space at end of rows
m_peakTable->horizontalHeader()->setSectionResizeMode(COL_PEAK_EMPTY, QHeaderView::Stretch);
m_peakTable->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
m_peakTable->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
// Cell context menu
m_peakTable->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_peakTable, &QTableWidget::customContextMenuRequested, this, &SpectrumMeasurements::peakTableContextMenu);
}
void SpectrumMeasurements::createTableMenus()
{
// Add context menu to allow hiding/showing of columns
m_rowMenu = new QMenu(m_table);
for (int i = 0; i < m_table->verticalHeader()->count(); i++)
{
QString text = m_table->verticalHeaderItem(i)->text();
m_rowMenu->addAction(createCheckableItem(text, i, true, true));
}
m_table->verticalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_table->verticalHeader(), &QTableWidget::customContextMenuRequested, this, &SpectrumMeasurements::rowSelectMenu);
// Add context menu to allow hiding/showing of rows
m_columnMenu = new QMenu(m_table);
for (int i = 0; i < m_table->horizontalHeader()->count(); i++)
{
QString text = m_table->horizontalHeaderItem(i)->text();
m_columnMenu->addAction(createCheckableItem(text, i, true, false));
}
m_table->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_table->horizontalHeader(), &QTableWidget::customContextMenuRequested, this, &SpectrumMeasurements::columnSelectMenu);
}
void SpectrumMeasurements::createChannelPowerTable()
{
QStringList rows = {"Channel power"};
QStringList units = {" dB"};
createMeasurementsTable(rows, units);
}
void SpectrumMeasurements::createAdjacentChannelPowerTable()
{
QStringList rows = {"Left power", "Left ACPR", "Center power", "Right power", "Right ACPR"};
QStringList units = {" dB", " dBc", " dB", " dB", " dBc"};
createMeasurementsTable(rows, units);
}
void SpectrumMeasurements::createSNRTable()
{
QStringList rows = {"SNR", "SNFR", "THD", "THD+N", "SINAD", "SFDR",};
QStringList units = {" dB", " dB", " dB", " dB", " dB", " dBc"};
createMeasurementsTable(rows, units);
}
// Create column select menu item
QAction *SpectrumMeasurements::createCheckableItem(QString &text, int idx, bool checked, bool row)
{
QAction *action = new QAction(text, this);
action->setCheckable(true);
action->setChecked(checked);
action->setData(QVariant(idx));
if (row) {
connect(action, &QAction::triggered, this, &SpectrumMeasurements::rowSelectMenuChecked);
} else {
connect(action, &QAction::triggered, this, &SpectrumMeasurements::columnSelectMenuChecked);
}
return action;
}
// Right click in table header - show row select menu
void SpectrumMeasurements::rowSelectMenu(QPoint pos)
{
m_rowMenu->popup(m_table->verticalHeader()->viewport()->mapToGlobal(pos));
}
// Hide/show row when menu selected
void SpectrumMeasurements::rowSelectMenuChecked(bool checked)
{
(void) checked;
QAction* action = qobject_cast<QAction*>(sender());
if (action != nullptr)
{
int idx = action->data().toInt(nullptr);
m_table->setRowHidden(idx, !action->isChecked());
}
}
// Right click in table header - show column select menu
void SpectrumMeasurements::columnSelectMenu(QPoint pos)
{
m_columnMenu->popup(m_table->horizontalHeader()->viewport()->mapToGlobal(pos));
}
// Hide/show column when menu selected
void SpectrumMeasurements::columnSelectMenuChecked(bool checked)
{
(void) checked;
QAction* action = qobject_cast<QAction*>(sender());
if (action != nullptr)
{
int idx = action->data().toInt(nullptr);
m_table->setColumnHidden(idx, !action->isChecked());
}
}
void SpectrumMeasurements::tableContextMenu(QPoint pos)
{
QTableWidgetItem *item = m_table->itemAt(pos);
if (item)
{
QMenu* tableContextMenu = new QMenu(m_table);
connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater);
// Copy current cell
QAction* copyAction = new QAction("Copy", tableContextMenu);
const QString text = item->text();
connect(copyAction, &QAction::triggered, this, [text]()->void {
QClipboard *clipboard = QGuiApplication::clipboard();
clipboard->setText(text);
});
tableContextMenu->addAction(copyAction);
tableContextMenu->addSeparator();
tableContextMenu->popup(m_table->viewport()->mapToGlobal(pos));
}
}
void SpectrumMeasurements::peakTableContextMenu(QPoint pos)
{
QTableWidgetItem *item = m_peakTable->itemAt(pos);
if (item)
{
QMenu* tableContextMenu = new QMenu(m_peakTable);
connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater);
// Copy current cell
QAction* copyAction = new QAction("Copy", tableContextMenu);
const QString text = item->text();
connect(copyAction, &QAction::triggered, this, [text]()->void {
QClipboard *clipboard = QGuiApplication::clipboard();
clipboard->setText(text);
});
tableContextMenu->addAction(copyAction);
tableContextMenu->addSeparator();
tableContextMenu->popup(m_peakTable->viewport()->mapToGlobal(pos));
}
}
void SpectrumMeasurements::resizeMeasurementsTable()
{
// Fill table with a row of dummy data that will size the columns nicely
int row = m_table->rowCount();
m_table->setRowCount(row + 1);
m_table->setItem(row, COL_CURRENT, new QTableWidgetItem("-120.0 dBc"));
m_table->setItem(row, COL_MEAN, new QTableWidgetItem("-120.0 dBc"));
m_table->setItem(row, COL_MIN, new QTableWidgetItem("-120.0 dBc"));
m_table->setItem(row, COL_MAX, new QTableWidgetItem("-120.0 dBc"));
m_table->setItem(row, COL_RANGE, new QTableWidgetItem("-120.0 dBc"));
m_table->setItem(row, COL_STD_DEV, new QTableWidgetItem("-120.0 dBc"));
m_table->setItem(row, COL_COUNT, new QTableWidgetItem("100000"));
m_table->setItem(row, COL_SPEC, new QTableWidgetItem(">= -120.0"));
m_table->setItem(row, COL_FAILS, new QTableWidgetItem("100000"));
m_table->resizeColumnsToContents();
m_table->removeRow(row);
}
void SpectrumMeasurements::resizePeakTable()
{
// Fill table with a row of dummy data that will size the columns nicely
int row = m_peakTable->rowCount();
m_peakTable->setRowCount(row + 1);
m_peakTable->setItem(row, COL_FREQUENCY, new QTableWidgetItem("6.000,000,000GHz"));
m_peakTable->setItem(row, COL_POWER, new QTableWidgetItem("-120.0 dB"));
m_peakTable->resizeColumnsToContents();
m_peakTable->removeRow(row);
}
void SpectrumMeasurements::setMeasurementParams(SpectrumSettings::Measurement measurement, int peaks, int precision)
{
if ( (measurement != m_measurement)
|| (m_precision != precision)
|| ((m_peakTable == nullptr) && (m_table == nullptr))
|| ((m_peakTable != nullptr) && (peaks != m_peakTable->rowCount()))
)
{
// Tried using setVisible(), but that would hang, so delete and recreate
delete m_peakTable;
m_peakTable = nullptr;
delete m_table;
m_table = nullptr;
m_measurement = measurement;
m_precision = precision;
switch (measurement)
{
case SpectrumSettings::MeasurementPeaks:
createPeakTable(peaks);
layout()->addWidget(m_peakTable);
break;
case SpectrumSettings::MeasurementChannelPower:
reset();
createChannelPowerTable();
layout()->addWidget(m_table);
break;
case SpectrumSettings::MeasurementAdjacentChannelPower:
reset();
createAdjacentChannelPowerTable();
layout()->addWidget(m_table);
break;
case SpectrumSettings::MeasurementSNR:
reset();
createSNRTable();
layout()->addWidget(m_table);
break;
default:
break;
}
}
}
void SpectrumMeasurements::reset()
{
for (int i = 0; i < m_measurements.size(); i++) {
m_measurements[i].reset();
}
if (m_table)
{
for (int i = 0; i < m_table->rowCount(); i++)
{
for (int j = 0; j < m_table->columnCount(); j++)
{
if (j != COL_SPEC) {
m_table->item(i, j)->setText("");
}
}
}
}
}
// Check the value meets the user-defined specification
bool SpectrumMeasurements::checkSpec(const QString &spec, double value) const
{
if (spec.isEmpty()) {
return true;
}
if (spec.startsWith("<="))
{
double limit = spec.mid(2).toDouble();
return value <= limit;
}
else if (spec[0] == '<')
{
double limit = spec.mid(1).toDouble();
return value < limit;
}
else if (spec.startsWith(">="))
{
double limit = spec.mid(2).toDouble();
return value >= limit;
}
else if (spec[0] == '>')
{
double limit = spec.mid(1).toDouble();
return value > limit;
}
else if (spec[0] == '=')
{
double limit = spec.mid(1).toDouble();
return value == limit;
}
return false;
}
void SpectrumMeasurements::updateMeasurement(int row, float value)
{
m_measurements[row].add(value);
double mean = m_measurements[row].mean();
m_table->item(row, COL_CURRENT)->setData(Qt::DisplayRole, value);
m_table->item(row, COL_MEAN)->setData(Qt::DisplayRole, mean);
m_table->item(row, COL_MIN)->setData(Qt::DisplayRole, m_measurements[row].m_min);
m_table->item(row, COL_MAX)->setData(Qt::DisplayRole, m_measurements[row].m_max);
m_table->item(row, COL_RANGE)->setData(Qt::DisplayRole, m_measurements[row].m_max - m_measurements[row].m_min);
m_table->item(row, COL_STD_DEV)->setData(Qt::DisplayRole, m_measurements[row].stdDev());
m_table->item(row, COL_COUNT)->setData(Qt::DisplayRole, m_measurements[row].m_values.size());
QString spec = m_table->item(row, COL_SPEC)->text();
bool valueOK = checkSpec(spec, value);
bool meanOK = checkSpec(spec, mean);
bool minOK = checkSpec(spec, m_measurements[row].m_min);
bool mmaxOK = checkSpec(spec, m_measurements[row].m_max);
if (!valueOK)
{
m_measurements[row].m_fails++;
m_table->item(row, 8)->setData(Qt::DisplayRole, m_measurements[row].m_fails);
}
// item->setForeground doesn't work, perhaps as we have style sheet applied?
m_table->item(row, COL_CURRENT)->setData(Qt::ForegroundRole, valueOK ? m_textBrush : m_redBrush);
m_table->item(row, COL_MEAN)->setData(Qt::ForegroundRole, meanOK ? m_textBrush : m_redBrush);
m_table->item(row, COL_MIN)->setData(Qt::ForegroundRole, minOK ? m_textBrush : m_redBrush);
m_table->item(row, COL_MAX)->setData(Qt::ForegroundRole, mmaxOK ? m_textBrush : m_redBrush);
}
void SpectrumMeasurements::setSNR(float snr, float snfr, float thd, float thdpn, float sinad)
{
updateMeasurement(0, snr);
updateMeasurement(1, snfr);
updateMeasurement(2, thd);
updateMeasurement(3, thdpn);
updateMeasurement(4, sinad);
}
void SpectrumMeasurements::setSFDR(float sfdr)
{
updateMeasurement(5, sfdr);
}
void SpectrumMeasurements::setChannelPower(float power)
{
updateMeasurement(0, power);
}
void SpectrumMeasurements::setAdjacentChannelPower(float left, float leftACPR, float center, float right, float rightACPR)
{
updateMeasurement(0, left);
updateMeasurement(1, leftACPR);
updateMeasurement(2, center);
updateMeasurement(3, right);
updateMeasurement(4, rightACPR);
}
void SpectrumMeasurements::setPeak(int peak, int64_t frequency, float power)
{
if (peak < m_peakTable->rowCount())
{
m_peakTable->item(peak, COL_FREQUENCY)->setData(Qt::DisplayRole, QVariant((qlonglong)frequency));
m_peakTable->item(peak, COL_POWER)->setData(Qt::DisplayRole, power);
}
else
{
qDebug() << "SpectrumMeasurements::setPeak: Attempt to set peak " << peak << " when only " << m_peakTable->rowCount() << " rows in peak table";
}
}

View File

@ -0,0 +1,149 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRGUI_SPECTRUMMEASUREMENTS_H_
#define SDRGUI_SPECTRUMMEASUREMENTS_H_
#include <QWidget>
#include <QMenu>
#include <QTableWidget>
#include "dsp/spectrumsettings.h"
#include "export.h"
// Displays spectrum measurements in a table
class SDRGUI_API SpectrumMeasurements : public QWidget {
Q_OBJECT
struct Measurement {
QList<float> m_values;
float m_min;
float m_max;
double m_sum;
int m_fails;
QString m_units;
Measurement()
{
reset();
}
void reset()
{
m_values.clear();
m_min = std::numeric_limits<float>::max();
m_max = -std::numeric_limits<float>::max();
m_sum = 0.0;
m_fails = 0;
}
void add(float value)
{
m_min = std::min(value, m_min);
m_max = std::max(value, m_max);
m_sum += value;
m_values.append(value);
}
double mean() const
{
return m_sum / m_values.size();
}
double stdDev() const
{
double u = mean();
double sum = 0.0;
for (int i = 0; i < m_values.size(); i++)
{
double d = m_values[i] - u;
sum += d * d;
}
if (m_values.size() < 2) {
return 0.0;
} else {
return sqrt(sum / (m_values.size() - 1));
}
}
};
public:
SpectrumMeasurements(QWidget *parent = nullptr);
void setMeasurementParams(SpectrumSettings::Measurement measurement, int peaks, int precision);
void setSNR(float snr, float snfr, float thd, float thdpn, float sinad);
void setSFDR(float sfdr);
void setChannelPower(float power);
void setAdjacentChannelPower(float left, float leftACPR, float center, float right, float rightACPR);
void setPeak(int peak, int64_t frequency, float power);
void reset();
private:
void createMeasurementsTable(const QStringList &rows, const QStringList &units);
void createPeakTable(int peaks);
void createTableMenus();
void createChannelPowerTable();
void createAdjacentChannelPowerTable();
void createSNRTable();
void tableContextMenu(QPoint pos);
void peakTableContextMenu(QPoint pos);
void rowSelectMenu(QPoint pos);
void rowSelectMenuChecked(bool checked);
void columnSelectMenu(QPoint pos);
void columnSelectMenuChecked(bool checked);
QAction *createCheckableItem(QString &text, int idx, bool checked, bool row);
void resizeMeasurementsTable();
void resizePeakTable();
void updateMeasurement(int row, float value);
bool checkSpec(const QString &spec, double value) const;
SpectrumSettings::Measurement m_measurement;
int m_precision;
QTableWidget *m_table;
QMenu *m_rowMenu;
QMenu *m_columnMenu;
QList<Measurement> m_measurements;
QTableWidget *m_peakTable;
QBrush m_textBrush;
QBrush m_redBrush;
enum MeasurementsCol {
COL_CURRENT,
COL_MEAN,
COL_MIN,
COL_MAX,
COL_RANGE,
COL_STD_DEV,
COL_COUNT,
COL_SPEC,
COL_FAILS,
COL_EMPTY
};
enum PeakTableCol {
COL_FREQUENCY,
COL_POWER,
COL_PEAK_EMPTY
};
static const QStringList m_measurementColumns;
static const QStringList m_tooltips;
};
#endif // SDRGUI_SPECTRUMMEASUREMENTS_H_

View File

@ -0,0 +1,173 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QStandardPaths>
#include <QColorDialog>
#include <QFileDialog>
#include "util/db.h"
#include "util/csv.h"
#include "spectrummeasurementsdialog.h"
#include "spectrummeasurements.h"
#include "glspectrum.h"
#include "ui_spectrummeasurementsdialog.h"
SpectrumMeasurementsDialog::SpectrumMeasurementsDialog(GLSpectrum *glSpectrum, SpectrumSettings *settings, QWidget *parent) :
QDialog(parent),
ui(new Ui::SpectrumMeasurementsDialog),
m_glSpectrum(glSpectrum),
m_settings(settings)
{
ui->setupUi(this);
ui->measurement->setCurrentIndex((int)m_settings->m_measurement);
ui->position->setCurrentIndex((int)m_settings->m_measurementsPosition);
ui->precision->setValue(m_settings->m_measurementPrecision);
ui->highlight->setChecked(m_settings->m_measurementHighlight);
ui->centerFrequencyOffset->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->centerFrequencyOffset->setValueRange(false, 8, -99999999L, 99999999L);
ui->centerFrequencyOffset->setValue(m_settings->m_measurementCenterFrequencyOffset);
ui->bandwidth->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->bandwidth->setValueRange(true, 8, 0L, 99999999L);
ui->bandwidth->setValue(m_settings->m_measurementBandwidth);
ui->chSpacing->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->chSpacing->setValueRange(true, 8, 0L, 99999999L);
ui->chSpacing->setValue(m_settings->m_measurementChSpacing);
ui->adjChBandwidth->setColorMapper(ColorMapper(ColorMapper::GrayGold));
ui->adjChBandwidth->setValueRange(true, 8, 0L, 99999999L);
ui->adjChBandwidth->setValue(m_settings->m_measurementAdjChBandwidth);
ui->harmonics->setValue(m_settings->m_measurementHarmonics);
ui->peaks->setValue(m_settings->m_measurementPeaks);
displaySettings();
}
SpectrumMeasurementsDialog::~SpectrumMeasurementsDialog()
{}
void SpectrumMeasurementsDialog::displaySettings()
{
bool show = m_settings->m_measurement != SpectrumSettings::MeasurementNone;
ui->positionLabel->setVisible(show);
ui->position->setVisible(show);
ui->precisionLabel->setVisible(show);
ui->precision->setVisible(show);
ui->highlightLabel->setVisible(show);
ui->highlight->setVisible(show);
bool reset = (m_settings->m_measurement >= SpectrumSettings::MeasurementChannelPower);
ui->resetMeasurements->setVisible(reset && show);
bool bw = (m_settings->m_measurement == SpectrumSettings::MeasurementChannelPower)
|| (m_settings->m_measurement == SpectrumSettings::MeasurementAdjacentChannelPower);
ui->centerFrequencyOffsetLabel->setVisible(bw && show);
ui->centerFrequencyOffset->setVisible(bw && show);
ui->bandwidthLabel->setVisible(bw && show);
ui->bandwidth->setVisible(bw && show);
bool adj = m_settings->m_measurement == SpectrumSettings::MeasurementAdjacentChannelPower;
ui->chSpacingLabel->setVisible(adj && show);
ui->chSpacing->setVisible(adj && show);
ui->adjChBandwidthLabel->setVisible(adj && show);
ui->adjChBandwidth->setVisible(adj && show);
bool harmonics = (m_settings->m_measurement == SpectrumSettings::MeasurementSNR);
ui->harmonicsLabel->setVisible(harmonics && show);
ui->harmonics->setVisible(harmonics && show);
bool peaks = (m_settings->m_measurement == SpectrumSettings::MeasurementPeaks);
ui->peaksLabel->setVisible(peaks && show);
ui->peaks->setVisible(peaks && show);
}
void SpectrumMeasurementsDialog::on_measurement_currentIndexChanged(int index)
{
m_settings->m_measurement = (SpectrumSettings::Measurement)index;
displaySettings();
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_precision_valueChanged(int value)
{
m_settings->m_measurementPrecision = value;
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_position_currentIndexChanged(int index)
{
m_settings->m_measurementsPosition = (SpectrumSettings::MeasurementsPosition)index;
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_highlight_toggled(bool checked)
{
m_settings->m_measurementHighlight = checked;
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_resetMeasurements_clicked(bool checked)
{
(void) checked;
if (m_glSpectrum) {
m_glSpectrum->getMeasurements()->reset();
}
}
void SpectrumMeasurementsDialog::on_centerFrequencyOffset_changed(qint64 value)
{
m_settings->m_measurementCenterFrequencyOffset = value;
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_bandwidth_changed(qint64 value)
{
m_settings->m_measurementBandwidth = value;
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_chSpacing_changed(qint64 value)
{
m_settings->m_measurementChSpacing = value;
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_adjChBandwidth_changed(qint64 value)
{
m_settings->m_measurementAdjChBandwidth = value;
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_harmonics_valueChanged(int value)
{
m_settings->m_measurementHarmonics = value;
emit updateMeasurements();
}
void SpectrumMeasurementsDialog::on_peaks_valueChanged(int value)
{
m_settings->m_measurementPeaks = value;
emit updateMeasurements();
}

View File

@ -0,0 +1,63 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2022 Jon Beniston, M7RCE //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation as version 3 of the License, or //
// (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License V3 for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef SDRBASE_GUI_SPECTRUMMEASUREMENTSDIALOG_H_
#define SDRBASE_GUI_SPECTRUMMEASUREMENTSDIALOG_H_
#include <QDialog>
#include "dsp/spectrumsettings.h"
#include "export.h"
namespace Ui {
class SpectrumMeasurementsDialog;
}
class GLSpectrum;
class SDRGUI_API SpectrumMeasurementsDialog : public QDialog {
Q_OBJECT
public:
explicit SpectrumMeasurementsDialog(GLSpectrum *glSpectrum, SpectrumSettings *settings, QWidget *parent = nullptr);
~SpectrumMeasurementsDialog();
private:
void displaySettings();
Ui::SpectrumMeasurementsDialog *ui;
GLSpectrum *m_glSpectrum;
SpectrumSettings *m_settings;
private slots:
void on_measurement_currentIndexChanged(int index);
void on_precision_valueChanged(int value);
void on_position_currentIndexChanged(int index);
void on_highlight_toggled(bool checked);
void on_resetMeasurements_clicked(bool checked);
void on_centerFrequencyOffset_changed(qint64 value);
void on_bandwidth_changed(qint64 value);
void on_chSpacing_changed(qint64 value);
void on_adjChBandwidth_changed(qint64 value);
void on_harmonics_valueChanged(int value);
void on_peaks_valueChanged(int value);
signals:
void updateMeasurements();
};
#endif // SDRBASE_GUI_SPECTRUMMEASUREMENTSDIALOG_H_

View File

@ -0,0 +1,435 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SpectrumMeasurementsDialog</class>
<widget class="QDialog" name="SpectrumMeasurementsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>302</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>250</height>
</size>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Spectrum Measurements</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="highlightLabel">
<property name="text">
<string>Highlight on spectrum</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QSpinBox" name="harmonics">
<property name="toolTip">
<string>Number of harmonics</string>
</property>
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="ValueDialZ" name="centerFrequencyOffset" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Center frequency offset (Hz)</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="chSpacingLabel">
<property name="text">
<string>Channel spacing</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="position">
<property name="toolTip">
<string>Where to display the measurements result table</string>
</property>
<item>
<property name="text">
<string>Above spectrum</string>
</property>
</item>
<item>
<property name="text">
<string>Below spectrum</string>
</property>
</item>
<item>
<property name="text">
<string>Left of spectrum</string>
</property>
</item>
<item>
<property name="text">
<string>Right of spectrum</string>
</property>
</item>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="peaksLabel">
<property name="text">
<string>Peaks</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="centerFrequencyOffsetLabel">
<property name="text">
<string>Center frequency offset</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="measurement">
<property name="toolTip">
<string>Type of measurement</string>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Peaks</string>
</property>
</item>
<item>
<property name="text">
<string>Channel power</string>
</property>
</item>
<item>
<property name="text">
<string>Adjacent channel power</string>
</property>
</item>
<item>
<property name="text">
<string>SNR</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="positionLabel">
<property name="text">
<string>Display results table</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="bandwidthLabel">
<property name="text">
<string>Channel bandwidth</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="ValueDialZ" name="chSpacing" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Channel spacing (Hz)</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="precisionLabel">
<property name="text">
<string>Results precision</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="ValueDialZ" name="bandwidth" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Channel bandwidth (Hz)</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="measurementLabel">
<property name="text">
<string>Measurement</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="highlight">
<property name="toolTip">
<string>Whether to highlight the measurements in the spectrum</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="harmonicsLabel">
<property name="text">
<string>Harmonics</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="peaks">
<property name="toolTip">
<string>Number of peaks to display</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="adjChBandwidthLabel">
<property name="text">
<string>Adjacent channel bandwidth</string>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="precision">
<property name="toolTip">
<string>Precision of results (number of decimal places)</string>
</property>
<property name="maximum">
<number>9</number>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="ValueDialZ" name="adjChBandwidth" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>32</width>
<height>16</height>
</size>
</property>
<property name="font">
<font>
<family>DejaVu Sans Mono</family>
<pointsize>12</pointsize>
</font>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>Adjacent channel bandwidth (Hz)</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="resetMeasurements">
<property name="toolTip">
<string>Reset measurement results</string>
</property>
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ValueDialZ</class>
<extends>QWidget</extends>
<header>gui/valuedialz.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>measurement</tabstop>
<tabstop>position</tabstop>
<tabstop>peaks</tabstop>
<tabstop>harmonics</tabstop>
<tabstop>resetMeasurements</tabstop>
</tabstops>
<resources>
<include location="../resources/res.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SpectrumMeasurementsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>203</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SpectrumMeasurementsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>314</x>
<y>194</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>203</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -141,7 +141,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow())); connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow()));
connect(m_hideButton, SIGNAL(clicked()), this, SLOT(hide())); connect(m_hideButton, SIGNAL(clicked()), this, SLOT(hide()));
connect(spectrum, &GLSpectrum::requestCenterFrequency, this, &MainSpectrumGUI::onRequestCenterFrequency); connect(spectrum->getSpectrumView(), &GLSpectrumView::requestCenterFrequency, this, &MainSpectrumGUI::onRequestCenterFrequency);
connect(spectrumGUI, &GLSpectrumGUI::requestCenterFrequency, this, &MainSpectrumGUI::onRequestCenterFrequency); connect(spectrumGUI, &GLSpectrumGUI::requestCenterFrequency, this, &MainSpectrumGUI::onRequestCenterFrequency);
m_resizer.enableChildMouseTracking(); m_resizer.enableChildMouseTracking();