mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-03 15:31:15 -05:00
175 lines
5.3 KiB
C++
175 lines
5.3 KiB
C++
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB //
|
|
// //
|
|
// See: http://www.embedded.com/design/connectivity/4025660/Detecting-CTCSS-tones-with-Goertzel-s-algorithm //
|
|
// //
|
|
// 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 <http://www.gnu.org/licenses/>. //
|
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <cmath>
|
|
|
|
#include "dsp/ctcssdetector.h"
|
|
|
|
CTCSSDetector::CTCSSDetector() :
|
|
m_N(0),
|
|
m_sampleRate(0),
|
|
m_samplesProcessed(0),
|
|
m_maxPowerIndex(0),
|
|
m_toneDetected(false),
|
|
m_maxPower(0.0)
|
|
{
|
|
m_k = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
m_coef = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
m_u0 = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
m_u1 = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
m_power = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
}
|
|
|
|
CTCSSDetector::CTCSSDetector(int _nTones, Real *tones) :
|
|
m_N(0),
|
|
m_sampleRate(0),
|
|
m_samplesProcessed(0),
|
|
m_maxPowerIndex(0),
|
|
m_toneDetected(false),
|
|
m_maxPower(0.0)
|
|
{
|
|
m_k = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
m_coef = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
m_u0 = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
m_u1 = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
m_power = new Real[CTCSSFrequencies::m_nbFreqs];
|
|
}
|
|
|
|
|
|
CTCSSDetector::~CTCSSDetector()
|
|
{
|
|
delete[] m_k;
|
|
delete[] m_coef;
|
|
delete[] m_u0;
|
|
delete[] m_u1;
|
|
delete[] m_power;
|
|
}
|
|
|
|
|
|
void CTCSSDetector::setCoefficients(int N, int sampleRate)
|
|
{
|
|
m_N = N; // save the basic parameters for use during analysis
|
|
m_sampleRate = sampleRate;
|
|
|
|
// for each of the frequencies (tones) of interest calculate
|
|
// k and the associated filter coefficient as per the Goertzel
|
|
// algorithm. Note: we are using a real value (as apposed to
|
|
// an integer as described in some references. k is retained
|
|
// for later display. The tone set is specified in the
|
|
// constructor. Notice that the resulting coefficients are
|
|
// independent of N.
|
|
for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j)
|
|
{
|
|
m_k[j] = ((double) m_N * CTCSSFrequencies::m_Freqs[j]) / (double)m_sampleRate;
|
|
m_coef[j] = 2.0 * cos((2.0 * M_PI * CTCSSFrequencies::m_Freqs[j])/(double)m_sampleRate);
|
|
}
|
|
}
|
|
|
|
|
|
// Analyze an input signal for the presence of CTCSS tones.
|
|
bool CTCSSDetector::analyze(Real *sample)
|
|
{
|
|
|
|
feedback(*sample); // Goertzel feedback
|
|
m_samplesProcessed += 1;
|
|
|
|
if (m_samplesProcessed == m_N) // completed a block of N
|
|
{
|
|
feedForward(); // calculate the m_power at each tone
|
|
m_samplesProcessed = 0;
|
|
return true; // have a result
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
void CTCSSDetector::feedback(Real in)
|
|
{
|
|
Real t;
|
|
|
|
// feedback for each tone
|
|
for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j)
|
|
{
|
|
t = m_u0[j];
|
|
m_u0[j] = in + (m_coef[j] * m_u0[j]) - m_u1[j];
|
|
m_u1[j] = t;
|
|
}
|
|
}
|
|
|
|
|
|
void CTCSSDetector::feedForward()
|
|
{
|
|
initializePower();
|
|
|
|
for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j)
|
|
{
|
|
m_power[j] = (m_u0[j] * m_u0[j]) + (m_u1[j] * m_u1[j]) - (m_coef[j] * m_u0[j] * m_u1[j]);
|
|
m_u0[j] = m_u1[j] = 0.0; // reset for next block.
|
|
}
|
|
|
|
evaluatePower();
|
|
}
|
|
|
|
|
|
void CTCSSDetector::reset()
|
|
{
|
|
for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j)
|
|
{
|
|
m_power[j] = m_u0[j] = m_u1[j] = 0.0; // reset
|
|
}
|
|
|
|
m_samplesProcessed = 0;
|
|
m_maxPower = 0.0;
|
|
m_maxPowerIndex = 0;
|
|
m_toneDetected = false;
|
|
}
|
|
|
|
|
|
void CTCSSDetector::initializePower()
|
|
{
|
|
for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j)
|
|
{
|
|
m_power[j] = 0.0; // reset
|
|
}
|
|
}
|
|
|
|
|
|
void CTCSSDetector::evaluatePower()
|
|
{
|
|
Real sumPower = 0.0;
|
|
Real aboveAvg = 2.0; // Arbitrary max m_power above average threshold
|
|
m_maxPower = 0.0;
|
|
|
|
for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j)
|
|
{
|
|
sumPower += m_power[j];
|
|
|
|
if (m_power[j] > m_maxPower)
|
|
{
|
|
m_maxPower = m_power[j];
|
|
m_maxPowerIndex = j;
|
|
}
|
|
}
|
|
|
|
m_toneDetected = (m_maxPower > (sumPower/CTCSSFrequencies::m_nbFreqs) + aboveAvg);
|
|
}
|