From 9d5d7b1113bd4a2b54320b4461187431ce55f238 Mon Sep 17 00:00:00 2001 From: f4exb Date: Sat, 5 Feb 2022 11:56:08 +0100 Subject: [PATCH] Spectrum calibration: further adjustments and documentation --- doc/img/Spectrum_Calibration_buttons.png | Bin 0 -> 4953 bytes doc/img/Spectrum_Calibration_buttons.xcf | Bin 0 -> 17549 bytes doc/img/Spectrum_Calibration_dialog.png | Bin 0 -> 24386 bytes doc/img/Spectrum_Calibration_dialog.xcf | Bin 0 -> 112220 bytes sdrbase/dsp/spectrumsettings.cpp | 4 + sdrbase/dsp/spectrumsettings.h | 4 +- sdrbase/resources/webapi/doc/html2/index.html | 6 +- .../doc/swagger/include/GLSpectrum.yaml | 6 ++ sdrgui/gui/glspectrum.cpp | 2 +- sdrgui/gui/glspectrumgui.cpp | 1 + sdrgui/gui/glspectrumgui.ui | 2 +- .../gui/spectrumcalibrationpointsdialog.cpp | 71 ++++++++---- sdrgui/gui/spectrumcalibrationpointsdialog.h | 8 ++ sdrgui/gui/spectrumcalibrationpointsdialog.ui | 81 ++++++++++---- sdrgui/gui/spectrummarkersdialog.cpp | 15 +-- sdrgui/gui/spectrummarkersdialog.h | 4 +- sdrgui/gui/spectrummarkersdialog.ui | 44 +++++--- sdrgui/spectrumcalibration.md | 101 ++++++++++++++++++ .../api/swagger/include/GLSpectrum.yaml | 6 ++ swagger/sdrangel/code/html2/index.html | 6 +- .../code/qt5/client/SWGGLSpectrum.cpp | 23 ++++ .../sdrangel/code/qt5/client/SWGGLSpectrum.h | 6 ++ 22 files changed, 321 insertions(+), 69 deletions(-) create mode 100644 doc/img/Spectrum_Calibration_buttons.png create mode 100644 doc/img/Spectrum_Calibration_buttons.xcf create mode 100644 doc/img/Spectrum_Calibration_dialog.png create mode 100644 doc/img/Spectrum_Calibration_dialog.xcf create mode 100644 sdrgui/spectrumcalibration.md diff --git a/doc/img/Spectrum_Calibration_buttons.png b/doc/img/Spectrum_Calibration_buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..2d740783189cb8a430694707a53dc6d6a8c3318d GIT binary patch literal 4953 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 KQLa+73jH4d4sE9Z literal 0 HcmV?d00001 diff --git a/doc/img/Spectrum_Calibration_buttons.xcf b/doc/img/Spectrum_Calibration_buttons.xcf new file mode 100644 index 0000000000000000000000000000000000000000..e2a89740a6154be7932182fa2f62099257f3b9ab GIT binary patch literal 17549 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 DlU0-I literal 0 HcmV?d00001 diff --git a/doc/img/Spectrum_Calibration_dialog.png b/doc/img/Spectrum_Calibration_dialog.png new file mode 100644 index 0000000000000000000000000000000000000000..569fb447cefcd34a8b4472a45cca262a484a416e GIT binary patch literal 24386 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 n-9)setWaterfallShare(m_waterfallShare); swgSpectrum->setMarkersDisplay((int) m_markersDisplay); swgSpectrum->setUseCalibration(m_useCalibration ? 1 : 0); + swgSpectrum->setCalibrationInterpMode((int) m_calibrationInterpMode); if (m_histogramMarkers.size() > 0) { @@ -455,6 +456,9 @@ void SpectrumSettings::updateFrom(const QStringList& keys, const SWGSDRangel::SW if (keys.contains("spectrumConfig.useCalibration")) { m_useCalibration = swgSpectrum->getUseCalibration() != 0; } + if (keys.contains("spectrumConfig.calibrationInterpMode")) { + m_calibrationInterpMode = (CalibrationInterpolationMode) swgSpectrum->getCalibrationInterpMode(); + } if (keys.contains("spectrumConfig.histogramMarkers")) { diff --git a/sdrbase/dsp/spectrumsettings.h b/sdrbase/dsp/spectrumsettings.h index f5dfba8e8..0d917848b 100644 --- a/sdrbase/dsp/spectrumsettings.h +++ b/sdrbase/dsp/spectrumsettings.h @@ -48,8 +48,8 @@ public: enum CalibrationInterpolationMode { - CalibInterpLinear, //!< linear power, linear frequency - CalibInterpLogLinear //!< log power, linear frequency + CalibInterpLinear, //!< linear power + CalibInterpLog //!< log power (dB linear) }; int m_fftSize; diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 1f4433d50..3b0b113d2 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -6159,6 +6159,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "boolean" }, + "calibrationInterpMode" : { + "type" : "integer", + "description" : "Interpolation scheme between points SpectrumSettings::CalibrationInterpolationMode\n * 0 - Linear\n * 1 - Log (dB linear)\n" + }, "histogramMarkers" : { "type" : "array", "items" : { @@ -52000,7 +52004,7 @@ except ApiException as e:
- Generated 2022-01-30T07:47:20.311+01:00 + Generated 2022-02-05T07:11:11.381+01:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/GLSpectrum.yaml b/sdrbase/resources/webapi/doc/swagger/include/GLSpectrum.yaml index 2795c9cd3..7a1308aa8 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/GLSpectrum.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/GLSpectrum.yaml @@ -163,6 +163,12 @@ GLSpectrum: useCalibration: type: integer description: boolean + calibrationInterpMode: + type: integer + description: > + Interpolation scheme between points SpectrumSettings::CalibrationInterpolationMode + * 0 - Linear + * 1 - Log (dB linear) histogramMarkers: type: array items: diff --git a/sdrgui/gui/glspectrum.cpp b/sdrgui/gui/glspectrum.cpp index e67d5e723..6eb0da297 100644 --- a/sdrgui/gui/glspectrum.cpp +++ b/sdrgui/gui/glspectrum.cpp @@ -2433,7 +2433,7 @@ void GLSpectrum::updateCalibrationPoints() m_calibrationGain = gainLow + interpolationRatio*(gainHigh - gainLow); // linear driven m_calibrationShiftdB = CalcDb::dbPower(m_calibrationGain); } - else if (m_calibrationInterpMode == SpectrumSettings::CalibInterpLogLinear) + else if (m_calibrationInterpMode == SpectrumSettings::CalibInterpLog) { m_calibrationShiftdB = CalcDb::dbPower(gainLow) + interpolationRatio*(CalcDb::dbPower(gainHigh) - CalcDb::dbPower(gainLow)); // log driven diff --git a/sdrgui/gui/glspectrumgui.cpp b/sdrgui/gui/glspectrumgui.cpp index c7f36b739..d77d73abc 100644 --- a/sdrgui/gui/glspectrumgui.cpp +++ b/sdrgui/gui/glspectrumgui.cpp @@ -379,6 +379,7 @@ void GLSpectrumGUI::on_markers_clicked(bool checked) m_glSpectrum->getWaterfallMarkers(), m_glSpectrum->getAnnotationMarkers(), m_glSpectrum->getMarkersDisplay(), + m_calibrationShiftdB, this ); diff --git a/sdrgui/gui/glspectrumgui.ui b/sdrgui/gui/glspectrumgui.ui index 43f83b5e3..85c249475 100644 --- a/sdrgui/gui/glspectrumgui.ui +++ b/sdrgui/gui/glspectrumgui.ui @@ -428,7 +428,7 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - -150 + -200 40 diff --git a/sdrgui/gui/spectrumcalibrationpointsdialog.cpp b/sdrgui/gui/spectrumcalibrationpointsdialog.cpp index b81df40bf..17f20fda4 100644 --- a/sdrgui/gui/spectrumcalibrationpointsdialog.cpp +++ b/sdrgui/gui/spectrumcalibrationpointsdialog.cpp @@ -41,22 +41,24 @@ SpectrumCalibrationPointsDialog::SpectrumCalibrationPointsDialog( m_markerZero(markerZero), m_calibrationPointIndex(0), m_centerFrequency(0), - m_globalCorrection(0.0) + m_globalCorrection(0.0), + m_setElseCorrectGlobal(false) { ui->setupUi(this); ui->calibPointFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->calibPointFrequency->setValueRange(false, 10, -9999999999L, 9999999999L); ui->calibrationGlobalCorr->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); - ui->calibrationGlobalCorr->setValueRange(false, 5, -15000L, 4000L, 2); + ui->calibrationGlobalCorr->setValueRange(false, 5, -20000L, 4000L, 2); ui->relativePower->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); - ui->relativePower->setValueRange(false, 5, -15000L, 4000L, 2); + ui->relativePower->setValueRange(false, 5, -20000L, 4000L, 2); ui->calibratedPower->setColorMapper(ColorMapper(ColorMapper::GrayYellow)); - ui->calibratedPower->setValueRange(false, 5, -15000L, 4000L, 2); + ui->calibratedPower->setValueRange(false, 5, -20000L, 4000L, 2); ui->calibPoint->setMaximum(m_calibrationPoints.size() - 1); ui->calibInterpMode->blockSignals(true); ui->calibInterpMode->setCurrentIndex((int) m_calibrationInterpMode); ui->calibInterpMode->blockSignals(false); ui->calibrationGlobalCorr->setValue(m_globalCorrection * 100.0); + ui->corrOrSet->setText("Cor"); displayCalibrationPoint(); } @@ -91,9 +93,9 @@ void SpectrumCalibrationPointsDialog::displayCalibrationPoint() ui->centerFrequency->setEnabled(true); ui->calibPoint->setValue(m_calibrationPointIndex); ui->calibPointText->setText(tr("%1").arg(m_calibrationPointIndex)); - double powerDB = CalcDb::dbPower(m_calibrationPoints[m_calibrationPointIndex].m_powerRelativeReference); + double powerDB = CalcDb::dbPower(m_calibrationPoints[m_calibrationPointIndex].m_powerRelativeReference, 1e-20); ui->relativePower->setValue(round(powerDB*100.0)); // fixed point 2 decimals - powerDB = CalcDb::dbPower(m_calibrationPoints[m_calibrationPointIndex].m_powerCalibratedReference); + powerDB = CalcDb::dbPower(m_calibrationPoints[m_calibrationPointIndex].m_powerCalibratedReference, 1e-20); ui->calibratedPower->setValue(round(powerDB*100.0)); // fixed point 2 decimals ui->calibPointFrequency->setValue(m_calibrationPoints[m_calibrationPointIndex].m_frequency); } @@ -183,6 +185,17 @@ void SpectrumCalibrationPointsDialog::on_calibPointDuplicate_clicked() displayCalibrationPoint(); } +void SpectrumCalibrationPointsDialog::on_calibPointsSort_clicked() +{ + if (m_calibrationPoints.size() == 0) { + return; + } + + std::sort(m_calibrationPoints.begin(), m_calibrationPoints.end(), calibrationPointsLessThan); + m_calibrationPointIndex = 0; + displayCalibrationPoint(); +} + void SpectrumCalibrationPointsDialog::on_importMarkerZero_clicked() { if ((m_calibrationPoints.size() == 0) || (m_markerZero == nullptr)) { @@ -217,10 +230,26 @@ void SpectrumCalibrationPointsDialog::on_calibrationGlobalCorr_changed(qint64 va m_globalCorrection = value / 100.0; } +void SpectrumCalibrationPointsDialog::on_corrOrSet_toggled(bool checked) +{ + if (checked) { + ui->corrOrSet->setText("Set"); + } else { + ui->corrOrSet->setText("Cor"); + } + + m_setElseCorrectGlobal = checked; +} + void SpectrumCalibrationPointsDialog::on_globalRelativeCorrection_clicked() { - for (auto& calibrationPoint : m_calibrationPoints) { - calibrationPoint.m_powerRelativeReference *= CalcDb::powerFromdB(m_globalCorrection); + for (auto& calibrationPoint : m_calibrationPoints) + { + if (m_setElseCorrectGlobal) { + calibrationPoint.m_powerRelativeReference = CalcDb::powerFromdB(m_globalCorrection); + } else { + calibrationPoint.m_powerRelativeReference *= CalcDb::powerFromdB(m_globalCorrection); + } } displayCalibrationPoint(); @@ -229,8 +258,13 @@ void SpectrumCalibrationPointsDialog::on_globalRelativeCorrection_clicked() void SpectrumCalibrationPointsDialog::on_globalCalibratedCorrection_clicked() { - for (auto& calibrationPoint : m_calibrationPoints) { - calibrationPoint.m_powerCalibratedReference *= CalcDb::powerFromdB(m_globalCorrection); + for (auto& calibrationPoint : m_calibrationPoints) + { + if (m_setElseCorrectGlobal) { + calibrationPoint.m_powerCalibratedReference = CalcDb::powerFromdB(m_globalCorrection); + } else { + calibrationPoint.m_powerCalibratedReference *= CalcDb::powerFromdB(m_globalCorrection); + } } displayCalibrationPoint(); @@ -259,13 +293,14 @@ void SpectrumCalibrationPointsDialog::on_calibPointsExport_clicked() { QTextStream stream; stream.setDevice(&file); - stream << "Frequency,Reference,Absolute\n"; + stream << "Frequency,Relative,Calibrated\n"; for (const auto &calibrationPint : m_calibrationPoints) { + stream << calibrationPint.m_frequency << "," - << calibrationPint.m_powerRelativeReference << "," - << calibrationPint.m_powerCalibratedReference << "\n"; + << CalcDb::dbPower(calibrationPint.m_powerRelativeReference, 1e-20) << "," + << CalcDb::dbPower(calibrationPint.m_powerCalibratedReference, 1e-20) << "\n"; } stream.flush(); @@ -299,7 +334,7 @@ void SpectrumCalibrationPointsDialog::on_calibPointsImport_clicked() QString error; QHash colIndexes = CSV::readHeader( in, - {"Frequency", "Reference", "Absolute"}, + {"Frequency", "Relative", "Calibrated"}, error ); @@ -307,8 +342,8 @@ void SpectrumCalibrationPointsDialog::on_calibPointsImport_clicked() { QStringList cols; int frequencyCol = colIndexes.value("Frequency"); - int referenceCol = colIndexes.value("Reference"); - int absoluteCol = colIndexes.value("Absolute"); + int referenceCol = colIndexes.value("Relative"); + int absoluteCol = colIndexes.value("Calibrated"); m_calibrationPoints.clear(); @@ -316,8 +351,8 @@ void SpectrumCalibrationPointsDialog::on_calibPointsImport_clicked() { m_calibrationPoints.push_back(SpectrumCalibrationPoint()); m_calibrationPoints.back().m_frequency = cols[frequencyCol].toLongLong(); - m_calibrationPoints.back().m_powerRelativeReference = cols[referenceCol].toFloat(); - m_calibrationPoints.back().m_powerCalibratedReference = cols[absoluteCol].toFloat(); + m_calibrationPoints.back().m_powerRelativeReference = CalcDb::powerFromdB(cols[referenceCol].toFloat()); + m_calibrationPoints.back().m_powerCalibratedReference = CalcDb::powerFromdB(cols[absoluteCol].toFloat()); } m_calibrationPointIndex = 0; diff --git a/sdrgui/gui/spectrumcalibrationpointsdialog.h b/sdrgui/gui/spectrumcalibrationpointsdialog.h index 2b186eb34..98fa1da6f 100644 --- a/sdrgui/gui/spectrumcalibrationpointsdialog.h +++ b/sdrgui/gui/spectrumcalibrationpointsdialog.h @@ -53,9 +53,15 @@ private: int m_calibrationPointIndex; qint64 m_centerFrequency; double m_globalCorrection; + bool m_setElseCorrectGlobal; void displayCalibrationPoint(); + static bool calibrationPointsLessThan(const SpectrumCalibrationPoint& m1, const SpectrumCalibrationPoint& m2) + { + return m1.m_frequency < m2.m_frequency; + } + private slots: void on_calibPoint_valueChanged(int value); void on_calibPointAdd_clicked(); @@ -64,9 +70,11 @@ private slots: void on_calibratedPower_changed(qint64 value); void on_calibPointFrequency_changed(qint64 value); void on_calibPointDuplicate_clicked(); + void on_calibPointsSort_clicked(); void on_importMarkerZero_clicked(); void on_centerFrequency_clicked(); void on_calibInterpMode_currentIndexChanged(int index); + void on_corrOrSet_toggled(bool checked); void on_calibrationGlobalCorr_changed(qint64 value); void on_globalRelativeCorrection_clicked(); void on_globalCalibratedCorrection_clicked(); diff --git a/sdrgui/gui/spectrumcalibrationpointsdialog.ui b/sdrgui/gui/spectrumcalibrationpointsdialog.ui index 2b6df6046..c7b72db01 100644 --- a/sdrgui/gui/spectrumcalibrationpointsdialog.ui +++ b/sdrgui/gui/spectrumcalibrationpointsdialog.ui @@ -7,7 +7,7 @@ 0 0 466 - 159 + 162 @@ -198,7 +198,7 @@ - 15 + 24 0 @@ -243,12 +243,6 @@ - - - 32 - 0 - - dB @@ -268,7 +262,7 @@ - 15 + 24 0 @@ -313,12 +307,6 @@ - - - 32 - 0 - - dB @@ -327,6 +315,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -410,6 +411,26 @@ + + + + + 24 + 24 + + + + Sort points by increasing frequency + + + + + + + :/sort.png:/sort.png + + + @@ -480,21 +501,36 @@ - Lin + Linear - LogLin + Log - + + + + 52 + 0 + + + + Correct or set value + Cor + + true + + + false + @@ -524,7 +560,7 @@ Qt::StrongFocus - Correction (dB) + Correction or set (dB) @@ -544,7 +580,7 @@ - Apply gain correction to all relative values + Set or apply correction to all relative values Rel @@ -560,7 +596,7 @@ - Apply gain correction to all calibrated values + Set or apply correction to all calibrated values Cal @@ -606,6 +642,9 @@ :/import.png:/import.png + + true + diff --git a/sdrgui/gui/spectrummarkersdialog.cpp b/sdrgui/gui/spectrummarkersdialog.cpp index a75555e9c..8309d8cb6 100644 --- a/sdrgui/gui/spectrummarkersdialog.cpp +++ b/sdrgui/gui/spectrummarkersdialog.cpp @@ -32,6 +32,7 @@ SpectrumMarkersDialog::SpectrumMarkersDialog( QList& waterfallMarkers, QList& annotationMarkers, SpectrumSettings::MarkersDisplay& markersDisplay, + float calibrationShiftdB, QWidget* parent) : QDialog(parent), ui(new Ui::SpectrumMarkersDialog), @@ -39,6 +40,7 @@ SpectrumMarkersDialog::SpectrumMarkersDialog( m_waterfallMarkers(waterfallMarkers), m_annotationMarkers(annotationMarkers), m_markersDisplay(markersDisplay), + m_calibrationShiftdB(calibrationShiftdB), m_histogramMarkerIndex(0), m_waterfallMarkerIndex(0), m_annotationMarkerIndex(0), @@ -56,8 +58,10 @@ SpectrumMarkersDialog::SpectrumMarkersDialog( ui->aMarkerFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->aMarkerFrequency->setValueRange(false, 10, -9999999999L, 9999999999L); ui->aMarker->setMaximum(m_annotationMarkers.size() - 1); - ui->aMarkerBandwidth->setColorMapper(ColorMapper::GrayYellow); + ui->aMarkerBandwidth->setColorMapper(ColorMapper::GrayGreenYellow); ui->aMarkerBandwidth->setValueRange(true, 9, 0, 999999999L); + ui->fixedPower->setColorMapper(ColorMapper::GrayYellow); + ui->fixedPower->setValueRange(false, 4, -2000, 400, 1); ui->showSelect->setCurrentIndex((int) m_markersDisplay); displayHistogramMarker(); displayWaterfallMarker(); @@ -87,7 +91,6 @@ void SpectrumMarkersDialog::displayHistogramMarker() ui->marker->setValue(0); ui->markerText->setText("-"); ui->fixedPower->setValue(0); - ui->fixedPowerText->setText(tr("0.0")); } else { @@ -100,9 +103,8 @@ void SpectrumMarkersDialog::displayHistogramMarker() ui->markerText->setText(tr("%1").arg(m_histogramMarkerIndex)); ui->markerFrequency->setValue(m_histogramMarkers[m_histogramMarkerIndex].m_frequency); ui->powerMode->setCurrentIndex((int) m_histogramMarkers[m_histogramMarkerIndex].m_markerType); - float powerDB = CalcDb::dbPower(m_histogramMarkers[m_histogramMarkerIndex].m_power); + float powerDB = CalcDb::dbPower(m_histogramMarkers[m_histogramMarkerIndex].m_power) + m_calibrationShiftdB; ui->fixedPower->setValue(powerDB*10); - ui->fixedPowerText->setText(QString::number(powerDB, 'f', 1)); int r,g,b,a; m_histogramMarkers[m_histogramMarkerIndex].m_markerColor.getRgb(&r, &g, &b, &a); ui->markerColor->setStyleSheet(tr("QLabel { background-color : rgb(%1,%2,%3); }").arg(r).arg(g).arg(b)); @@ -304,14 +306,13 @@ void SpectrumMarkersDialog::on_showMarker_clicked(bool clicked) m_histogramMarkers[m_histogramMarkerIndex].m_show = clicked; } -void SpectrumMarkersDialog::on_fixedPower_valueChanged(int value) +void SpectrumMarkersDialog::on_fixedPower_changed(qint64 value) { if (m_histogramMarkers.size() == 0) { return; } - float powerDB = value / 10.0f; - ui->fixedPowerText->setText(QString::number(powerDB, 'f', 1)); + float powerDB = (value / 10.0f) - m_calibrationShiftdB; m_histogramMarkers[m_histogramMarkerIndex].m_power = CalcDb::powerFromdB(powerDB); emit updateHistogram(); } diff --git a/sdrgui/gui/spectrummarkersdialog.h b/sdrgui/gui/spectrummarkersdialog.h index 6da8d6413..e3872f98d 100644 --- a/sdrgui/gui/spectrummarkersdialog.h +++ b/sdrgui/gui/spectrummarkersdialog.h @@ -39,6 +39,7 @@ public: QList& waterfallMarkers, QList& annotationMarkers, SpectrumSettings::MarkersDisplay& markersDisplay, + float calibrationShiftdB, QWidget* parent = nullptr ); ~SpectrumMarkersDialog(); @@ -52,6 +53,7 @@ private: QList& m_waterfallMarkers; QList& m_annotationMarkers; SpectrumSettings::MarkersDisplay& m_markersDisplay; + float m_calibrationShiftdB; int m_histogramMarkerIndex; int m_waterfallMarkerIndex; int m_annotationMarkerIndex; @@ -71,7 +73,7 @@ private slots: void on_centerFrequency_clicked(); void on_markerColor_clicked(); void on_showMarker_clicked(bool clicked); - void on_fixedPower_valueChanged(int value); + void on_fixedPower_changed(qint64 value); void on_marker_valueChanged(int value); void on_setReference_clicked(); void on_markerAdd_clicked(); diff --git a/sdrgui/gui/spectrummarkersdialog.ui b/sdrgui/gui/spectrummarkersdialog.ui index 1f3b1fc5b..366cf5cd9 100644 --- a/sdrgui/gui/spectrummarkersdialog.ui +++ b/sdrgui/gui/spectrummarkersdialog.ui @@ -431,26 +431,38 @@ - + + + + 0 + 0 + + + + + 32 + 16 + + + + + DejaVu Sans Mono + 12 + + + + PointingHandCursor + + + Qt::StrongFocus + Fixed power (dB) - - -1500 - - - 1500 - - - 1 - - - Qt::Horizontal - - + 32 @@ -458,10 +470,10 @@ - -100.0 + dB - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter diff --git a/sdrgui/spectrumcalibration.md b/sdrgui/spectrumcalibration.md new file mode 100644 index 000000000..3c4171b4c --- /dev/null +++ b/sdrgui/spectrumcalibration.md @@ -0,0 +1,101 @@ +

Spectrum calibration management

+ +The spectrum calibration is controlled by this dialog. It manages the list of calibration points (calibration chart), genaral options and the import/export from/to a .csv file. + +This calibration is an artifact of the spectrum display it does not make any change in the actual levels in the DSP processing. It assumes nothing about the receiving or transmitting chains it is up to the user to run the calibration procedure to make the face power levels match the desired levels. If anything is changed in the receiving or transmitting parameters then the calibration procedure may have to be re-run again. Also it assumes nothing about the units of the calibrated power. Normally one would like to make dBm (or mW) measurements but it is up to the user to assume the actual units. One may want to use dBW or any other custom units therefore the displayed calibrated values will remain unit-less (dB). + +For frequencies between calibration points the shift from relative value to displayed calibration value is interpolated using the next closest calibration points. If only one calibration point is provided then it applies regardless of frequency. The retained frequency for calculation is the center frequency of the spectrum and the shift applied is constant over the spectrum span. When no calibration points are provided turning on calibration correction has no effect. + +

Interface description

+ +![Spectrum calibration dialog](../doc/img/Spectrum_Calibration_dialog.png) + +

1. Select point

+ +Use the rotating button to select the point to edit + +

2. Add/Delete point

+ +Click on the "+" button to add a new point and "-" to remove the current point. + +

3. Relative power

+ +Enter the relative power in dB. This is the power displayed without calibration at this frequency point. + +

4. Calibrated power

+ +Enter the calibrated power in dB. This is the power displayed with calibration at this frequency point. + +

5. Point frequency

+ +This is the frequency of the calibration point + +

6. Point controls

+ +![Spectrum calibration buttons](../doc/img/Spectrum_Calibration_buttons.png) + +

6.1. Sort points

+ +Sort points by increasing frequency + +

6.2. Duplicate point

+ +Append a new point to the list as a copy of the current point. The editor is automatically transferred to the new point. + +

6.3. Import from spectrum marker

+ +Import data (frequency and relative power) from the first histogram marker (marker 0) if it exists. If there are no histogram markers this just does nothing. + +

6.4. Set frequency to center

+ +Sets the point frequency to the center frequency of the spectrum + +

7. Interpolation method

+ +The calibration shift between calibration points is interpolated based on the relative distance in frequency between points. The applied calibration shift is interpolated from the shift of the neighboring points. The interpolation can be: + + - **Linear**: interpolation is linear in terms of power + - **Log**: interpolation is logarithmic in terms of power or linear if the power is expressed in dB + +

8. Apply correction or set value

+ +This button lets you choose how the constant power next (9) is applied to all points + + - **Cor**: applies a correction i.e. the value in (9) is added (in dB) to all points + - **Set**: sets power of all point to the value in (9) + +The correction allows to take roughly into account the reception or transmission gain changes. It is like applying a constant gain over all frequencies. + +You may apply correction to either the relative powers or the calibrated powers. The choice depends on whether you are in a reception or transmission chain (source or sink device set respectively). + +In reception you would apply it to the relative powers as the input power that you measure for calibration remains the same. + +In transmission you would apply it to the calibrated powers as the output power that you measure for calibration actually changes. + +

9. Constant power value in dB

+ +This value is used to set all points to or to be applied as a correction depending on (8). It is used for relative powers (10) or calibrated powers (11) + +

10. Set or apply correction to relative powers

+ +All relative power values are set to value in (9) if "Set" is selected in (8). + +The value in (9) is added (in dB) to all relative power values if "Cor" is selected in (8) + +

11. Set or apply correction to calibrated powers

+ +All calibrated power values are set to value in (9) if "Set" is selected in (8). + +The value in (9) is added (in dB) to all calibrated power values if "Cor" is selected in (8) + +

12. Export calibrated points to .csv

+ +Export the calibrated points to a .csv file. Colums are: + + - **Frequency**: frequency in Hz of the point + - **Relative**: relative power in dB + - **Calibrated**: calibrated power in dB + +

13. Import calibrated points from .csv file

+ +Import the calibrated points from a .csv file in the format described above. diff --git a/swagger/sdrangel/api/swagger/include/GLSpectrum.yaml b/swagger/sdrangel/api/swagger/include/GLSpectrum.yaml index 2a2f6666b..7e003fbfa 100644 --- a/swagger/sdrangel/api/swagger/include/GLSpectrum.yaml +++ b/swagger/sdrangel/api/swagger/include/GLSpectrum.yaml @@ -163,6 +163,12 @@ GLSpectrum: useCalibration: type: integer description: boolean + calibrationInterpMode: + type: integer + description: > + Interpolation scheme between points SpectrumSettings::CalibrationInterpolationMode + * 0 - Linear + * 1 - Log (dB linear) histogramMarkers: type: array items: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 1f4433d50..3b0b113d2 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -6159,6 +6159,10 @@ margin-bottom: 20px; "type" : "integer", "description" : "boolean" }, + "calibrationInterpMode" : { + "type" : "integer", + "description" : "Interpolation scheme between points SpectrumSettings::CalibrationInterpolationMode\n * 0 - Linear\n * 1 - Log (dB linear)\n" + }, "histogramMarkers" : { "type" : "array", "items" : { @@ -52000,7 +52004,7 @@ except ApiException as e:
- Generated 2022-01-30T07:47:20.311+01:00 + Generated 2022-02-05T07:11:11.381+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGGLSpectrum.cpp b/swagger/sdrangel/code/qt5/client/SWGGLSpectrum.cpp index fe11e43be..d7ca38957 100644 --- a/swagger/sdrangel/code/qt5/client/SWGGLSpectrum.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGGLSpectrum.cpp @@ -84,6 +84,8 @@ SWGGLSpectrum::SWGGLSpectrum() { m_markers_display_isSet = false; use_calibration = 0; m_use_calibration_isSet = false; + calibration_interp_mode = 0; + m_calibration_interp_mode_isSet = false; histogram_markers = nullptr; m_histogram_markers_isSet = false; waterfall_markers = nullptr; @@ -156,6 +158,8 @@ SWGGLSpectrum::init() { m_markers_display_isSet = false; use_calibration = 0; m_use_calibration_isSet = false; + calibration_interp_mode = 0; + m_calibration_interp_mode_isSet = false; histogram_markers = new QList(); m_histogram_markers_isSet = false; waterfall_markers = new QList(); @@ -198,6 +202,7 @@ SWGGLSpectrum::cleanup() { + if(histogram_markers != nullptr) { auto arr = histogram_markers; for(auto o: *arr) { @@ -295,6 +300,8 @@ SWGGLSpectrum::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&use_calibration, pJson["useCalibration"], "qint32", ""); + ::SWGSDRangel::setValue(&calibration_interp_mode, pJson["calibrationInterpMode"], "qint32", ""); + ::SWGSDRangel::setValue(&histogram_markers, pJson["histogramMarkers"], "QList", "SWGSpectrumHistogramMarker"); @@ -403,6 +410,9 @@ SWGGLSpectrum::asJsonObject() { if(m_use_calibration_isSet){ obj->insert("useCalibration", QJsonValue(use_calibration)); } + if(m_calibration_interp_mode_isSet){ + obj->insert("calibrationInterpMode", QJsonValue(calibration_interp_mode)); + } if(histogram_markers && histogram_markers->size() > 0){ toJsonArray((QList*)histogram_markers, obj, "histogramMarkers", "SWGSpectrumHistogramMarker"); } @@ -699,6 +709,16 @@ SWGGLSpectrum::setUseCalibration(qint32 use_calibration) { this->m_use_calibration_isSet = true; } +qint32 +SWGGLSpectrum::getCalibrationInterpMode() { + return calibration_interp_mode; +} +void +SWGGLSpectrum::setCalibrationInterpMode(qint32 calibration_interp_mode) { + this->calibration_interp_mode = calibration_interp_mode; + this->m_calibration_interp_mode_isSet = true; +} + QList* SWGGLSpectrum::getHistogramMarkers() { return histogram_markers; @@ -828,6 +848,9 @@ SWGGLSpectrum::isSet(){ if(m_use_calibration_isSet){ isObjectUpdated = true; break; } + if(m_calibration_interp_mode_isSet){ + isObjectUpdated = true; break; + } if(histogram_markers && (histogram_markers->size() > 0)){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGGLSpectrum.h b/swagger/sdrangel/code/qt5/client/SWGGLSpectrum.h index 5d60364e7..51ecd32e6 100644 --- a/swagger/sdrangel/code/qt5/client/SWGGLSpectrum.h +++ b/swagger/sdrangel/code/qt5/client/SWGGLSpectrum.h @@ -131,6 +131,9 @@ public: qint32 getUseCalibration(); void setUseCalibration(qint32 use_calibration); + qint32 getCalibrationInterpMode(); + void setCalibrationInterpMode(qint32 calibration_interp_mode); + QList* getHistogramMarkers(); void setHistogramMarkers(QList* histogram_markers); @@ -231,6 +234,9 @@ private: qint32 use_calibration; bool m_use_calibration_isSet; + qint32 calibration_interp_mode; + bool m_calibration_interp_mode_isSet; + QList* histogram_markers; bool m_histogram_markers_isSet;