| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * ctcssdetector.cpp | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Created on: Jun 16, 2015 | 
					
						
							|  |  |  |  *      Author: f4exb | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-09-16 04:50:25 +02:00
										 |  |  | #include <math.h>
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | #include "dsp/ctcssdetector.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-16 04:50:25 +02:00
										 |  |  | #undef M_PI
 | 
					
						
							|  |  |  | #define M_PI		3.14159265358979323846
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | CTCSSDetector::CTCSSDetector() : | 
					
						
							|  |  |  | 			N(0), | 
					
						
							|  |  |  | 			sampleRate(0), | 
					
						
							|  |  |  | 			samplesProcessed(0), | 
					
						
							|  |  |  | 			maxPowerIndex(0), | 
					
						
							|  |  |  | 			toneDetected(false), | 
					
						
							|  |  |  | 			maxPower(0.0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	nTones = 32; | 
					
						
							|  |  |  | 	k = new Real[nTones]; | 
					
						
							|  |  |  | 	coef = new Real[nTones]; | 
					
						
							|  |  |  | 	toneSet = new Real[nTones]; | 
					
						
							|  |  |  | 	u0 = new Real[nTones]; | 
					
						
							|  |  |  | 	u1 = new Real[nTones]; | 
					
						
							|  |  |  | 	power = new Real[nTones]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The 32 EIA standard tones
 | 
					
						
							|  |  |  | 	toneSet[0]  = 67.0; | 
					
						
							|  |  |  | 	toneSet[1]  = 71.9; | 
					
						
							|  |  |  | 	toneSet[2]  = 74.4; | 
					
						
							|  |  |  | 	toneSet[3]  = 77.0; | 
					
						
							|  |  |  | 	toneSet[4]  = 79.7; | 
					
						
							|  |  |  | 	toneSet[5]  = 82.5; | 
					
						
							|  |  |  | 	toneSet[6]  = 85.4; | 
					
						
							|  |  |  | 	toneSet[7]  = 88.5; | 
					
						
							|  |  |  | 	toneSet[8]  = 91.5; | 
					
						
							|  |  |  | 	toneSet[9]  = 94.8; | 
					
						
							|  |  |  | 	toneSet[10]  = 97.4; | 
					
						
							|  |  |  | 	toneSet[11] = 100.0; | 
					
						
							|  |  |  | 	toneSet[12] = 103.5; | 
					
						
							|  |  |  | 	toneSet[13] = 107.2; | 
					
						
							|  |  |  | 	toneSet[14] = 110.9; | 
					
						
							|  |  |  | 	toneSet[15] = 114.8; | 
					
						
							|  |  |  | 	toneSet[16] = 118.8; | 
					
						
							|  |  |  | 	toneSet[17] = 123.0; | 
					
						
							|  |  |  | 	toneSet[18] = 127.3; | 
					
						
							|  |  |  | 	toneSet[19] = 131.8; | 
					
						
							|  |  |  | 	toneSet[20] = 136.5; | 
					
						
							|  |  |  | 	toneSet[21] = 141.3; | 
					
						
							|  |  |  | 	toneSet[22] = 146.2; | 
					
						
							|  |  |  | 	toneSet[23] = 151.4; | 
					
						
							|  |  |  | 	toneSet[24] = 156.7; | 
					
						
							|  |  |  | 	toneSet[25] = 162.2; | 
					
						
							|  |  |  | 	toneSet[26] = 167.9; | 
					
						
							|  |  |  | 	toneSet[27] = 173.8; | 
					
						
							|  |  |  | 	toneSet[28] = 179.9; | 
					
						
							|  |  |  | 	toneSet[29] = 186.2; | 
					
						
							|  |  |  | 	toneSet[30] = 192.8; | 
					
						
							|  |  |  | 	toneSet[31] = 203.5; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CTCSSDetector::CTCSSDetector(int _nTones, Real *tones) : | 
					
						
							|  |  |  | 			N(0), | 
					
						
							|  |  |  | 			sampleRate(0), | 
					
						
							|  |  |  | 			samplesProcessed(0), | 
					
						
							|  |  |  | 			maxPowerIndex(0), | 
					
						
							|  |  |  | 			toneDetected(false), | 
					
						
							|  |  |  | 			maxPower(0.0) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	nTones = _nTones; | 
					
						
							|  |  |  | 	k = new Real[nTones]; | 
					
						
							|  |  |  | 	coef = new Real[nTones]; | 
					
						
							|  |  |  | 	toneSet = new Real[nTones]; | 
					
						
							|  |  |  | 	u0 = new Real[nTones]; | 
					
						
							|  |  |  | 	u1 = new Real[nTones]; | 
					
						
							|  |  |  | 	power = new Real[nTones]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int j = 0; j < nTones; ++j) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		toneSet[j] = tones[j]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CTCSSDetector::~CTCSSDetector() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	delete[] k; | 
					
						
							|  |  |  | 	delete[] coef; | 
					
						
							|  |  |  | 	delete[] toneSet; | 
					
						
							|  |  |  | 	delete[] u0; | 
					
						
							|  |  |  | 	delete[] u1; | 
					
						
							|  |  |  | 	delete[] power; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-28 10:53:37 +01:00
										 |  |  | void CTCSSDetector::setCoefficients(int zN, int _samplerate ) | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-02-28 10:53:37 +01:00
										 |  |  | 	N = zN;                   // save the basic parameters for use during analysis
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	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 < nTones; ++j) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		k[j] = ((double)N * toneSet[j]) / (double)sampleRate; | 
					
						
							|  |  |  | 		coef[j] = 2.0 * cos((2.0 * M_PI * toneSet[j])/(double)sampleRate); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Analyze an input signal for the presence of CTCSS tones.
 | 
					
						
							|  |  |  | bool CTCSSDetector::analyze(Real *sample) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	feedback(*sample); // Goertzel feedback
 | 
					
						
							|  |  |  | 	samplesProcessed += 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (samplesProcessed == N) // completed a block of N
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		feedForward(); // calculate the power at each tone
 | 
					
						
							|  |  |  | 		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 < nTones; ++j) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		t = u0[j]; | 
					
						
							|  |  |  | 		u0[j] = in + (coef[j] * u0[j]) - u1[j]; | 
					
						
							|  |  |  | 		u1[j] = t; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::feedForward() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	initializePower(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int j = 0; j < nTones; ++j) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		power[j] = (u0[j] * u0[j]) + (u1[j] * u1[j]) - (coef[j] * u0[j] * u1[j]); | 
					
						
							|  |  |  | 		u0[j] = u1[j] = 0.0; // reset for next block.
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	evaluatePower(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::reset() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	for (int j = 0; j < nTones; ++j) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		power[j] = u0[j] = u1[j] = 0.0; // reset
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	samplesProcessed = 0; | 
					
						
							|  |  |  | 	maxPower = 0.0; | 
					
						
							|  |  |  | 	maxPowerIndex = 0; | 
					
						
							|  |  |  | 	toneDetected = false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::initializePower() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	for (int j = 0; j < nTones; ++j) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		power[j] = 0.0; // reset
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CTCSSDetector::evaluatePower() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Real sumPower = 0.0; | 
					
						
							| 
									
										
										
										
											2015-06-17 01:51:25 +02:00
										 |  |  | 	Real aboveAvg = 2.0; // Arbitrary max power above average threshold
 | 
					
						
							| 
									
										
										
										
											2015-06-16 04:42:37 +02:00
										 |  |  | 	maxPower = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int j = 0; j < nTones; ++j) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		sumPower += power[j]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (power[j] > maxPower) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			maxPower = power[j]; | 
					
						
							|  |  |  | 			maxPowerIndex = j; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	toneDetected = (maxPower > (sumPower/nTones) + aboveAvg); | 
					
						
							|  |  |  | } |