/////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2023 Edouard Griffiths, F4EXB // // // // Helper class for noise reduction // // // // 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 #include "fftnr.h" FFTNoiseReduction::FFTNoiseReduction(int len) : m_flen(len) { m_scheme = SchemeAverage; m_mags = new float[m_flen]; m_tmp = new float[m_flen]; m_aboveAvgFactor = 1.0; m_sigmaFactor = 1.0; m_nbPeaks = m_flen; } FFTNoiseReduction::~FFTNoiseReduction() { delete[] m_mags; delete[] m_tmp; } void FFTNoiseReduction::init() { std::fill(m_mags, m_mags + m_flen, 0); std::fill(m_tmp, m_tmp + m_flen, 0); m_magAvg = 0; } void FFTNoiseReduction::push(cmplx data, int index) { m_mags[index] = std::abs(data); if ((m_scheme == SchemeAverage) || (m_scheme == SchemeAvgStdDev)) { m_magAvg += m_mags[index]; } } void FFTNoiseReduction::calc() { if (m_scheme == SchemeAverage) { m_magAvg /= m_flen; m_magAvg = m_expFilter.push(m_magAvg); } if (m_scheme == SchemeAvgStdDev) { m_magAvg /= m_flen; auto variance_func = [this](float accumulator, const float& val) { return accumulator + ((val - m_magAvg)*(val - m_magAvg) / (m_flen - 1)); }; float var = std::accumulate(m_mags, m_mags + m_flen, 0.0, variance_func); m_magThr = (m_sigmaFactor/2.0)*std::sqrt(var) + m_magAvg; m_magThr = m_expFilter.push(m_magThr); } else if (m_scheme == SchemePeaks) { std::copy(m_mags, m_mags + m_flen, m_tmp); std::sort(m_tmp, m_tmp + m_flen); m_magThr = m_tmp[m_flen - m_nbPeaks]; } } bool FFTNoiseReduction::cut(int index) { if (m_scheme == SchemeAverage) { return m_mags[index] < m_aboveAvgFactor * m_magAvg; } else if ((m_scheme == SchemePeaks) || (m_scheme == SchemeAvgStdDev)) { return m_mags[index] < m_magThr; } return false; } void FFTNoiseReduction::setScheme(Scheme scheme) { if (m_scheme != scheme) { m_expFilter.reset(); } m_scheme = scheme; } FFTNoiseReduction::ExponentialFilter::ExponentialFilter() { m_alpha = 1.0; m_init = true; } float FFTNoiseReduction::ExponentialFilter::push(float newValue) { if (m_init) { m_prev = newValue; m_init = false; } if (m_alpha == 1.0) { m_prev = newValue; return newValue; } else { float result = m_alpha*m_prev + (1.0 - m_alpha)*newValue; m_prev = result; return result; } } void FFTNoiseReduction::ExponentialFilter::reset() { m_init = true; } void FFTNoiseReduction::ExponentialFilter::setAlpha(float alpha) { m_alpha = alpha < 0.0f ? 0.0f : alpha > 1.0f ? 1.0f : alpha; qDebug("FFTNoiseReduction::ExponentialFilter::setAlpha: %f", m_alpha); m_init = true; }