////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2015-2017, 2020 Edouard Griffiths, F4EXB // // Copyright (C) 2020 Kacper Michajłow // // // // 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 . // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include #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() { 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); }