Localisation sonore par GCC

From Eric

Revision as of 16:34, 21 April 2014 by Ejenn (Talk | contribs)
Jump to: navigation, search

Le code C de test de la GCC

Le code de la version (préliminaire est donné ci-après) :

// (Eric)

// La fréquence d'horloge est fosc= 7.42MHz x 16 (PLL) =118.72MHz // Le temps de cycle est fcy = fosc/4, soit :

  1. define FCY 29680000UL
  1. include <xc.h>
  2. include <libpic30.h>
  3. include <dsp.h>
  4. include <stdio.h>
  1. include <float.h>
  2. include "chuck_fft.h"
  3. include "fft_num_rec.h"

//----------------------------------------------------------------------------- // Configuration du chip //-----------------------------------------------------------------------------

// Horloge interne à 7.37Mhz, x16 par la PLL # 120Mhz // soit environ 30 MIPS (il y a 4 tics par cycle instruction) _FOSC(CSW_FSCM_OFF & FRC_PLL16); _FWDT(WDT_OFF); /* Watch dog Timer OFF */ _FBORPOR(PBOR_OFF ); /* Brown Out OFF, MCLR Enabled */ _FGS(CODE_PROT_OFF); /* Code Protect OFF */



// Seuil à partir duquel on fait le calcul de position (puissance au carré)

  1. define PWR_THRESHOLD 0.0

// Nombre total d'échantillons. // Attention : le buffer contient NB_SAMPLES valeurs réelles // qui vont donner NB_SAMPLES / 2 valeurs complexes après FFT.

  1. define NB_SAMPLES 128

//----------------------------------------------------------------------------- // Définition des fonctions locales //-----------------------------------------------------------------------------


// Transmet une chaine de caractère sur le port série. static void print (const char * msg);

// Démarre une séquence d'échantillonnage (63 valeurs)) void do_sampling( void );

// Effectue le calcul de corrélation void do_gcc( void );

// Affiche le résultat de la FFT sous forme d'histogramme void dump_data( int channel );

// Normalise dans [0,1] les valeurs complexes à partie imaginaire // nulle. void normalize( int channel );

// Retourne la puissance (carrée) maximale du signal complexe. float maxpwr( int channel);

// Affiche la position par un "#". // La position est comprise entre 0 et 70. void show_pos ( int pos ) ;

//----------------------------------------------------------------------------- // Définition des variables globales internes //-----------------------------------------------------------------------------

// Compteur d'échantillons. unsigned char nb_samples=0;

// Les délais calculés par la GCC doivent être filtrés. // Nombre de délais pris en compte pour le filtrage par moyenne glissante. // Doit être une puissance de 2.

  1. define MAX_DELAYS 4

// Variables utilisées pour le filtrage des délais float delays[MAX_DELAYS]; // Les délais mesurés sur les n derniers cycles; int delays_ind = 0; // L'index dans le buffer.

//----------------------------------------------------------------------------- // Définition des variables globales externes //-----------------------------------------------------------------------------

//----------------------------------------------------------------------------- // Le signal. //-----------------------------------------------------------------------------

// La variable "data" contient les NB_SAMPLES échantillons réels en entrée // et les NB_SAMPLES/2 valeurs complexes en sortie de la FFT. float data[2][NB_SAMPLES];

// Utilisés pour naviguer dans le tableau data. static unsigned int * p_int_ch0 = (unsigned int *) & data[0][0]; static unsigned int * p_int_ch1 = (unsigned int *) & data[1][0];

//----------------------------------------------------------------------------- // Définition des ports //----------------------------------------------------------------------------- // Attention : la modification d'un port de sortie doit utiliser LAT et non PORT // car si on utilise PORT, le compilo. génère un READ-MODIFY-WRITE qui va // entrainer la modification des bits AUTRES que celui que l'on veut modifier...

  1. define LED0 LATEbits.LATE0
  2. define LED1 LATEbits.LATE1

//----------------------------------------------------------------------------- // Setup ports //----------------------------------------------------------------------------- // PORT B // - D0 : --- // - D1 : --- // - D2 : <= SOUND 1 IN (Analog input) // - D3 : <= SOUND 2 IN (Analog input) // - D4 : --- // - D5 : --- // - D6 : --- // - D7 : --- // - D8 : == // PORT C // - D0 : --- // - D1 : --- // - D2 : --- // - D3 : --- // - D4 : --- // - D5 : --- // - D6 : --- // - D7 : --- // PORT D // - D0 : --- // - D1 : --- // - D2 : --- // - D3 : --- // - D4 : --- // - D5 : --- // - D6 : --- // - D7 : --- // PORT E // - D0 : => LED 1 // - D1 : => LED 2 // - D2 : --- // - D3 : --- // - D4 : --- // - D5 : --- // - D6 : --- // - D7 : --- // - D8 : --- // PORT F // - D0 : --- // - D1 : --- // - D2 : --- // - D3 : --- // - D4 : U2RX // - D5 : U2TX => Vers PC via convertisseur RS232 TTL <=> USB // - D6 : --- // - D7 : ---

// Attention : un bit à 1 dans TRIS<X> signifie "ENTREE" void setup_ports() { // Tous les ports à 0 PORTB=0; PORTC=0; PORTD=0; PORTE=0; PORTF=0;

// Positionnement de la direction des ports (par défaut en entrée)

 	TRISB = 0xFFFF;
 	TRISC = 0xFFFF;
 	TRISD = 0xFFFF;

TRISE = 0xFFFF;

           TRISEbits.TRISE0=0; // LED 0
           TRISEbits.TRISE1=0; // LED 1
 	TRISF = 0xFFFF;

}

/*

* Configuration des lignes séries
*/

void setup_serials() {

   // Configuration 8 N 1
   U2MODEbits.PDSEL = 0;
   U2MODEbits.STSEL = 0;
   // Baud Rate Generator (BRG) à 15
   // soit :
   // Fcy = 7.37e6 x 16 ) / 4
   // Bds = Fcy / (16 x (BRG+1)) = 115156 # 115200 bds
    U2BRG = 15;
   // Activation du port no 2 (broches 28=RX, 27=TX)
   U2MODEbits.UARTEN=1;
   U2STAbits.UTXEN=1;

}


/*

* Configuration des timers
*/

void setup_timers() {

   // Configuration du timer de conversion de l'ADC
   T3CONbits.TCKPS = 0b0; // Prescaler 1/1
   T3CONbits.TCS = 0 ;     // Internal clock = Tosc/4
   TMR3 = 0;
   PR3 = 295;  // Cette valeur correspond à une fréquence de 100Khz :
               // fCy/295 = 7.37e6 x 16 /4 / 295 = 100000

}

/*

* Configuration de l'ADC .
*/ 

void setup_adc() {

   // Inhibition de l'ADC
   ADCON1bits.ADON = 0;
   // Configuration en entrée analogique de AN2 et AN3
   ADPCFGbits.PCFG2 = 0;
   ADPCFGbits.PCFG3 = 0;
   // Configuration des références de tension :
   ADCON2bits.VCFG= 0b0; 	// References de tension AVDD / AVSS
   // Configuration des temps d'échantillonnage
   // TAD=TCYC/2(ADCS+1))
   ADCON3bits.ADCS=63;
   ADCON3bits.SAMC=31;
   ADCON3bits.ADRC= 0;         // Utilisation de l'horloge système.
   ADCON1bits.SIMSAM = 0b01;	// Simultaneous sampling
   ADCON1bits.SSRC = 0b010;	// Conversion trigger sur le timer 3
   ADCON1bits.ASAM = 0b1;	// L'échantillonnage est automatique après fin
                               // de la conversion
   ADCON1bits.FORM = 0b0;	// Représentation des valeurs en entier.


   // Configuration des entrées des samplers (MUXA)
   ADCHSbits.CH123NA = 0;  // CH1, CH2, CH3 negative input is VREF-
   ADCHSbits.CH123SA = 1 ; // CH1 positive input is AN3, CH2 positive input
                           // is AN4, CH3 positive input is AN5
   ADCHSbits.CH0NA = 0;    // CH0 negative input is VREF-
   ADCHSbits.CH0SA = 2;    // CH0 positive input is AN2
   ADCSSL = 0b0;
   ADCON2bits.CHPS = 0b01; // Convert channels 0 and 1
   ADCON2bits.ALTS = 0;    // MUX A only
   ADCON2bits.CSCNA = 0;   // Do not scan inputs
   ADCON3bits.ADRC=0b0;
   ADCON2bits.BUFM = 0;    // 1 seul groupe de 16 buffers
   ADCON2bits.SMPI = 1;    // Interruption après deux conversions,
                           // Les résultats sont donc dans ADCBUF0..3 puisqu'on
                           // échantillonne ch0 et ch1.
   // Activation de l'ADC
   ADCON1bits.ADON = 1;
   // Autoriser les interruptions sur ADC
   IEC0bits.ADIE = 1;

}

/*

* Configuration des ITs externes 
*/

void setup_it() {

   // Aucune it externe.

}


// Traitement de l'IT 1 void __attribute__((__interrupt__,auto_psv)) _INT1Interrupt(void) {

   // Effacement du bit d'interruption
   IFS1bits.INT1IF = 0;

}


// Traitement de l'IT 0 void __attribute__((__interrupt__,auto_psv)) _INT0Interrupt(void) {

   // Effacement du bit d'interruption
   IFS0bits.INT0IF = 0;

}


/*

* Handler de l'IT timer
*/

void __attribute__((__interrupt__, auto_psv)) _T1Interrupt( void ) {

   // Effacement du bit d'interruption.
   IFS0bits.T1IF = 0;

}


// Handler de l'IT ADC. // Elle est générée toutes les deux phases de sampling. // Le buffer contient donc 2x2 échantillons (2 par canal). void __attribute__((interrupt, no_auto_psv)) _ADCInterrupt(void) {

   // On lit les valeurs et on les place dans les buffers.
   // (Les valeurs sont placées dans des emplacements
   // contigues, donc sur une valeur réelle puis sur une valeur
   // complexes. Elles seront déplacées plus tard.)
   nb_samples--;
   *(p_int_ch0+nb_samples) = ADCBUF0;
   *(p_int_ch1+nb_samples) = ADCBUF1;
   nb_samples--;
   *(p_int_ch0+nb_samples) = ADCBUF2;
   *(p_int_ch1+nb_samples) = ADCBUF3;
   LED1 = ~LED1;
   // On arrête le timer lorsque tous les échantillons ont été obtenus.
   if ( ! nb_samples )
       T3CONbits.TON = 0;
   // Acquittement de l'interruption.
   IFS0bits.ADIF = 0;


}

//----------------------------------------------------------------------------- // Programme principal //----------------------------------------------------------------------------- void setup_globals() {

   int i;
   for ( i =0; i < MAX_DELAYS; i++)
       delays[i]= 1.0;

}

//----------------------------------------------------------------------------- // Programme principal //-----------------------------------------------------------------------------

int main() {

   // Configuration des ports d'I/O
   setup_ports();
   // Configuration des lignes séries
   setup_serials();
   // Configuration de l'ADC
   setup_adc();
   // Configuration des interruptions
   //setup_it();
   // Configuration du timer "ligne"
   setup_timers();
   // Initialisation des variables globales (si si...)
   setup_globals();
   while(1) {
       LED0 = ~LED0;
       do_sampling();
       while( nb_samples );
      do_gcc();
       // __delay_ms(100);
   }

}


// Transmet une chaine de caractère sur le port série void print ( const char * msg) {

   while (*msg)
   {
       // Attente de disponibilité du buffer
       // 1 = buffer full
       while ( U2STAbits.UTXBF );
       // Envoi du caractère
       U2TXREG = *msg;
       msg++;
   }

}

// Démarre une séquence d'échantillonnage (63 valeurs)) void do_sampling( void ) {

   nb_samples = NB_SAMPLES;
   T3CONbits.TON = 1;  // Démarrer le timer de conversion

}


void do_gcc( void ) {

   char msg[40];
   int i;
  float filtered_delay = 0.0;
       // Conversion des échantillons en flottants entre 0 et 1.
       for ( i=NB_SAMPLES-1; i>0  ; i--)
       {
           // Partie réelle.
           data[0][i] = ((float) *(p_int_ch0+i)) / 1023.0;
           data[1][i] = ((float) *(p_int_ch1+i)) / 1023.0;
       }

       // Calcul de la FFT sur des échantillons réels.
       // data doit contenir 2*N valeurs flottantes.
       // La fonction retourne N valeurs complexes.
       rfft(&data[0][0], NB_SAMPLES/2, FFT_FORWARD);
       rfft(&data[1][0], NB_SAMPLES/2, FFT_FORWARD);
       // On ne poursuit le calcul que si la puissance est suffisante.
      if (maxpwr(0) > PWR_THRESHOLD)
       {
           // Calcul du conjugué de fft(sig2)
           for ( i=0;i<NB_SAMPLES/2; i++)
                   data[1][i*2+1] = -data[1][i*2+1];
           // Calcul de la GCC : [fft(sig1)*.conj(fft(sig2))];
           for ( i=0;i<NB_SAMPLES/2; i++)
           {
              float tmp1 = data[0][i*2]*data[1][i*2]-data[0][i*2+1]*data[1][i*2+1];
              float tmp2 = data[0][i*2]*data[1][i*2+1]+data[1][i*2]*data[0][i*2+1];
              data[0][i*2] = tmp1;
              data[0][i*2+1] = tmp2;
           }
           // Calcul de la transformée inverse
           cfft(&data[0][0], NB_SAMPLES/2,FFT_INVERSE);
           // Normalisation dans [0.0, 1.0]
          normalize(0);
          // Filtrage
          delays[delays_ind] = data[0][2];
          delays_ind = ( delays_ind + 1) & (MAX_DELAYS-1);
          for ( i = 0; i < MAX_DELAYS ; i++ )
              filtered_delay += delays[i];
          filtered_delay  = filtered_delay / (float) MAX_DELAYS;
          // Affichage de la courbe
          // dump_data(0);
          // Affichage du décalage (100 est au centre)
          show_pos((int)(filtered_delay*70.0));
       }

}

// Retourne la puissance (carrée) maximale du signal complexe. float maxpwr( int ch ) {

   int i;
   // Calcul du max
   float pwr, maxpwr = FLT_MIN;
   for ( i = 1; i< (NB_SAMPLES/2); i++)
   {
           pwr=data[ch][i*2]*data[ch][i*2]+data[ch][i*2+1]*data[ch][i*2+1];
           if ( pwr > maxpwr )
           {
                   maxpwr = pwr;
           }
   }
   return maxpwr;

}

// Affiche le résultat de la FFT sous forme d'histogramme void dump_data( int channel ) {

   int col, line;
   print("\33[2J");
   for ( line= 32; line>=0; line--)
   {
     for (col=1; col< NB_SAMPLES/2; col++)
      {
         float val = line / 32.0 ;
           if  ( data[channel][col<<1] >=  val )
               print("#");
           else
               print(" ");
     }
     print("\n\r");
   }

}


// Retourne la position sous la forme d'une chaine // pos est comprise entre 0 et 70. // ------------<>---- void show_pos ( int pos ) {

   char  msg[74];
   int i = 0;
   
   for ( i = 0; i < pos ; i++ )
       msg[i]=' ';
   msg[pos]='#';
   msg[pos+1]='\r';
   msg[pos+2]='\n';
  msg[pos+3]=0;
   print(msg);

} /* Normalise les valeurs (réelles) du canal */ void normalize( int channel ) {

   int i, imax, imin = 0;
   double vmax = FLT_MIN;
   double vmin = FLT_MAX;
   for ( i =0; i<NB_SAMPLES/2;i++)
   {
       if ( data[channel][i*2] > vmax )
       {
           imax = i;
           vmax = data[channel][i*2];
       }
       else
       if ( data[channel][i*2] < vmin )
       {
           imin = i;
           vmin = data[channel][i*2];
       }
   }
   if ( vmax != vmin )
       for ( i =0; i<NB_SAMPLES/2;i++)
           data[channel][i*2] = (data[channel][i*2]-vmin) / (vmax-vmin);


}

Personal tools