Add occupied bandwidth and 3dB bandwidth measurements

pull/1465/head
Jon Beniston 2022-10-03 16:02:24 +01:00
rodzic 8843a8cfbb
commit 47c63d3154
14 zmienionych plików z 209 dodań i 11 usunięć

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 43 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 78 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 81 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 56 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 87 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 42 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 40 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 54 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 11 KiB

Wyświetl plik

@ -76,6 +76,8 @@ public:
MeasurementPeaks,
MeasurementChannelPower,
MeasurementAdjacentChannelPower,
MeasurementOccupiedBandwidth,
Measurement3dBBandwidth,
MeasurementSNR
};

Wyświetl plik

@ -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,98 @@ 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
float hzPerBin = m_sampleRate / (float) m_fftSize;
int bins = rightBin - leftBin - 1;
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 +2583,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;
}
}

Wyświetl plik

@ -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;

Wyświetl plik

@ -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())

Wyświetl plik

@ -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);

Wyświetl plik

@ -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
![Spectrum Measurements - peaks](../../doc/img/Spectrum_Measurement_Peak.png)
<h2>Channel Power</h2>
Channel power measures the total power within a user-defined bandwidth. Channel shift is adjustable with the dialog.
@ -41,27 +43,60 @@ The adjacent channel power measurement measures the power in a channel of user-d
![Spectrum Measurements dialog - adjacentchannel power](../../doc/img/Spectrum_Measurement_dialog_AdjChannelPower.png)
- **Center frequency offset**: channels offset from the center in Hz
- **Channel bandwidth**: bandwidth of the in-channel in Hz
- **Channel spacing**: In-channel to adjacent channel centers spacing in Hz
- **Channel bandwidth**: bandwidth of the center channel in Hz
- **Channel spacing**: Center channel to adjacent channel centers spacing in Hz
- **Adjacent channel bandwidth**: Adjacent channels bandwidth in Hz
Spectrum display with the highlight artifact on:
![Spectrum Measurements - adjacentchannel power](../../doc/img/Spectrum_Measurement_AdjChannelPower.png)
![Spectrum Measurements - adjacent channel power](../../doc/img/Spectrum_Measurement_AdjChannelPower.png)
The results table displays:
- **Left power**: power in the left channel in dB
- **Left ACPR**: difference in power between the left channel and the center channel in dB
- **Center power**: power in the center channel in dB
- **Right ACPR**: difference in power between the right channel and the center channel in dB
- **Right power**: power in the right channel in dB
<h2>Occupied Bandwidth</h2>
The occupied bandwidth measurement measures the bandwidth that contains 99% of the total power.
![Spectrum Measurements dialog - occupied bandwidth](../../doc/img/Spectrum_Measurement_dialog_OccupiedBandwidth.png)
- **Center frequency offset**: channel offset from the center in Hz
- **Channel bandwidth**: bandwidth of the channel in Hz, over which the total power should be measured
Spectrum display with the highlight artifact on:
![Spectrum Measurements - occupied bandwidth](../../doc/img/Spectrum_Measurement_OccupiedBandwidth.png)
<h2>3dB Bandwidth</h2>
The 3dB bandwidth measurement measures the bandwidth of a signal, calculated as the frequency difference between where the power falls by 3dB either side of the maximum peak.
![Spectrum Measurements - 3dB bandwidth](../../doc/img/Spectrum_Measurement_3dBBandwidth.png)
<h2>SNR</h2>
The SNR measurement simultaneously calculates SNR, SNFR, THD, THD+N, SINAD and SFDR.
![Spectrum Measurements dialog - SNR](../../doc/img/Spectrum_Measurement_dialog_SNR.png)
- **Harmonics**: controls the number of harmonics for THD, THD+N or SINAD measurements (see next)
- **Harmonics**: controls the number of harmonics for SNR, THD or THD+N measurements (see next)
Spectrum display with the highlight artifact on:
![Spectrum Measurements - SNR](../../doc/img/Spectrum_Measurement_SNR.png)
The fundamental, harmonics and SFDR are highlighted.
<h3>SNR: Signal to Noise Ratio</h3>
The SNR measurement estimates a signal-to-noise ratio. The fundamental signal is the largest peak (i.e. FFT bin with highest magnitude). The bandwidth of the signal is assumed to be the width of the largest peak, which includes adjacent bins with a monotonically decreasing magnitude. Noise is summed over the full bandwidth (i.e all FFT bins), with the fundamental and user-specified number of harmonics being replaced with the noise median from outside of these regions. The noise median is also subtracted from the signal, before the SNR is calculated.
![Spectrum Measurements - SNR](../../doc/img/Spectrum_Measurement_SNR.png)
<h3>SFNR: Signal to Noise Floor Ratio</h3>
<h3>SNFR: Signal to Noise Floor Ratio</h3>
The SNFR measurement estimates a signal-to-noise-floor ratio. This is similar to the SNR, except that the noise used in the ratio, is only the median noise value calculated from the noise outside of the fundamental and harmonics, summed over the bandwidth of the signal. One way to think of this, is that it is the SNR if all noise outside of the signal's bandwidth was filtered.
@ -81,5 +116,12 @@ SINAD is measured as per SNR, but the result is the ratio of the fundamental to
SFDR is a measurement of the difference in power from the largest peak (the fundamental) to the second largest peak (the strongest spurious signal).
![Spectrum Measurements - SFDR](../../doc/img/Spectrum_Measurement_SFDR.png)
<h2>Specifications</h2>
The measurements table has a Spec column that allows entry of user-defined specifications for the Current, Mean, Min and Max values to be checked against.
For example, you might choose to enter >50 in the SFDR row, to check that SFDR is greater than 50dB.
Each time the specification fails to be met, the value in the Fails column is incremented.
Values out of specification are highlighted in red.
The Spec column supports the following operators: <, <=, >, >= and =.
![Spectrum Measurements - specifications](../../doc/img/Spectrum_Measurement_Specifications.png)

Wyświetl plik

@ -80,7 +80,8 @@ void SpectrumMeasurementsDialog::displaySettings()
ui->resetMeasurements->setVisible(reset && show);
bool bw = (m_settings->m_measurement == SpectrumSettings::MeasurementChannelPower)
|| (m_settings->m_measurement == SpectrumSettings::MeasurementAdjacentChannelPower);
|| (m_settings->m_measurement == SpectrumSettings::MeasurementAdjacentChannelPower)
|| (m_settings->m_measurement == SpectrumSettings::MeasurementOccupiedBandwidth);
ui->centerFrequencyOffsetLabel->setVisible(bw && show);
ui->centerFrequencyOffset->setVisible(bw && show);
ui->bandwidthLabel->setVisible(bw && show);

Wyświetl plik

@ -155,6 +155,16 @@
<string>Adjacent channel power</string>
</property>
</item>
<item>
<property name="text">
<string>Occupied b/w</string>
</property>
</item>
<item>
<property name="text">
<string>3dB bandwidth</string>
</property>
</item>
<item>
<property name="text">
<string>SNR</string>