Newsgroup: it.hobby.elettronica.digitale
Titolo: Problemi con Goertzel [lungo]

Ciao,
	mi sto cimentando nell'implementazione dell'algoritmo (modificato) di 
Goertzel per la decodifica dei toni dtmf.
L'implementazione in sé è piuttosto semplice ma ho comunque dei 
problemi. Mi spiego, il firmware a seguito dell'interrupt esterno eccita 
un rele il quale verrà sganciato quando viene rilevato il carattere '#' 
dtmf. Il problema è che lo sgancio avviene abbastanza casualmente: non 
certo in condizioni di silenzio, ma sia con un segnale vocale sia con il 
dtmf vero e proprio il riconoscimento non è deterministico.
Le tabelle dei valori di seno e coseno per ogni campione le ho generate 
in Excel calcolando i valori delle otto sinusoidi campionate a 4 KHz 
(per 128 campioni), riducendo tali valori a 1 bit (0 se < 0, 1 se >= 0) 
e poi incapsulandole in un byte. Ovvero ciascun bit corrisponde al 
campione atteso di ciascuna frequenza.
Dopo la firma riporto la parte di programma interessata.
Quello che chiedo non è tanto di correggermi gli errori (se ci si riesce 
tanto meglio :) ) ma piuttosto un consiglio su come procedere. Sono i 
primi passi nel dsp e non ho proprio idea di come effettuare il debug!
Tempo fa avevo fatto alcune simulazioni in Octave ma il segnale di 
ingresso era ovviamente uno 'finto', generato da me, e lì funzionava.
Ma una volta scritto il firmware per il target (in questo caso un PIC) 
come si procede per capire dove sta l'errore? Il grosso problema è che 
non ha senso fare un debug step-by-step in quanto il codice deve girare 
in run-time per fare il suo dovere.... e non avendo pin a disposizione 
per la seriale non so cosa sta succedendo là dentro!!!
Ogni suggerimento è bene accetto!
Marco / iw2nzm
Di seguito il codice:
#define	MAX_SAMPLES	128
#define	THRS			100
#define	MAX_DETECT	  3
int1  ready = FALSE;
int8  i, detect;
signed int8	sum_sin[8];
signed int8	sum_cos[8];
#inline
void init() {
	for (i = 0; i < 8; i++) sum_sin[i] = sum_cos[i] = 0;
	i = 0;
}
char decode_dtmf();
void goertzel(int8 _sin, int8 _cos, int8 _in);
const char dtmf[4][4] = {{'1','2','3','A'},
  								 {'4','5','6','B'},
  								 {'7','8','9','C'},
  								 {'*','0','#','D'}};						
const int8 sin[128] = 
{0,255,15,112,192,19,111,204,184,97,211,166,92,217,171,82,
							  102,189,73,106,146,197,53,138,250,59,133,244,2,26,233,5,52,
							  226,27,41,220,150,39,209,168,92,79,167,80,104,191,79,118,
							  144,193,127,142,252,97,129,247,78,28,225,19,38,236,157,41,
							  210,134,53,221,170,58,67,181,12,74,187,1,116,166,219,123,136,
							  244,102,19,233,72,20,231,83,40,248,223,39,208,192,57,207,174,
							  48,65,187,14,94,177,129,114,174,157,109,129,146,102,29,173,114,
							  18,229,93,12,250,195,53,212,206,59,75,244};
const int8 cos[128] = 
{0,15,48,224,135,63,204,152,33,215,190,108,89,163,86,68,
							  173,89,106,178,197,117,154,202,115,5,244,106,10,249,5,22,
							  226,155,57,236,150,35,217,136,44,87,163,16,72,191,159,96,
							  176,193,127,142,192,113,3,255,110,28,241,67,22,236,221,41,
							  242,214,45,93,138,50,69,165,60,74,187,133,84,182,139,123,
							  164,148,103,11,185,108,22,231,211,24,232,223,39,240,200,41,
							  95,198,48,65,251,62,78,241,129,82,174,141,113,34,146,110,
							  29,153,98,18,165,253,8,170,211,37,188,206,59,65,196,52};
#int_EXT
void EXT_isr(void) {
	init();
	ready = FALSE;
	output_drive(RING);
	output_low(RING);
	delay_ms(10);
	output_high(OUT);
	detect = 0;
	disable_interrupts(INT_EXT);
	enable_interrupts(INT_TIMER2);
}
#int_TIMER2
void  TIMER2_isr(void) {	// 256 us
	goertzel(sin[i], cos[i], input(IN));
	if (++i == MAX_SAMPLES) ready = TRUE;
}
void main() {
    setup_adc_ports(NO_ANALOGS|VSS_VDD);
    setup_adc(ADC_OFF);
    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_2);	
    setup_timer_1(T1_INTERNAL|T1_DIV_BY_8);
	setup_timer_2(T2_DIV_BY_16, 19, 4);
    setup_comparator(NC_NC);
    setup_vref(FALSE);
    enable_interrupts(INT_EXT);
    disable_interrupts(INT_TIMER1);
    disable_interrupts(INT_TIMER2);
    enable_interrupts(GLOBAL);
    ext_int_edge(L_TO_H);
    setup_oscillator(False);
	output_float(RING);
	output_float(IN);
	output_drive(OUT);
	output_low(OUT);	
 
	for (;;) {
		if (ready) {
			disable_interrupts(INT_TIMER2);
			ready = FALSE;
			if(decode_dtmf() == '#') {
				if (++detect == MAX_DETECT) {
    				output_float(RING);
    				output_low(OUT);
    				enable_interrupts(INT_EXT);
				} else {
					init();
					enable_interrupts(INT_TIMER2);
				}
			}
			else {
				detect = 0;
				init();
				enable_interrupts(INT_TIMER2);
			}
		}
	}
}
char decode_dtmf() {
	int8 j;
	int16 sum[8];
	int8 row = 0, col = 0;
	int16 tmp;
 
	for (j = 0; j < 8; j++) sum[j] = abs(sum_sin[j]) + abs(sum_cos[j]);
 
	tmp = 0;
	for (j = 0; j < 4; j++) {
		if (sum[j] > tmp) {
			tmp = sum[j];
			col = j;			
		}
	}
 
	if (tmp < THRS) return 0;
 
	tmp = 0;
	for (j = 4; j < 8; j++) {
		if (sum[j] > tmp) {
			tmp = sum[j];
			row = j - 4;			
		}
	}
 
	if (tmp < THRS) return 0;
	return dtmf[4-row][4-col];
}
void goertzel(int8 _sin, int8 _cos, int8 _in) {
	int8 j;
 
	for (j = 0; j < 8; j++) {
		if (bit_test(_sin, j) & _in) sum_sin[j]++; else sum_sin[j]--;
		if (bit_test(_cos, j) & _in) sum_cos[j]++; else sum_cos[j]--;
	}
}

 if (tmp < THRS) return 0;
Per dire che un tono è stato riconosciuto bisognerebbe fare un controllo sul
valore di tutte le righe (e di tutte colonne) non solo sulla riga (colonna)
di valore massimo controllando che questo valore sia maggiore di THRS.
Faccio un esempio.
THRS vale 100. Se le 4 righe valgono 200, 240, 260, 210, per come hai fatto
tu, scegli la riga 2 (cominciando a contare da 0). In questo caso è vero che
la componente in frequenza della riga 2 è presente (valore > THRS), però la
componente in frequenza delle altre righe è pure presente (non siamo in
presenza di un tono DTMF).
Occorre quindi definire una seconda soglia THRS_MIN (oltra a quella già
esistente che possiamo chiamare THRS_MAX) e controllare che quando una riga
supera THRS_MAX le altre 3 righe siano inferiori a THRS_MIN (stessa cosa per
le colonne).
Questo probabilmente spiega perchè rilevi il tono # anche quando c'è un
generico segnale vocale.
Il problema è determinare il valore ottimale delle soglie THRS_MAX e
THRS_MIN.
Un controllo più semplificato è avere THRS_MAX uguale a THRS_MIN che
equivale a dire che è sufficiente controllare che solo una riga (colonna)
supera THRS (ora a te manca il controllo che le altre 3 righe siano
inferiori a THRS).
E' fondamentale poter vedere gli 8 valori di sum[] (o di sum_sin[] e
sum_cos[]) al termine di una finestra temporale di MAX_SAMPLES campioni..
Questo per verificare se danno valori corretti nelle varie situazioni di
segnale di ingresso e poi, una volta appurato che funzionano bene, per
determinare i valori ottimali delle soglie THRS_MAX e THRS_MIN. In questo
modo puoi anche vedere se è sufficiente usare il controllo semplificato.
In questa fase iniziale, nel loop principale del main potresti togliere la
parte che verifica la presenza del tono # e mettere invece un contatore che
conta le finestre temporali di MAX_SAMPLES campioni. Quando il contatore ha
contato N finestre temporali (corrispondenti ad esempio a 3 secondi di
tempo) entri in un parte di codice dove metterai un breackpoint. A questo
punto ti vai a vedere gli 8 valori di sum[] (che potresti temporanemante
mettere come variabili globale) al termine dell'ultima finestra temporale
delle N finestre.
In questo modo hai tempo a sufficienza per generare un segnale di ingresso
provando diversi segnali (solo vocale, solo tono DTMF, ...). Non provare
solo il tono #, prova anche gli altri toni.

 modo puoi anche vedere se è sufficiente usare il controllo semplificato.
Si, mi sa che dovrò fare così!
Grazie dei suggerimenti, mi sono stati molto utili!
Nei prox giorni farò le prove e riporterò eventuali successi (o 
insuccessi) :)
Ciao!
Marco / iw2nzm

 sarebbe bello riuscire a starci dentro i 100 ms.
Si, penso anch'io che con 100 ms dovrebbe funzionare lo stesso, è solo un
suggerimento da provare in caso di problemi o in fase di debug.