hdo  beta
/home/mrazik/Documents/web/old/hdo/goertzel.c
00001 //
00002 // C Interface: goertzel
00003 //
00004 // Description: 
00005 //
00006 //
00007 // Author: Miroslav Mraz <Mrazik@volny.cz>, (C) 2009
00008 //
00009 // Copyright: See COPYING file that comes with this distribution
00010 //
00011 //
00012 #include <stdio.h>
00013 #include <math.h>
00014 #include <utility/trace.h>
00015 
00016 #include "goertzel.h"
00017 /*----------------------------------------------------------------------------
00018  *  calc_coeffs
00019  *----------------------------------------------------------------------------
00020  */
00021 // NOCOMPUTE definovano v Makefile
00022 // #define NOCOMPUTE  // ! pokud se toto definuje, zmenší se délka kódu podstatně, počítá vždy ve float
00023 aritfmt calc_coeff (float nfreq) {
00024 #ifdef  NOCOMPUTE
00025   return 1706;    // napevno pro 216,6 Hz
00026 #else //  NOCOMPUTE - je to tu pro ukázku, jak se to počítá
00027   aritfmt  reval;
00028   float coeff;
00029   coeff  = 2.0 * 3.141592654 * nfreq;
00030   coeff  = 2.0 * cos (coeff);
00031 #ifndef  COMPUTE_FLOAT
00032   // aby se to dalo počítat v int, tak to tady zvětšíme
00033   coeff *= (float)(1 << ISHIFT);
00034 #endif //  COMPUTE_FLOAT
00035 
00036   reval  = (aritfmt) coeff;
00037   // TRACE_INFO ("coeff = %d\r\n", reval);
00038   return reval;
00039 #endif // NOCOMPUTE
00040 }
00041 /*----------------------------------------------------------------------------
00042  *  goertzel
00043  *----------------------------------------------------------------------------
00044  */
00045 int goertzel (aritfmt coeff, short *samples, int n) {
00046   register aritfmt  rv,q0,q1=0,q2=0;    // prefix register je použit pro případnou optimalizaci
00047   register int      i;
00048   //aritfmt  min=0,max=0;               // hledáme přetečení
00049 #ifdef  COMPUTE_FLOAT
00050   aritfmt  a,b,c;
00051 #endif // COMPUTE_FLOAT
00052 
00053   // pro všechny vzorky
00054   for (i=0; i<n; i++) {
00055     q0   = coeff * q1;
00056     // pokud byl coeff zvětšen, je třeba to tu zase zmenšit. Bez FPU je to ale rychlejší než počítat ve float.
00057 #ifndef COMPUTE_FLOAT
00058     q0 >>= ISHIFT;                        // zmenšení pro int
00059 #endif // COMPUTE_FLOAT
00060     q0  += (aritfmt) samples[i] - q2;     // vlastní výpočet
00061 
00062     q2   = q1;                            // posuv o vzorek
00063     q1   = q0;                            // rekurze
00064   }
00065   // po n vzorcích vypočteme kvadrát výkonu signálu, počítá se to jen jednou za čas (120 ms)
00066 #ifdef COMPUTE_FLOAT
00067   a  = (float) q1;
00068   b  = (float) q2;
00069   c  = (float) coeff;
00070   a /= (float)(1 << HSHIFT);              // zmenšíme, jinak jsou tam moc velká čísla a přeteče to
00071   b /= (float)(1 << HSHIFT);
00072   // výpočet kvadrátu výkonu signálu pro danou frekvenci
00073   rv = (a * a) + (b * b) - (c * a * b);
00074   TRACE_INFO ("Presna hodnota = %f\n", r);
00075   return (int) r;
00076 #else
00077   // Předchozí jde udělat i v integer-32, ale není to tak přesné
00078   q1 >>= HSHIFT;
00079   q2 >>= HSHIFT;
00080   rv   = q1 * q2;
00081   // TRACE_INFO ("q1*q2=%d\n", r);
00082   rv  *= -coeff;
00083   rv >>= ISHIFT;              // tady nutno zmenšit tak, jak bylo zvětšeno v calc_coeff()
00084   rv  += q1 * q1 + q2 * q2;   // výkon by byl sqrt (rv), není nutné počítat, napětí stačí
00085   return (int) rv;
00086 #endif // COMPUTE_FLOAT
00087 }