///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Edouard Griffiths, F4EXB //
// Copyright (C) 2021 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 . //
///////////////////////////////////////////////////////////////////////////////////
#include
#include
#include "dsp/dspengine.h"
#include "noisefigure.h"
#include "noisefiguresink.h"
NoiseFigureSink::NoiseFigureSink(NoiseFigure *noiseFigure) :
m_noiseFigure(noiseFigure),
m_channelSampleRate(48000),
m_fftSequence(-1),
m_fft(nullptr),
m_fftCounter(0),
m_magsqSum(0.0f),
m_magsqPeak(0.0f),
m_magsqCount(0),
m_messageQueueToChannel(nullptr),
m_powerSum(0.0),
m_count(0),
m_enabled(false)
{
m_magsq = 0.0;
applySettings(m_settings, true);
applyChannelSettings(m_channelSampleRate, 0, true);
}
NoiseFigureSink::~NoiseFigureSink()
{
}
void NoiseFigureSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end)
{
for (SampleVector::const_iterator it = begin; it != end; ++it)
{
Complex c(it->real(), it->imag());
processOneSample(c);
}
}
void NoiseFigureSink::processOneSample(Complex &ci)
{
// Add to FFT input buffer
m_fft->in()[m_fftCounter] = Complex(ci.real() / SDR_RX_SCALEF, ci.imag() / SDR_RX_SCALEF);
m_fftCounter++;
if (m_fftCounter == m_settings.m_fftSize)
{
// Calculate FFT (note no windowing as input should be broadband noise)
m_fft->transform();
m_fftCounter = 0;
// Calculate power in FFT bin selected by input frequency offset
double frequencyResolution = m_channelSampleRate / (double)m_settings.m_fftSize;
int bin;
if (m_settings.m_inputFrequencyOffset >= 0) {
bin = m_settings.m_inputFrequencyOffset / frequencyResolution;
} else {
bin = m_settings.m_fftSize + m_settings.m_inputFrequencyOffset / frequencyResolution;
}
Complex c = m_fft->out()[bin];
Real v = c.real() * c.real() + c.imag() * c.imag();
// Calculate average and peak levels for level meter
Real magsq = v / (m_settings.m_fftSize*m_settings.m_fftSize);
m_movingAverage(magsq);
m_magsq = m_movingAverage.asDouble();
m_magsqSum += magsq;
if (magsq > m_magsqPeak)
{
m_magsqPeak = magsq;
}
m_magsqCount++;
if (m_enabled)
{
// Average power for measurement
m_powerSum += v;
m_count++;
if (m_count == m_settings.m_fftCount)
{
// Convert average to dB
// This is 10*log10(p/(1/fftSize)^2) optimised to not use log10 in the loop
const Real mult = (10.0f / log2(10.0f)); // ~3.01
Real ofs = 20.0f * log10f(1.0f / m_settings.m_fftSize);
Real avg = mult * log2f(m_powerSum / m_count) + ofs;
// Send NF results to channel
if (getMessageQueueToChannel())
{
NoiseFigure::MsgPowerMeasurement *msg = NoiseFigure::MsgPowerMeasurement::create(avg);
getMessageQueueToChannel()->push(msg);
}
// Prepare for a new measurement
m_powerSum = 0.0;
m_count = 0;
m_enabled = false;
}
}
}
}
void NoiseFigureSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force)
{
(void) force;
qDebug() << "NoiseFigureSink::applyChannelSettings:"
<< " channelSampleRate: " << channelSampleRate
<< " channelFrequencyOffset: " << channelFrequencyOffset;
m_channelSampleRate = channelSampleRate;
}
void NoiseFigureSink::applySettings(const NoiseFigureSettings& settings, bool force)
{
qDebug() << "NoiseFigureSink::applySettings:"
<< " force: " << force;
if ((settings.m_fftSize != m_settings.m_fftSize) || force)
{
FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory();
if (m_fftSequence >= 0) {
fftFactory->releaseEngine(m_settings.m_fftSize, false, m_fftSequence);
}
m_fftSequence = fftFactory->getEngine(settings.m_fftSize, false, &m_fft);
m_fftCounter = 0;
}
if ((settings.m_fftCount != m_settings.m_fftCount) || force)
{
m_powerSum = 0.0;
m_count = 0;
}
m_settings = settings;
}