diff --git a/doc/img/Spectrum_Measurement_3dBBandwidth.png b/doc/img/Spectrum_Measurement_3dBBandwidth.png new file mode 100644 index 000000000..93d19423c Binary files /dev/null and b/doc/img/Spectrum_Measurement_3dBBandwidth.png differ diff --git a/doc/img/Spectrum_Measurement_AdjChannelPower.png b/doc/img/Spectrum_Measurement_AdjChannelPower.png index d55489a04..96913166c 100644 Binary files a/doc/img/Spectrum_Measurement_AdjChannelPower.png and b/doc/img/Spectrum_Measurement_AdjChannelPower.png differ diff --git a/doc/img/Spectrum_Measurement_ChannelPower.png b/doc/img/Spectrum_Measurement_ChannelPower.png index bbf837d08..9c523d68d 100644 Binary files a/doc/img/Spectrum_Measurement_ChannelPower.png and b/doc/img/Spectrum_Measurement_ChannelPower.png differ diff --git a/doc/img/Spectrum_Measurement_OccupiedBandwidth.png b/doc/img/Spectrum_Measurement_OccupiedBandwidth.png new file mode 100644 index 000000000..e1a27c19e Binary files /dev/null and b/doc/img/Spectrum_Measurement_OccupiedBandwidth.png differ diff --git a/doc/img/Spectrum_Measurement_Peak.png b/doc/img/Spectrum_Measurement_Peak.png index 903c45995..d8065adbc 100644 Binary files a/doc/img/Spectrum_Measurement_Peak.png and b/doc/img/Spectrum_Measurement_Peak.png differ diff --git a/doc/img/Spectrum_Measurement_SNR.png b/doc/img/Spectrum_Measurement_SNR.png index 41e0e3a3d..b82651b80 100644 Binary files a/doc/img/Spectrum_Measurement_SNR.png and b/doc/img/Spectrum_Measurement_SNR.png differ diff --git a/doc/img/Spectrum_Measurement_Specification.png b/doc/img/Spectrum_Measurement_Specification.png new file mode 100644 index 000000000..94c435f34 Binary files /dev/null and b/doc/img/Spectrum_Measurement_Specification.png differ diff --git a/doc/img/Spectrum_Measurement_dialog_OccupiedBandwidth.png b/doc/img/Spectrum_Measurement_dialog_OccupiedBandwidth.png new file mode 100644 index 000000000..36cbb963a Binary files /dev/null and b/doc/img/Spectrum_Measurement_dialog_OccupiedBandwidth.png differ diff --git a/sdrbase/dsp/spectrumsettings.h b/sdrbase/dsp/spectrumsettings.h index 3be5836f3..8d2829fec 100644 --- a/sdrbase/dsp/spectrumsettings.h +++ b/sdrbase/dsp/spectrumsettings.h @@ -76,6 +76,8 @@ public: MeasurementPeaks, MeasurementChannelPower, MeasurementAdjacentChannelPower, + MeasurementOccupiedBandwidth, + Measurement3dBBandwidth, MeasurementSNR }; diff --git a/sdrgui/gui/glspectrumview.cpp b/sdrgui/gui/glspectrumview.cpp index 0f0937cad..a6bdf7183 100644 --- a/sdrgui/gui/glspectrumview.cpp +++ b/sdrgui/gui/glspectrumview.cpp @@ -1751,6 +1751,12 @@ void GLSpectrumView::paintGL() case SpectrumSettings::MeasurementAdjacentChannelPower: measureAdjacentChannelPower(); break; + case SpectrumSettings::MeasurementOccupiedBandwidth: + measureOccupiedBandwidth(); + break; + case SpectrumSettings::Measurement3dBBandwidth: + measure3dBBandwidth(); + break; case SpectrumSettings::MeasurementSNR: measureSNR(); measureSFDR(); @@ -2223,6 +2229,99 @@ void GLSpectrumView::measureAdjacentChannelPower() } } +// Measure bandwidth that has 99% of power +void GLSpectrumView::measureOccupiedBandwidth() +{ + float hzPerBin = m_sampleRate / (float) m_fftSize; + int bins = m_measurementBandwidth / hzPerBin; + int start = frequencyToBin(m_centerFrequency + m_measurementCenterFrequencyOffset); + float totalPower, power = 0.0f; + int step = 0; + int width = 0; + int idx = start; + float gain = m_useCalibration ? m_calibrationGain : 1.0f; + float shift = m_useCalibration ? m_calibrationShiftdB : 0.0f; + + totalPower = CalcDb::powerFromdB(calcChannelPower(m_centerFrequency + m_measurementCenterFrequencyOffset, m_measurementBandwidth)); + do + { + if ((idx >= 0) && (idx < m_nbBins)) + { + if (m_linear) { + power += m_currentSpectrum[idx] * gain; + } else { + power += CalcDb::powerFromdB(m_currentSpectrum[idx]) + shift; + } + width++; + } + + step++; + if ((step & 1) == 1) { + idx -= step; + } else { + idx += step; + } + } + while (((power / totalPower) < 0.99f) && (step < m_nbBins)); + + float occupiedBandwidth = width * hzPerBin; + if (m_measurements) { + m_measurements->setOccupiedBandwidth(occupiedBandwidth); + } + if (m_measurementHighlight) + { + drawBandwidthMarkers(m_centerFrequency + m_measurementCenterFrequencyOffset, m_measurementBandwidth, m_measurementDarkMarkerColor); + drawBandwidthMarkers(m_centerFrequency + m_measurementCenterFrequencyOffset, occupiedBandwidth, m_measurementLightMarkerColor); + } +} + +// Measure bandwidth -3dB from peak +void GLSpectrumView::measure3dBBandwidth() +{ + // Find max peak and it's power in dB + int peakBin = findPeakBin(m_currentSpectrum); + float peakPower = m_linear ? CalcDb::dbPower(m_currentSpectrum[peakBin]) : m_currentSpectrum[peakBin]; + + // Search right until 3dB from peak + int rightBin = peakBin; + for (int i = peakBin + 1; i < m_nbBins; i++) + { + float power = m_linear ? CalcDb::dbPower(m_currentSpectrum[i]) : m_currentSpectrum[i]; + if (peakPower - power > 3.0f) + { + rightBin = i - 1; + break; + } + } + + // Search left until 3dB from peak + int leftBin = peakBin; + for (int i = peakBin - 1; i >= 0; i--) + { + float power = m_linear ? CalcDb::dbPower(m_currentSpectrum[i]) : m_currentSpectrum[i]; + if (peakPower - power > 3.0f) + { + leftBin = i + 1; + break; + } + } + + // Calcualte bandwidth + int bins = rightBin - leftBin - 1; + bins = std::max(1, bins); + float hzPerBin = m_sampleRate / (float) m_fftSize; + float bandwidth = bins * hzPerBin; + int centerBin = leftBin + (rightBin - leftBin) / 2; + float centerFrequency = binToFrequency(centerBin); + + if (m_measurements) { + m_measurements->set3dBBandwidth(bandwidth); + } + if (m_measurementHighlight) { + drawBandwidthMarkers(centerFrequency, bandwidth, m_measurementLightMarkerColor); + } +} + const QVector4D GLSpectrumView::m_measurementLightMarkerColor = QVector4D(0.6f, 0.6f, 0.6f, 0.2f); const QVector4D GLSpectrumView::m_measurementDarkMarkerColor = QVector4D(0.6f, 0.6f, 0.6f, 0.15f); @@ -2485,17 +2584,20 @@ float GLSpectrumView::calcChannelPower(int64_t centerFrequency, int channelBandw int end = start + bins; float power = 0.0; + start = std::max(start, 0); + end = std::min(end, m_nbBins); + if (m_linear) { float gain = m_useCalibration ? m_calibrationGain : 1.0f; - for (int i = start; i <= end; i++) { + for (int i = start; i < end; i++) { power += m_currentSpectrum[i] * gain; } } else { float shift = m_useCalibration ? m_calibrationShiftdB : 0.0f; - for (int i = start; i <= end; i++) { + for (int i = start; i < end; i++) { power += CalcDb::powerFromdB(m_currentSpectrum[i]) + shift; } } diff --git a/sdrgui/gui/glspectrumview.h b/sdrgui/gui/glspectrumview.h index e5dcd64b5..234b220bc 100644 --- a/sdrgui/gui/glspectrumview.h +++ b/sdrgui/gui/glspectrumview.h @@ -431,6 +431,8 @@ private: void measurePeaks(); void measureChannelPower(); void measureAdjacentChannelPower(); + void measureOccupiedBandwidth(); + void measure3dBBandwidth(); void measureSNR(); void measureSFDR(); float calcChannelPower(int64_t centerFrequency, int channelBandwidth) const; diff --git a/sdrgui/gui/spectrummeasurements.cpp b/sdrgui/gui/spectrummeasurements.cpp index b90d42d59..611416729 100644 --- a/sdrgui/gui/spectrummeasurements.cpp +++ b/sdrgui/gui/spectrummeasurements.cpp @@ -347,6 +347,22 @@ void SpectrumMeasurements::createAdjacentChannelPowerTable() createMeasurementsTable(rows, units); } +void SpectrumMeasurements::createOccupiedBandwidthTable() +{ + QStringList rows = {"Occupied B/W"}; + QStringList units = {"Hz"}; + + createMeasurementsTable(rows, units); +} + +void SpectrumMeasurements::create3dBBandwidthTable() +{ + QStringList rows = {"3dB B/W"}; + QStringList units = {"Hz"}; + + createMeasurementsTable(rows, units); +} + void SpectrumMeasurements::createSNRTable() { QStringList rows = {"SNR", "SNFR", "THD", "THD+N", "SINAD", "SFDR",}; @@ -512,6 +528,16 @@ void SpectrumMeasurements::setMeasurementParams(SpectrumSettings::Measurement me createAdjacentChannelPowerTable(); layout()->addWidget(m_table); break; + case SpectrumSettings::MeasurementOccupiedBandwidth: + reset(); + createOccupiedBandwidthTable(); + layout()->addWidget(m_table); + break; + case SpectrumSettings::Measurement3dBBandwidth: + reset(); + create3dBBandwidthTable(); + layout()->addWidget(m_table); + break; case SpectrumSettings::MeasurementSNR: reset(); createSNRTable(); @@ -648,6 +674,16 @@ void SpectrumMeasurements::setAdjacentChannelPower(float left, float leftACPR, f updateMeasurement(4, rightACPR); } +void SpectrumMeasurements::setOccupiedBandwidth(float occupiedBandwidth) +{ + updateMeasurement(0, occupiedBandwidth); +} + +void SpectrumMeasurements::set3dBBandwidth(float bandwidth) +{ + updateMeasurement(0, bandwidth); +} + void SpectrumMeasurements::setPeak(int peak, int64_t frequency, float power) { if (peak < m_peakTable->rowCount()) diff --git a/sdrgui/gui/spectrummeasurements.h b/sdrgui/gui/spectrummeasurements.h index ab14999b6..f202a0aab 100644 --- a/sdrgui/gui/spectrummeasurements.h +++ b/sdrgui/gui/spectrummeasurements.h @@ -96,6 +96,8 @@ public: void setSFDR(float sfdr); void setChannelPower(float power); void setAdjacentChannelPower(float left, float leftACPR, float center, float right, float rightACPR); + void setOccupiedBandwidth(float occupiedBandwidth); + void set3dBBandwidth(float bandwidth); void setPeak(int peak, int64_t frequency, float power); void reset(); @@ -105,6 +107,8 @@ private: void createTableMenus(); void createChannelPowerTable(); void createAdjacentChannelPowerTable(); + void createOccupiedBandwidthTable(); + void create3dBBandwidthTable(); void createSNRTable(); void tableContextMenu(QPoint pos); void peakTableContextMenu(QPoint pos); diff --git a/sdrgui/gui/spectrummeasurements.md b/sdrgui/gui/spectrummeasurements.md index 1fe3f2f0d..57e2b252d 100644 --- a/sdrgui/gui/spectrummeasurements.md +++ b/sdrgui/gui/spectrummeasurements.md @@ -20,6 +20,8 @@ Shows the n largest peaks in magnitude - **Results precision**: controls the number of decimal places displayed on the power readings in dB. This control is common to all measurement types - **Peaks**: controls the number of peaks + +