| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | // 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>
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | #include "dsp/ctcssdetector.h"
 | 
					
						
							| 
									
										
										
										
											2017-09-16 04:50:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | CTCSSDetector::CTCSSDetector() : | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 			m_N(0), | 
					
						
							|  |  |  | 			m_sampleRate(0), | 
					
						
							|  |  |  | 			m_samplesProcessed(0), | 
					
						
							|  |  |  | 			m_maxPowerIndex(0), | 
					
						
							|  |  |  | 			m_toneDetected(false), | 
					
						
							|  |  |  | 			m_maxPower(0.0) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	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]; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CTCSSDetector::~CTCSSDetector() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	delete[] m_k; | 
					
						
							|  |  |  | 	delete[] m_coef; | 
					
						
							|  |  |  | 	delete[] m_u0; | 
					
						
							|  |  |  | 	delete[] m_u1; | 
					
						
							|  |  |  | 	delete[] m_power; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | void CTCSSDetector::setCoefficients(int N, int sampleRate) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	m_N = N;                   // save the basic parameters for use during analysis
 | 
					
						
							|  |  |  | 	m_sampleRate = sampleRate; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// 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.
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 		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); | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Analyze an input signal for the presence of CTCSS tones.
 | 
					
						
							|  |  |  | bool CTCSSDetector::analyze(Real *sample) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	feedback(*sample); // Goertzel feedback
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	m_samplesProcessed += 1; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	if (m_samplesProcessed == m_N) // completed a block of N
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 		feedForward(); // calculate the m_power at each tone
 | 
					
						
							|  |  |  | 		m_samplesProcessed = 0; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 		return true; // have a result
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::feedback(Real in) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Real t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// feedback for each tone
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 		t = m_u0[j]; | 
					
						
							|  |  |  | 		m_u0[j] = in + (m_coef[j] * m_u0[j]) - m_u1[j]; | 
					
						
							|  |  |  | 		m_u1[j] = t; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::feedForward() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	initializePower(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 		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.
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	evaluatePower(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::reset() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 		m_power[j] = m_u0[j] = m_u1[j] = 0.0; // reset
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	m_samplesProcessed = 0; | 
					
						
							|  |  |  | 	m_maxPower = 0.0; | 
					
						
							|  |  |  | 	m_maxPowerIndex = 0; | 
					
						
							|  |  |  | 	m_toneDetected = false; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::initializePower() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 		m_power[j] = 0.0; // reset
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::evaluatePower() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Real sumPower = 0.0; | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	Real aboveAvg = 2.0; // Arbitrary max m_power above average threshold
 | 
					
						
							|  |  |  | 	m_maxPower = 0.0; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	for (int j = 0; j < CTCSSFrequencies::m_nbFreqs; ++j) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 		sumPower += m_power[j]; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 		if (m_power[j] > m_maxPower) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 			m_maxPower = m_power[j]; | 
					
						
							|  |  |  | 			m_maxPowerIndex = j; | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-01 08:23:50 +01:00
										 |  |  | 	m_toneDetected = (m_maxPower > (sumPower/CTCSSFrequencies::m_nbFreqs) + aboveAvg); | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | } |