mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-22 08:04:49 -05:00
Merge pull request #1454 from srcejon/measurements_widget
Measurements widget
This commit is contained in:
commit
947eee4b29
@ -69,11 +69,15 @@ void SpectrumSettings::resetToDefaults()
|
||||
m_colorMap = "Angel";
|
||||
m_spectrumStyle = Line;
|
||||
m_measurement = MeasurementNone;
|
||||
m_measurementCenterFrequencyOffset = 0;
|
||||
m_measurementBandwidth = 10000;
|
||||
m_measurementChSpacing = 10000;
|
||||
m_measurementAdjChBandwidth = 10000;
|
||||
m_measurementHarmonics = 5;
|
||||
m_measurementPeaks = 5;
|
||||
m_measurementHighlight = true;
|
||||
m_measurementsPosition = PositionBelow;
|
||||
m_measurementPrecision = 1;
|
||||
}
|
||||
|
||||
QByteArray SpectrumSettings::serialize() const
|
||||
@ -120,6 +124,10 @@ QByteArray SpectrumSettings::serialize() const
|
||||
s.writeS32(39, m_measurementHarmonics);
|
||||
// 41, 42 used below
|
||||
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());
|
||||
|
||||
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(39, &m_measurementHarmonics, 5);
|
||||
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;
|
||||
d.readS32(100, &histogramMarkersSize, 0);
|
||||
|
@ -73,15 +73,17 @@ public:
|
||||
enum Measurement
|
||||
{
|
||||
MeasurementNone,
|
||||
MeasurementPeak,
|
||||
MeasurementPeaks,
|
||||
MeasurementChannelPower,
|
||||
MeasurementAdjacentChannelPower,
|
||||
MeasurementSNR,
|
||||
MeasurementSNFR,
|
||||
MeasurementTHD,
|
||||
MeasurementTHDPN,
|
||||
MeasurementSINAD,
|
||||
MeasurementSFDR
|
||||
MeasurementSNR
|
||||
};
|
||||
|
||||
enum MeasurementsPosition {
|
||||
PositionAbove,
|
||||
PositionBelow,
|
||||
PositionLeft,
|
||||
PositionRight
|
||||
};
|
||||
|
||||
int m_fftSize;
|
||||
@ -123,11 +125,16 @@ public:
|
||||
QString m_colorMap;
|
||||
SpectrumStyle m_spectrumStyle;
|
||||
Measurement m_measurement;
|
||||
int m_measurementCenterFrequencyOffset;
|
||||
int m_measurementBandwidth;
|
||||
int m_measurementChSpacing;
|
||||
int m_measurementAdjChBandwidth;
|
||||
int m_measurementHarmonics;
|
||||
int m_measurementPeaks;
|
||||
bool m_measurementHighlight;
|
||||
MeasurementsPosition m_measurementsPosition;
|
||||
int m_measurementPrecision;
|
||||
|
||||
static const int m_log2FFTSizeMin = 6; // 64
|
||||
static const int m_log2FFTSizeMax = 15; // 32k
|
||||
|
||||
|
@ -51,6 +51,7 @@ set(sdrgui_SOURCES
|
||||
gui/glshadertvarray.cpp
|
||||
gui/glspectrum.cpp
|
||||
gui/glspectrumgui.cpp
|
||||
gui/glspectrumview.cpp
|
||||
gui/graphicsdialog.cpp
|
||||
gui/graphicsviewzoom.cpp
|
||||
gui/httpdownloadmanagergui.cpp
|
||||
@ -69,6 +70,8 @@ set(sdrgui_SOURCES
|
||||
gui/sdrangelsplash.cpp
|
||||
gui/spectrumcalibrationpointsdialog.cpp
|
||||
gui/spectrummarkersdialog.cpp
|
||||
gui/spectrummeasurementsdialog.cpp
|
||||
gui/spectrummeasurements.cpp
|
||||
gui/tickedslider.cpp
|
||||
gui/timedelegate.cpp
|
||||
gui/transverterbutton.cpp
|
||||
@ -154,6 +157,7 @@ set(sdrgui_HEADERS
|
||||
gui/glshadertextured.h
|
||||
gui/glspectrum.h
|
||||
gui/glspectrumgui.h
|
||||
gui/glspectrumview.h
|
||||
gui/graphicsdialog.h
|
||||
gui/graphicsviewzoom.h
|
||||
gui/httpdownloadmanagergui.h
|
||||
@ -173,6 +177,8 @@ set(sdrgui_HEADERS
|
||||
gui/sdrangelsplash.h
|
||||
gui/spectrumcalibrationpointsdialog.h
|
||||
gui/spectrummarkersdialog.h
|
||||
gui/spectrummeasurementsdialog.h
|
||||
gui/spectrummeasurements.h
|
||||
gui/tickedslider.h
|
||||
gui/timedelegate.h
|
||||
gui/transverterbutton.h
|
||||
@ -239,6 +245,7 @@ set(sdrgui_FORMS
|
||||
gui/samplingdevicecontrol.ui
|
||||
gui/samplingdevicedialog.ui
|
||||
gui/spectrummarkersdialog.ui
|
||||
gui/spectrummeasurementsdialog.ui
|
||||
gui/spectrumcalibrationpointsdialog.ui
|
||||
gui/myposdialog.ui
|
||||
gui/transverterdialog.ui
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "dsp/dspdevicesourceengine.h"
|
||||
#include "dsp/dspdevicesinkengine.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "gui/glspectrumview.h"
|
||||
#include "gui/glspectrumgui.h"
|
||||
// #include "gui/channelwindow.h"
|
||||
#include "gui/workspace.h"
|
||||
@ -42,7 +43,7 @@
|
||||
|
||||
DeviceUISet::DeviceUISet(int deviceSetIndex, DeviceSet *deviceSet)
|
||||
{
|
||||
m_spectrum = new GLSpectrum;
|
||||
m_spectrum = new GLSpectrum();
|
||||
m_spectrum->setIsDeviceSpectrum(true);
|
||||
m_spectrumVis = deviceSet->m_spectrumVis;
|
||||
m_spectrumVis->setGLSpectrum(m_spectrum);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2016 F4EXB //
|
||||
// written by Edouard Griffiths //
|
||||
// //
|
||||
// OpenGL interface modernization. //
|
||||
// See: http://doc.qt.io/qt-5/qopenglshaderprogram.html //
|
||||
// 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 //
|
||||
@ -19,465 +15,102 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_GLSPECTRUM_H
|
||||
#define INCLUDE_GLSPECTRUM_H
|
||||
#ifndef SDRGUI_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 "util/incrementalarray.h"
|
||||
#include "util/message.h"
|
||||
#include "util/colormap.h"
|
||||
#include "glspectrumview.h"
|
||||
|
||||
class QOpenGLShaderProgram;
|
||||
class MessageQueue;
|
||||
class SpectrumVis;
|
||||
class QOpenGLDebugLogger;
|
||||
class QSplitter;
|
||||
class SpectrumMeasurements;
|
||||
|
||||
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
|
||||
|
||||
public:
|
||||
class MsgReportSampleRate : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
GLSpectrum(QWidget *parent = nullptr);
|
||||
GLSpectrumView *getSpectrumView() const { return m_spectrum; }
|
||||
SpectrumMeasurements *getMeasurements() const { return m_measurements; }
|
||||
void setMeasurementsVisible(bool visible);
|
||||
void setMeasurementsPosition(SpectrumSettings::MeasurementsPosition position);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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)
|
||||
void setCenterFrequency(qint64 frequency) { m_spectrum->setCenterFrequency(frequency); }
|
||||
qint64 getCenterFrequency() const { return m_spectrum->getCenterFrequency(); }
|
||||
float getPowerMax() const { return m_spectrum->getPowerMax(); }
|
||||
float getTimeMax() const { return m_spectrum->getTimeMax(); }
|
||||
void setSampleRate(qint32 sampleRate) { m_spectrum->setSampleRate(sampleRate); }
|
||||
void setTimingRate(qint32 timingRate) { m_spectrum->setTimingRate(timingRate); }
|
||||
void setFFTOverlap(int overlap) { m_spectrum->setFFTOverlap(overlap); }
|
||||
void setReferenceLevel(Real referenceLevel) { m_spectrum->setReferenceLevel(referenceLevel); }
|
||||
void setPowerRange(Real powerRange){ m_spectrum->setPowerRange(powerRange); }
|
||||
void setDecay(int decay) { m_spectrum->setDecay(decay); }
|
||||
void setDecayDivisor(int decayDivisor) { m_spectrum->setDecayDivisor(decayDivisor); }
|
||||
void setHistoStroke(int stroke) { m_spectrum->setHistoStroke(stroke); }
|
||||
void setDisplayWaterfall(bool display) { m_spectrum->setDisplayWaterfall(display); }
|
||||
void setDisplay3DSpectrogram(bool display) { m_spectrum->setDisplay3DSpectrogram(display); }
|
||||
void set3DSpectrogramStyle(SpectrumSettings::SpectrogramStyle style) { m_spectrum->set3DSpectrogramStyle(style); }
|
||||
void setColorMapName(const QString &colorMapName) { m_spectrum->setColorMapName(colorMapName); }
|
||||
void setSpectrumStyle(SpectrumSettings::SpectrumStyle style) { m_spectrum->setSpectrumStyle(style); }
|
||||
void setSsbSpectrum(bool ssbSpectrum) { m_spectrum->setSsbSpectrum(ssbSpectrum); }
|
||||
void setLsbDisplay(bool lsbDisplay) { m_spectrum->setLsbDisplay(lsbDisplay); }
|
||||
void setInvertedWaterfall(bool inv) { m_spectrum->setInvertedWaterfall(inv); }
|
||||
void setDisplayMaxHold(bool display) { m_spectrum->setDisplayMaxHold(display); }
|
||||
void setDisplayCurrent(bool display) { m_spectrum->setDisplayCurrent(display); }
|
||||
void setDisplayHistogram(bool display) { m_spectrum->setDisplayHistogram(display); }
|
||||
void setDisplayGrid(bool display) { m_spectrum->setDisplayGrid(display); }
|
||||
void setDisplayGridIntensity(int intensity) { m_spectrum->setDisplayGridIntensity(intensity); }
|
||||
void setDisplayTraceIntensity(int intensity) { m_spectrum->setDisplayTraceIntensity(intensity); }
|
||||
void setLinear(bool linear) { m_spectrum->setLinear(linear); }
|
||||
void setUseCalibration(bool useCalibration) { m_spectrum->setUseCalibration(useCalibration); }
|
||||
void setMeasurementParams(SpectrumSettings::Measurement measurement,
|
||||
int centerFrequencyOffset, int bandwidth, int chSpacing, int adjChBandwidth,
|
||||
int harmonics, int peaks, bool highlight, int precision)
|
||||
{
|
||||
m_displaySourceOrSink = sourceOrSink;
|
||||
m_displayStreamIndex = streamIndex;
|
||||
m_spectrum->setMeasurementParams(measurement, centerFrequencyOffset, bandwidth, chSpacing, adjChBandwidth, harmonics, peaks, highlight, precision);
|
||||
}
|
||||
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; }
|
||||
qint32 getSampleRate() const { return m_spectrum->getSampleRate(); }
|
||||
void addChannelMarker(ChannelMarker* channelMarker) { m_spectrum->addChannelMarker(channelMarker); }
|
||||
void removeChannelMarker(ChannelMarker* channelMarker) { m_spectrum->removeChannelMarker(channelMarker); }
|
||||
void setMessageQueueToGUI(MessageQueue* messageQueue) { m_spectrum->setMessageQueueToGUI(messageQueue); }
|
||||
void newSpectrum(const Real* spectrum, int nbBins, int fftSize) { m_spectrum->newSpectrum(spectrum, nbBins, fftSize); }
|
||||
void clearSpectrumHistogram() { m_spectrum->clearSpectrumHistogram(); }
|
||||
Real getWaterfallShare() const { return m_spectrum->getWaterfallShare(); }
|
||||
void setWaterfallShare(Real waterfallShare) { m_spectrum->setWaterfallShare(waterfallShare); }
|
||||
void setFPSPeriodMs(int fpsPeriodMs) { m_spectrum->setFPSPeriodMs(fpsPeriodMs); }
|
||||
void setDisplayedStream(bool sourceOrSink, int streamIndex) { m_spectrum->setDisplayedStream(sourceOrSink, streamIndex); }
|
||||
void setSpectrumVis(SpectrumVis *spectrumVis) { m_spectrum->setSpectrumVis(spectrumVis); }
|
||||
SpectrumVis *getSpectrumVis() { return m_spectrum->getSpectrumVis(); }
|
||||
const QList<SpectrumHistogramMarker>& getHistogramMarkers() const { return m_spectrum->getHistogramMarkers(); }
|
||||
QList<SpectrumHistogramMarker>& getHistogramMarkers() { return m_spectrum->getHistogramMarkers(); }
|
||||
void setHistogramMarkers(const QList<SpectrumHistogramMarker>& histogramMarkers) { m_spectrum->setHistogramMarkers(histogramMarkers); }
|
||||
const QList<SpectrumWaterfallMarker>& getWaterfallMarkers() const { return m_spectrum->getWaterfallMarkers(); }
|
||||
QList<SpectrumWaterfallMarker>& getWaterfallMarkers() { return m_spectrum->getWaterfallMarkers(); }
|
||||
void setWaterfallMarkers(const QList<SpectrumWaterfallMarker>& waterfallMarkers) { m_spectrum->setWaterfallMarkers(waterfallMarkers); }
|
||||
const QList<SpectrumAnnotationMarker>& getAnnotationMarkers() const { return m_spectrum->getAnnotationMarkers(); }
|
||||
QList<SpectrumAnnotationMarker>& getAnnotationMarkers() { return m_spectrum->getAnnotationMarkers(); }
|
||||
void setAnnotationMarkers(const QList<SpectrumAnnotationMarker>& annotationMarkers) { m_spectrum->setAnnotationMarkers(annotationMarkers); };
|
||||
void updateHistogramMarkers() { m_spectrum->updateHistogramMarkers(); }
|
||||
void updateWaterfallMarkers() { m_spectrum->updateWaterfallMarkers(); }
|
||||
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:
|
||||
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;
|
||||
QSplitter *m_splitter;
|
||||
GLSpectrumView *m_spectrum;
|
||||
SpectrumMeasurements *m_measurements;
|
||||
|
||||
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_
|
||||
|
@ -28,10 +28,13 @@
|
||||
#include "dsp/fftwindow.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
#include "gui/glspectrum.h"
|
||||
#include "gui/glspectrumview.h"
|
||||
#include "gui/crightclickenabler.h"
|
||||
#include "gui/wsspectrumsettingsdialog.h"
|
||||
#include "gui/spectrummarkersdialog.h"
|
||||
#include "gui/spectrumcalibrationpointsdialog.h"
|
||||
#include "gui/spectrummeasurementsdialog.h"
|
||||
#include "gui/spectrummeasurements.h"
|
||||
#include "gui/flowlayout.h"
|
||||
#include "util/colormap.h"
|
||||
#include "util/simpleserializer.h"
|
||||
@ -51,7 +54,6 @@ GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) :
|
||||
ui->setupUi(this);
|
||||
|
||||
// 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->Line5Layout);
|
||||
ui->verticalLayout->removeItem(ui->Line4Layout);
|
||||
@ -65,11 +67,10 @@ GLSpectrumGUI::GLSpectrumGUI(QWidget* parent) :
|
||||
flowLayout->addItem(ui->Line4Layout);
|
||||
flowLayout->addItem(ui->Line5Layout);
|
||||
flowLayout->addItem(ui->Line6Layout);
|
||||
flowLayout->addItem(ui->Line7Layout);
|
||||
ui->verticalLayout->addItem(flowLayout);
|
||||
|
||||
on_linscale_toggled(false);
|
||||
on_measurement_currentIndexChanged(0);
|
||||
//displayMeasurementGUI();
|
||||
|
||||
QString levelStyle = QString(
|
||||
"QSpinBox {background-color: rgb(79, 79, 79);}"
|
||||
@ -229,18 +230,13 @@ void GLSpectrumGUI::displaySettings()
|
||||
ui->calibration->setChecked(m_settings.m_useCalibration);
|
||||
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->averaging->blockSignals(false);
|
||||
ui->averagingMode->blockSignals(false);
|
||||
ui->linscale->blockSignals(false);
|
||||
blockApplySettings(false);
|
||||
|
||||
updateMeasurements();
|
||||
}
|
||||
|
||||
void GLSpectrumGUI::displayGotoMarkers()
|
||||
@ -342,15 +338,6 @@ void GLSpectrumGUI::applySpectrumSettings()
|
||||
m_glSpectrum->setMarkersDisplay(m_settings.m_markersDisplay);
|
||||
m_glSpectrum->setCalibrationPoints(m_settings.m_calibrationPoints);
|
||||
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)
|
||||
@ -860,7 +847,7 @@ void GLSpectrumGUI::setMaximumOverlap()
|
||||
|
||||
bool GLSpectrumGUI::handleMessage(const Message& message)
|
||||
{
|
||||
if (GLSpectrum::MsgReportSampleRate::match(message))
|
||||
if (GLSpectrumView::MsgReportSampleRate::match(message))
|
||||
{
|
||||
setAveragingToolitp();
|
||||
setFFTSizeToolitp();
|
||||
@ -886,24 +873,24 @@ bool GLSpectrumGUI::handleMessage(const Message& message)
|
||||
ui->wsSpectrum->blockSignals(false);
|
||||
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();
|
||||
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();
|
||||
ui->fftOverlap->blockSignals(true);
|
||||
ui->fftOverlap->setValue(m_settings.m_fftOverlap);
|
||||
ui->fftOverlap->blockSignals(false);
|
||||
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_powerRange = report.getRange();
|
||||
ui->refLevel->blockSignals(true);
|
||||
@ -914,9 +901,9 @@ bool GLSpectrumGUI::handleMessage(const Message& message)
|
||||
ui->refLevel->blockSignals(false);
|
||||
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();
|
||||
ui->refLevel->blockSignals(true);
|
||||
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);
|
||||
ui->highlight->setVisible(highlight);
|
||||
connect(&measurementsDialog, &SpectrumMeasurementsDialog::updateMeasurements, this, &GLSpectrumGUI::updateMeasurements);
|
||||
|
||||
bool bw = (m_settings.m_measurement == SpectrumSettings::MeasurementChannelPower)
|
||||
|| (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();
|
||||
measurementsDialog.exec();
|
||||
}
|
||||
|
||||
void GLSpectrumGUI::on_highlight_toggled(bool checked)
|
||||
void GLSpectrumGUI::updateMeasurements()
|
||||
{
|
||||
m_settings.m_measurementHighlight = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void GLSpectrumGUI::on_bandwidth_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_measurementBandwidth = value;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void GLSpectrumGUI::on_chSpacing_valueChanged(int value)
|
||||
{
|
||||
m_settings.m_measurementChSpacing = value;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
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();
|
||||
if (m_glSpectrum)
|
||||
{
|
||||
m_glSpectrum->setMeasurementsVisible(m_settings.m_measurement != SpectrumSettings::MeasurementNone);
|
||||
m_glSpectrum->setMeasurementsPosition(m_settings.m_measurementsPosition);
|
||||
m_glSpectrum->setMeasurementParams(
|
||||
m_settings.m_measurement,
|
||||
m_settings.m_measurementCenterFrequencyOffset,
|
||||
m_settings.m_measurementBandwidth,
|
||||
m_settings.m_measurementChSpacing,
|
||||
m_settings.m_measurementAdjChBandwidth,
|
||||
m_settings.m_measurementHarmonics,
|
||||
m_settings.m_measurementPeaks,
|
||||
m_settings.m_measurementHighlight,
|
||||
m_settings.m_measurementPrecision
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -123,12 +123,7 @@ private slots:
|
||||
void on_calibration_toggled(bool checked);
|
||||
void on_gotoMarker_currentIndexChanged(int index);
|
||||
|
||||
void on_measurement_currentIndexChanged(int index);
|
||||
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 on_measure_clicked(bool checked);
|
||||
|
||||
void handleInputMessages();
|
||||
void openWebsocketSpectrumSettingsDialog(const QPoint& p);
|
||||
@ -139,6 +134,7 @@ private slots:
|
||||
void updateAnnotationMarkers();
|
||||
void updateMarkersDisplay();
|
||||
void updateCalibrationPoints();
|
||||
void updateMeasurements();
|
||||
|
||||
signals:
|
||||
// Emitted when user selects an annotation marker
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>630</width>
|
||||
<height>274</height>
|
||||
<height>228</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
@ -1073,6 +1073,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
<widget class="ButtonSwitch" name="calibration">
|
||||
<property name="toolTip">
|
||||
@ -1132,185 +1152,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</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>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
4694
sdrgui/gui/glspectrumview.cpp
Normal file
4694
sdrgui/gui/glspectrumview.cpp
Normal file
File diff suppressed because it is too large
Load Diff
497
sdrgui/gui/glspectrumview.h
Normal file
497
sdrgui/gui/glspectrumview.h
Normal 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
|
625
sdrgui/gui/spectrummeasurements.cpp
Normal file
625
sdrgui/gui/spectrummeasurements.cpp
Normal 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";
|
||||
}
|
||||
}
|
149
sdrgui/gui/spectrummeasurements.h
Normal file
149
sdrgui/gui/spectrummeasurements.h
Normal 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_
|
173
sdrgui/gui/spectrummeasurementsdialog.cpp
Normal file
173
sdrgui/gui/spectrummeasurementsdialog.cpp
Normal 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();
|
||||
}
|
63
sdrgui/gui/spectrummeasurementsdialog.h
Normal file
63
sdrgui/gui/spectrummeasurementsdialog.h
Normal 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_
|
435
sdrgui/gui/spectrummeasurementsdialog.ui
Normal file
435
sdrgui/gui/spectrummeasurementsdialog.ui
Normal 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>
|
@ -141,7 +141,7 @@ MainSpectrumGUI::MainSpectrumGUI(GLSpectrum *spectrum, GLSpectrumGUI *spectrumGU
|
||||
connect(this, SIGNAL(forceShrink()), this, SLOT(shrinkWindow()));
|
||||
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);
|
||||
|
||||
m_resizer.enableChildMouseTracking();
|
||||
|
Loading…
Reference in New Issue
Block a user