Outils pour utilisateurs

Outils du site


tptns2016

TP 1 Architecture pour le TNS: Mise en oeuvre sur PC avec type double

Objectifs

  • Maîtrise de l'environnement de développement QT
  • Utilisation des structure de contrôle et de données pour le filtrage
  • Codage de l'équation de récurrence pour appliquer le filtre
  • Test sur des filtres RII et RIF simples

Récupération du projet

Pour récupérez le fichier zip du projet la première fois, ouvrir une console (alt+F2 et taper lxterm) puis taper dedans:

cd /mnt/etu/s4en/..... à compléter

puis copier coller (sélectionner le code qui suit puis clic sur le bouton central de la souris dans le terminal) :

wget https://bvdp.inetdoc.net/files/iut/tp_tns/TP_ARCHI_TNS.zip
unzip TP_ARCHI_TNS.zip
cd TP_ARCHI_TNS
qtcreator TP_ARCHI_TNS.pro &
echo "C est bon !"

Travail à effectuer

Compiler et exécuter le projet.

  1. Donner l'équation de récurrence du filtre décrit dans la fonction void code_tparchi1(void) du fichier tparchi1.cpp. Indiquer le type de filtre (RII ou RIF).
  2. Dans le fichier filtre.cpp, implémenter les fonctions:
    1. filtreUnEchantillon qui applique l'équation de récurrence pour un échantillon du signal comme décrit en TD.
    2. calculReponseFiltreEnLigne qui appelle l'initialisation des variables globales en fonction du filtre passé en paramètre (fonction initFiltreUnEchantillon), et qui applique le filtrage à chacun des échantillons.
  3. Vérifier l'exactitude des réponses temporelles pour le filtre fourni jusque $k=6$.
  4. Déclarer les variables nécessaires pour ajouter un second filtre d'équation de récurrence: $ s_k=e_k+0.3s_{k-7} $
  5. Calculer les réponses temporelles en utilisant la fonction calculReponseFiltreEnLigne et ajouter le code permettant de les afficher. Superposer l'affichage du module de la fonction de transfert de ce filtre à l'affichage précédent.

TP 2 Architecture pour le TNS: Simulation des convertisseurs et quantifications des coefficients du filtre

Fonctions simulant les convertisseurs

Nous souhaitons maintenant intégrer les convertisseur dans la chaîne de traitement en les simulant par des fonctions:

  1. simuADC dont le rôle est de convertir un nombre représentant une tension en une valeur numérique quantifiée.
  2. simuDAC dont le rôle est de convertir une valeur numérique quantifiée en un nombre représentant une tension.

Le schéma suivant représente l'agencement de ces deux fonctions relativement à FiltreUnEchantillon.

Implémenter les fonctions simuADC et simuDAC dans le fichier filtre.cpp.

Définir 2 variables globales nbBitsQuantDAC et nbBitsQuantADC à la valeur 10.

Tester ces 2 fonctions en les appelant depuis calculReponseFiltreEnLigne avec des valeurs d'entrées connues et vérifier les valeurs de retour en mode Debug pour au moins 3 points.

Intégrer dans la fonction calculReponseFiltreEnLigne l'appel de ces 2 fonctions pour les utiliser avec la fonction filtreUnEchantillon:

  1. appeler simuADC et stocker sa valeur de retour dans une variable du bon type.
  2. appeler filtreUnEchantillon en lui fournissant en paramètre la variable précédemment calculée (qui sera convertie automatiquement en double).
  3. appeler simuDAC en lui fournissant la valeur fournie par filtreUnEchantillon et ranger la valeur de sortie de simuDAC dans le tableau sk.

Régler les valeurs des variables globales:

  1. OFFSET_ENTREE et OFFSET_SORTIE doivent être réglées en fonction de nbBitsQuantDAC et nbBitsQuantADC.
  2. VALEUR_MAX et VALEUR_MIN pour s'assurer que la fonction filtreUnEchantillon fournisse des valeurs en sortie dans la plage admissible pour le type unsigned short int qui sera utilisé par la suite.

Modifier la valeur des échantillons pour l'entrée du filtre. Un offset de 3.3V/2 doit être ajouté à chaque échantillon.

Faire le test du programme en traçant les réponses temporelles du filtre.

Quantification des coefficients

Les coefficients du filtre sont stockés dans des tableaux de valeurs codées en double. Nous souhaitons maintenant les approximer par codage en virgule fixe au format Q0.15.

Déterminer les types adéquats pour les paramètres d'entrée/sortie de la fonction quantCoeffsShortInt. Cette fonction prend en entrée:

  1. un tableau tabDouble des coefficients à convertir.
  2. un tableau tabInt pour ranger les coefficients convertis en virgule fixe.
  3. un tableau tabDoubleQuant pour ranger les coefficients convertis en virgule fixe exprimés en virgule flottante. Ce tableau permettra de comparer les valeurs flottantes avant et après conversion.
  4. un nombre de coefficients nbCoeffs indiquant le nombre de case du tableau tabDouble à traiter.
  5. une valeur nbBitsFractionnairenbCoeffs indiquant le nombre de bits utilisés pour la partie fractionnaires des nombres en virgule fixe.

Coder en C cette fonctions. On utilisera l'arrondi plutôt que la troncature pour le calcul.

Le code de la fonction quantCoeffsShortInt ci dessous doit être intégré dans filtre.cpp.

simu3.cpp
    void quantCoeffsShortInt(double * tabDouble, short int * tabInt, double * tabDoubleQuant, int nbCoeffs, unsigned int nbBitsFractionnaire){
      int i;
      long int temp;
      for (i=0;i<nbCoeffs;i++)
        {
       A FAIRE: affecter à la variable temp le résultat de la conversion de tabDouble[i] au format virgule fixe signé avec nbBitsFractionnaire bits après la virgule. 
 
 
            //Vérifier qu'il n'y a pas de dépassement sinon saturer
            if (temp>( (1<<15)-1) )
              temp= (1<<15)-1;
            if (temp<( -(1<<15)) )
              temp=-(1<<15);
            tabInt[i]= (short int)temp;
            tabDoubleQuant[i]=tabInt[i] / (double)(1<<nbBitsFractionnaire);
            printf("coefficient %d valant %lf codé avec %d bits pour la partie fractionnaire: %d soit %lf\n",i,tabDouble[i],nbBitsFractionnaire,tabInt[i],tabDoubleQuant[i]); fflush(stdout);      
        }
    }

Compléter cette fonction pour réaliser la conversion.

Tester cette fonction avec les constantes définies dans un tableau

double val[]={ 0, 0.999, 0.0001, 1, 2, -1, 0.5, -0.5};

Réaliser la conversion vers les formats Q0.15, Q3.12 et analyser les résultats. Indiquer quels coefficients ont été codés de manière:

  1. exacte
  2. approchée correctement (par une valeur dont l'erreur inférieure ou égale au pas de quantification /2 du format en virgule fixe.)
  3. non correcte (à cause des saturations)

Opérations de calcul en virgule fixe

Vous allez maintenant implémenter le filtre avec les calculs en virgule fixe. Nous traiterons uniquement des filtres RIF car les filtres RII sont plus complexes à dimensionner.

Aide au dimensionnement

  1. Pour les RIF, le tableau memoireVk contient les échantillons d'entrée $\hat{ek}$ = $ek$ - OFFSET_ENTREE, interprété au format Qn.0.
  2. On considère que les coefficients du numérateur de la fonction de transfert (b[i]) sont codés en Q0.15.
  3. On utilise ici un entier 32 bits pour stocker le résultat de $\hat{s_k}=\sum_{i=0}^{N}b_i.\hat{e_{k-i}}$

Indiquer le format de $\hat{s_k}$ par application du calcul sur les entiers codant les nombres en virgule fixe.

On souhaite un résultat entier au format UQN.0 pour piloter le DAC. Avant l'ajout d'offset numérique sur la sortie dû au référencement par rapport au zéro, on utilise donc le codage au format Q(N-1).0. Quelle opération doit on effectuer pour convertir le résultat de $\hat{s_k}$ à ce format?

En déduire l’intérêt d'avoir effectué les calculs intermédiaires sur 32 bits pour obtenir un résultat sur 16 bits.

Si la sortie de la fonction d'application du calcul doit être utilisée pour piloter un DAC 10 bits, indiquer les opérations à effectuer pour s'assurer que le résultat obtenu après conversion puisse être codé au format UQ10.0 (et donc Q(N-1).0 avant ajout d'offset numérique).

Le schéma suivant représente les différentes opérations et formats utilisés. Compléter les parties manquantes.

En vous inspirant du codage de la fonction double filtreUnEchantillon(double ek) (qui réalise un filtre RII), proposer une implémentation de unsigned short int filtreUnEchantillon_RIF(unsigned short int ek) qui réalise un filtre RIF en VIRGULE FIXE. Pour cela, vous modifierez le code imprimé sur la feuille distribuée pendant la séance ( https://bvdp.inetdoc.net/files/iut/tp_tns/filtreUnEchantillon.pdf ). Vous ferez valider cette étape d'analyse avant de réaliser le codage sur machine.

Test du filtre en virgule fixe sur un filtre simple

Pour le test, vous utiliserez le filtre RIF simple défini par les tableaux suivants:

filtreRIF.cpp
    const int NB_COEFF_B=3 ;
    double coeffB[NB_COEFF_B]={0.5,0.7,0.3};
    const int NB_COEFF_A=1;
    double coeffA[NB_COEFF_A]={1};

Tester la réponse du filtre en utilisant la fonction double filtreUnEchantillon(double ek).

Pour l'implémentation en virgule fixe, vous devrez définir 4 pointeurs en variables globales:

short int * a_Q015;
double * a_q;
short int * b_Q015;
double * b_q;
short int *memoireVk_i;

Dans la fonction initFiltreUnEchantillon, vous réaliserez l'allocation dynamique des tableaux à la bonne taille:

 a_Q015=new short int[NB_COEFF_A];
 a_q=new double[NB_COEFF_A];
 b_Q015=new short int[NB_COEFF_B];
 b_q=new double[NB_COEFF_B];
 memoireVk_i=new short int[NB_COEFF_B+1];
 

Toujours dans la fonction initFiltreUnEchantillon, vous réaliserez ensuite le codage en virgule fixe des coefficients en appelant la fonction de conversion:

printf("\n Coefficients a:\n");
quantCoeffsShortInt(a, a_Q015, a_q, NB_COEFF_A,  NB_BITS_FRACTIONNAIRE);
printf("\n Coefficients b:\n");
quantCoeffsShortInt(b, b_Q015, b_q, NB_COEFF_B,  NB_BITS_FRACTIONNAIRE);

Puis vous initialiserez le contenu du tableau memoireVk_i à 0:

for (i=0;i<NB_COEFF_B+1;i++)
      memoireVk_i[i]=0;

Coder la fonction unsigned short int filtreUnEchantillon_RIF(unsigned short int ek) puis remplacez l'appel de double filtreUnEchantillon(double ek) par cette nouvelle fonction et vérifiez que les réponses temporelles sont inchangées.

Test du filtre en virgule fixe sur un filtre de moyenne complexité

Pour le test, vous utiliserez le filtre défini par les tableaux suivants:

filtreRIF.cpp
    const int NB_COEFF_B=201 ;
    double coeffB[NB_COEFF_B]={0.0010497657664069,0.0010248032141060,0.0008950989702105,0.0006754260236328,0.0003914824095416,0.0000766934492105,-0.0002321048737491,-0.0004995386970487,-0.0006964600877342,-0.0008039239652070,-0.0008157836216176,-0.0007394830559167,-0.0005948664932619,-0.0004111061172667,-0.0002221300434536,-0.0000611735162575,0.0000447598151640,0.0000796811308439,0.0000418604532548,-0.0000550716758918,-0.0001834867559374,-0.0003055590399293,-0.0003791090967164,-0.0003647560591893,-0.0002333866931914,0.0000271842699248,0.0004074693745616,0.0008744059311998,0.0013728937238789,0.0018309281895384,0.0021679805132536,0.0023057764653131,0.0021801999890144,0.0017527602862087,0.0010199632250395,0.0000190482922570,-0.0011711072134873,-0.0024345821147888,-0.0036284987472194,-0.0045978697208798,-0.0051937725260009,-0.0052928489822895,-0.0048157655121297,-0.0037421830301847,-0.0021200045223263,-0.0000671897708197,0.0022347930844576,0.0045558495491448,0.0066394125108034,0.0082298250869960,0.0091021062349281,0.0090907379538161,0.0081139702738372,0.0061904123965458,0.0034453596342308,0.0001053400669057,-0.0035193504333161,-0.0070629599518906,-0.0101409269984312,-0.0123912150958553,-0.0135159141893970,-0.0133183847721507,-0.0117314514765957,-0.0088329189234603,-0.0048459221009567,-0.0001232158250872,0.0048837149938801,0.0096680778553808,0.0137195341098129,0.0165789638233212,0.0178903118599852,0.0174436522975187,0.0152043335317103,0.0113244013729469,0.0061343135538643,0.0001150678565127,-0.0061469611180756,-0.0120181883656648,-0.0168831175207452,-0.0202099218564704,-0.0216092075118743,-0.0208793906867327,-0.0180333949515915,-0.0133032695303891,-0.0071216486525055,-0.0000814675096998,0.0071222518254061,0.0137626997041129,0.0191563866117297,0.0227351202843601,0.0241071081284681,0.0231004857065963,0.0197843647677042,0.0144648478539781,0.0076561189341496,0.0000294013994965,-0.0076550186151438,-0.0146256412959852,-0.0201792994512465,-0.0237539929269980,0.9750127221960708,-0.0237539929269980,-0.0201792994512465,-0.0146256412959852,-0.0076550186151438,0.0000294013994965,0.0076561189341496,0.0144648478539781,0.0197843647677042,0.0231004857065963,0.0241071081284681,0.0227351202843601,0.0191563866117297,0.0137626997041129,0.0071222518254061,-0.0000814675096998,-0.0071216486525055,-0.0133032695303891,-0.0180333949515915,-0.0208793906867327,-0.0216092075118743,-0.0202099218564704,-0.0168831175207452,-0.0120181883656648,-0.0061469611180756,0.0001150678565127,0.0061343135538643,0.0113244013729469,0.0152043335317103,0.0174436522975187,0.0178903118599852,0.0165789638233212,0.0137195341098129,0.0096680778553808,0.0048837149938801,-0.0001232158250872,-0.0048459221009567,-0.0088329189234603,-0.0117314514765957,-0.0133183847721507,-0.0135159141893970,-0.0123912150958553,-0.0101409269984312,-0.0070629599518906,-0.0035193504333161,0.0001053400669057,0.0034453596342308,0.0061904123965458,0.0081139702738372,0.0090907379538161,0.0091021062349281,0.0082298250869960,0.0066394125108034,0.0045558495491448,0.0022347930844576,-0.0000671897708197,-0.0021200045223263,-0.0037421830301847,-0.0048157655121297,-0.0052928489822895,-0.0051937725260009,-0.0045978697208798,-0.0036284987472194,-0.0024345821147888,-0.0011711072134873,0.0000190482922570,0.0010199632250395,0.0017527602862087,0.0021801999890144,0.0023057764653131,0.0021679805132536,0.0018309281895384,0.0013728937238789,0.0008744059311998,0.0004074693745616,0.0000271842699248,-0.0002333866931914,-0.0003647560591893,-0.0003791090967164,-0.0003055590399293,-0.0001834867559374,-0.0000550716758918,0.0000418604532548,0.0000796811308439,0.0000447598151640,-0.0000611735162575,-0.0002221300434536,-0.0004111061172667,-0.0005948664932619,-0.0007394830559167,-0.0008157836216176,-0.0008039239652070,-0.0006964600877342,-0.0004995386970487,-0.0002321048737491,0.0000766934492105,0.0003914824095416,0.0006754260236328,0.0008950989702105,0.0010248032141060,0.0010497657664069};
    const int NB_COEFF_A=1;
    double coeffA[NB_COEFF_A]={1};

Dans le fichier tparchi1.cpp, régler FECH à 1000 (Hz) et tracer la réponse fréquentielle entre 0.1Hz et 500Hz pour 10000 échantillons de fréquences.

filtreRIF.cpp
   const double FECH=1000;
   const double FMIN=0.1;
   const double FMAX=(FECH/2.0);
   const int NB_FREQ=10000;  

L’exécution du programme doit produire la réponse fréquentielle suivante:

Quelle est la fonction réalisée par ce filtre au niveau fréquentiel?

La réponse fréquentielle affichée est calculée uniquement grâce au valeur des coefficients. Vous allez maintenant vérifier le comportement du filtre au niveau temporel. Pour cela vous allez générer les réponses temporelles pour des signaux d'entrée sinusoïdaux et vérifier la réponse du filtre.

TP 3 Architecture pour le TNS: Mise en place de l'architecture de traitement sur Arduino

Installation de la librairie permettant d'utiliser le convertisseur Numérique analogique

Afin de pouvoir utiliser le shield DAC (présenté sur: https://bvdp.inetdoc.net/wiki/doku.php?id=tpdacspi ), vous devez installer une librairie. Pour cela, copiez-collez dans une console après l'avoir ouverte avec ALT+F2 lxterm :

mkdir ~/sketchbook
cd ~/sketchbook
mkdir libraries
cd libraries
wget https://bvdp.inetdoc.net/files/iut/tp_dacspi/Tlv5637.zip
unzip -o Tlv5637.zip
rm Tlv5637.zip

Au lancement d'arduino, si c'est la première fois qu'il est exécuté sur cette machine, sélectionnez le dossier sketchbook.

Programme de traitement

La plateforme de traitement se compose de :

  1. Un arduino Uno R3
  2. Un “shield” DAC TLV5637

Dans ce TP nous allons mettre en place l'architecture qui permet de :

  1. Acquérir un signal analogique sur la voie 0 au rythme de 2kHz
  2. Appliquer l'équation de récurrence du filtre RIF en point fixe
  3. Générer la sortie du filtrage sur la voie A du DAC

Les exercices suivants sont à réaliser à l'aide du dernier TD en complétant le code suivant.

#include <SPI.h>
#include <Tlv5637.h>


unsigned ek, sk_suiv; 

const int chipSelectPin = 3;
TLV5637 DAC(chipSelectPin,REF_2048MV_TLV5637);

ISR(TIMER2_COMPA_vect){
   //PORTB |= 0x01 ; //à dé-commenter plus tard
   // Lancer une conversion ADC
   startADC0Conversion();
   // Lire le résultat de la conversion
   ek = readADC0Conversion();
      
   // Filtrer ek : appel du filtre avec ek en entrée et sk_suiv en sortie
   sk_suiv = ek ;
   
   //ecrire sk_suiv sur DACA
   
   //ecrire ek sur DACB pour vérification
     
   PORTB ^= 0x01 ; //à commenter plus tard
   //PORTB &= ~0x01 ; //à dé-commenter plus tard
}

void setupTimer2(unsigned char prescaler, unsigned int period){
  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0
  //compléter avec la configuration du TIMER 2 vue en TD
}


void setupDAC(){
  //à compléter
}

void setupADC0(){
  //à compléter
}

void startADC0Conversion(){
  //à compléter
}

unsigned int readADC0Conversion(){
    //à compléter
}


void setup() {
  setupADC0();
  setupDAC();
  cli();
  setupTimer2(0, 0);
  sei();
  Serial.begin(115200);
  DDRB |= 0x01 ; //set pin B[0] as output
  PORTB |= 0x01 ; //set PIN B[0] to high
}

void loop() {
  //exercice 2 : Générer une rampe sur la voie A du DAC
}

Mise en place de la base de temps

Pour mettre en place un base de temps qui cadencera l'acquisition et le traitement, il est dans un premier temps nécessaire de compléter la fonction setupTimer, puis modifier la fonction setup pour lui passer les bons arguments (fréquence d’interruption 2kHz). Vérifiez le bon fonctionnement en observant le signal sur la broche D8 de l'arduino.

Le code fournit implémente déja le gestion d'interruption ISR(TIMER2_COMPA_vect). Dans cette fonction, le code mis en place complémente la sortie 8 (PORTB ^= 0x01) ce qui à pour effet de faire changer l'état de la broche 8 au rythme des interruptions générées par le timer 2.

Mise en place de l'acquisition

Comme vu en TD, compléter les fonctions setupADC0, startADC0Conversion, readADC0Conversion pour respectivement :

  1. configurer la conversion sur le canal 0 avec une fréquence ADC de 125kHz
  2. Démarrer une conversion sur le canal 0
  3. Attendre la fin de la conversion et récupérer le résultat de la conversion.

Vérifiez le bon fonctionnement en complétant le code de la fonction loop pour afficher le résultat de la conversion (fonction Serial.println) dix fois par seconde (utilisation de la fonction delay). La carte shield TLV5637 dispose de deux potentiomètres marqués A0 et A1. Lorsque le cavalier au dessus du potentiomètre A0 est connecté, le canal A0 est connecté la sortie du montage pont diviseur réalisé à l'aide du potentiomètre. Pour tester l'ADC vous pouvez donc utiliser ce potentiomètre pour faire varier la tension en entrée de ADC0.

Mise en place de la génération d'un signal

Comme vu en TD compléter le code de la fonction setupDAC(). Pour vérifier le fonctionnement, implémentez dans la fonction loop() la génération d'une rampe périodique de 0v à 4,096v sur la voie A du DAC.

Test de l'architecture

On souhaite recopier sous interruption la tension d'entrée de l'ADC directement en sortie du DAC pour vérifier que l'Arduino est correctement configuré avant de passer au filtrage.

Modifiez et complétez le code de la fonction ISR(TIMER2_COMPA_vect) de la façon suivante:

ISR(TIMER2_COMPA_vect){
   PORTB |= 0x01 ; // patte 8 à 1 pour mesurer le temps passé dans l'IT
   // Lancer une conversion ADC
   
   // Filtrer ek : appel du filtre avec ek en entrée et sk_suiv en sortie
   sk_suiv = ek ;   // simple recopie b0=1 !

   //ecrire sk_suiv sur DACA
   
   //ecrire aussi ek sur DACB pour comparaison
     
   // Lire le résultat de la conversion
   ek = 
       
   PORTB &= ~0x01 ; // patte 8 à 0 
}

Connexion

IMPORTANT: Ne jamais connecter de signal sur l'entrée analogique A0 avant de s'être assuré avec l'oscilloscope que le signal soit dans la plage 0 à 5V!!!

Le signal connecté à l'entrée A0 du shield pourra avoir une amplitude crête à crête inférieure à 5V mais devra être centré sur 2.5V.

Sur la sortie A du DAC, la sortie du filtre est visible, avec une amplitude crête à crête maximale de 4.096V.

Sur la sortie B du DAC, une version numérisée et atténuée de l'entrée du filtre est visible, avec une amplitude crête à crête maximale de 4.096V.

Sur la broche 8 de l'Arduino, un signal périodique indique la fréquence de l'interruption Timer 2. La durée à l'état haut indique la durée nécessaire au traitement de l'interruption TIMER.

Mesures

  1. Mesurez $T_{ech}$. Est-elle conforme à ce que vous avez configuré ?
  2. Mesurez le temps passé dans le sous-programme d'interruption. Est-il cohérent avec le réglage choisi pour l'ADC ?
  3. Déduisez-en le temps disponible pour effectuer le calcul de l'équation de récurrence avec cette période d'échantillonnage.

TP 4 Architecture pour le TNS: Implémentation de filtres RIF sur Arduino

Déclarations

Il faut déclarer le filtre original (type double) comme sur Qt en variable globale et ajouter un tableau d'entiers pour stocker les coefficients du filtre au format Q0.15. Soit pour le filtre $s_k=\frac{1}{2}e_k + \frac{1}{2}e_{k-1}$ :

filtreRIF.cpp
    const int NB_COEFF_B=2 ;
    double coeffB[NB_COEFF_B]={0.5,0.5};
    short int coeffB_i[NB_COEFF_B];

Initialisations

Vous devez initialiser le tableau coeffB_i dans la fonction setup() en appelant quantCoeffsShortInt(double * tabDouble, short int * tabInt, int nbCoeffs, unsigned int nbBitsFractionnaire). On rappelle que cette fonction a été testée dans Qt, néanmoins, il faut l'adapter pour l'arduino.

filtre_i.cpp
void quantCoeffsShortInt(double * tabDouble, short int * tabInt, int nbCoeffs, unsigned int nbBitsFractionnaire){
     int i;
     long int temp;
     for (i=0;i<nbCoeffs;i++)
       {
        Serial.print(i,DEC);
        Serial.print(":");
        Serial.print( tabDouble[i]);
        Serial.print(":");
        temp= floor(  (tabDouble[i]  * (((unsigned long int)1)<<nbBitsFractionnaire) ) + 0.5) ; //1 <<nbBitsFractionnaire génère une valeur négative sans le cast
        //Vérifier qu'il n'y a pas de dépassement sinon saturer
        if (temp>( (1<<15)-1) )
           temp= (1<<15)-1;
        if (temp<( -(1<<15)) )
           temp=-(1<<15);
        tabInt[i]= (short int)temp;
        Serial.print( tabInt[i],DEC);
        Serial.print(":");
        Serial.println( tabInt[i],HEX);
      }
   }
  1. Vérifiez dans la console que les coefficients sont correctement codés.

Calcul de l'équation de récurrence

Copiez le code suivant et appelez-le correctement dans le sous-programme d'interruption pour calculer sk_temp à partir de ek. Vous devrez déclarer toutes les variables nécessaires.

filtre_i.cpp
unsigned int indice_ecr=0;
const int NB_COEFF_B=2 ;
double coeffB[NB_COEFF_B]={0.5,0.5};
const unsigned int MEMORYSIZE=NB_COEFF_B+1;
const unsigned int NB_BITS_FRACTIONNAIRE = 15;
short int memoireVk_i[MEMORYSIZE];
short int coeffB_i[NB_COEFF_B];
    /////////////////////////////////////////////////////////////////
    short int filtreUnEchantillon_i(short int ek){
        short int temp; // calcul intermediaire 
        int i;  //indice de lecture pour les valeurs des coefficients du filtre
        int j; //indice de lecture pour les valeurs des coefficients du filtre
        int indice_lec; //indice de lecture pour les échantillons d'entrée
        long int skout; //valeur pour la sortie calculée
        //suppression offset numérique
        temp=ek-512;
        //pour un RIF, NB_COEFF_A=1 donc vktemp sera inchangé
        //rangement de la valeur calculée dans tableau vk à l'indice indice_ecr
        memoireVk_i[indice_ecr]=temp;
        //calcul de sk
        skout = 0; //valeur par défaut pour le résultat
        indice_lec = indice_ecr;
        for (i = 0; i <NB_COEFF_B; i++){ //N+1 itérations
            skout += (long int) coeffB_i[i] * (long int) memoireVk_i[indice_lec];
            //ici le premier vk pris en compte est vk, donc on décremente indice_lec avant après utilisation
            indice_lec=indice_lec-1;
            if (indice_lec < 0)
                indice_lec = MEMORYSIZE - 1;
        }
        //incrémentation du pointeur d'écriture pour la prochaine itération
        indice_ecr = (indice_ecr + 1);
        if (indice_ecr>=MEMORYSIZE)
            indice_ecr=0;
         if  (skout & (1<<(NB_BITS_FRACTIONNAIRE-1)) ) // calcul de l'arrondi
          skout = (skout >> NB_BITS_FRACTIONNAIRE ) + 1;	 
         else					 
          skout = (skout >> NB_BITS_FRACTIONNAIRE ) ;
         if (skout >  511 )		               // saturation du résultat sur 10 bits
          skout = 511 ;		 
         else if (skout < -512 )	 
          skout = -512 ;	     
        //ajout offset numérique 
         return  skout + 512; 
    }
    /////////////////////////////////////////////////////////////////

Caractérisation du filtre

  1. Quel est le type de ce filtre et mesurez le Gain max en vous basant sur la mesure automatique de l'amplitude crête à crête.
  2. Mesurez la bande passante à -3dB

Analyse des performances

  1. En mettant à 1 la broche 10 de l'arduino juste avant le calcul de sk_temp et en la remettant à 0 juste après, mesurez le temps nécessaire au calcul d'une équation de récurrence.
  2. Mesurer le temps total passé dans l'interruption timer. Est-il égal au temps de conversion de l'ADC additionné du temps de calcul de l'équation de récurrence ? Pourquoi ?
  3. Déduisez-en la fréquence d'échantillonnage maximale pour un filtre réalisant l'équation $s_k=\frac{1}{2}e_k + \frac{1}{2}e_{k-1}$
  4. Refaites ces mesures pour le filtre passe-bas à 11 coefficients ci-dessous.
  5. Déduisez-en le nombre de coefficients maximal de l'équation de récurrence pour une fréquence d'échantillonnage de 2kHz. Vérifier avec les exemples suivants :

Changement de filtre

Version filtre passe bas, fc=260Hz pour fe=2khz

 #define SIZEFILTER_RIF 11 
 const double numCoeff_d_FILTRE_RIF[SIZEFILTER_RIF] = {-0.0349249458233189,-0.0370641891927234,0.0209236117413746,0.1368055347416942,0.2562279031324722,0.3071029997932009,0.2562279031324722,0.1368055347416942,0.0209236117413746,-0.0370641891927234,-0.0349249458233189};

Passe haut, fe=2khz, fpass (-1dB) =275Hz, fstop (-40dB) =200Hz, equiripple

coeff du filtre RIF 41 termes: num

#define SIZEFILTER_RIF 41 
const double numCoeff_d_FILTRE_RIF[SIZEFILTER_RIF]={ 0.016244657856333,-0.018864421488768,-0.012238782422342,-0.005085409431928,0.003969691594410, 0.012267146479049, 0.015030618183398, 0.008966346831124, -0.004682203645285,-0.019357735197421,-0.025875762043837,-0.017435787337996, 0.005471034822059, 0.033037851847265, 0.049218355348873, 0.038520192602532, -0.006112347636894,-0.078041093847378,-0.157847718052422,-0.220135476387537,0.756344281504610,-0.220135476387537,-0.157847718052422,-0.078041093847378, -0.006112347636894, 0.038520192602532, 0.049218355348873, 0.033037851847265,0.005471034822059,-0.017435787337996,-0.025875762043837,-0.019357735197421, -0.004682203645285, 0.008966346831124, 0.015030618183398, 0.012267146479049,0.003969691594410,-0.005085409431928,-0.012238782422342,-0.018864421488768, 0.016244657856333};

Rejecteur 50Hz, fe=1khz, module de la fonction de transfert:

#define SIZEFILTER_RIF 201 
const double numCoeff_d_FILTRE_RIF[SIZEFILTER_RIF] = {0.0010497657664069,0.0010248032141060,0.0008950989702105,0.0006754260236328,0.0003914824095416,0.0000766934492105,-0.0002321048737491,-0.0004995386970487,-0.0006964600877342,-0.0008039239652070,-0.0008157836216176,-0.0007394830559167,-0.0005948664932619,-0.0004111061172667,-0.0002221300434536,-0.0000611735162575,0.0000447598151640,0.0000796811308439,0.0000418604532548,-0.0000550716758918,-0.0001834867559374,-0.0003055590399293,-0.0003791090967164,-0.0003647560591893,-0.0002333866931914,0.0000271842699248,0.0004074693745616,0.0008744059311998,0.0013728937238789,0.0018309281895384,0.0021679805132536,0.0023057764653131,0.0021801999890144,0.0017527602862087,0.0010199632250395,0.0000190482922570,-0.0011711072134873,-0.0024345821147888,-0.0036284987472194,-0.0045978697208798,-0.0051937725260009,-0.0052928489822895,-0.0048157655121297,-0.0037421830301847,-0.0021200045223263,-0.0000671897708197,0.0022347930844576,0.0045558495491448,0.0066394125108034,0.0082298250869960,0.0091021062349281,0.0090907379538161,0.0081139702738372,0.0061904123965458,0.0034453596342308,0.0001053400669057,-0.0035193504333161,-0.0070629599518906,-0.0101409269984312,-0.0123912150958553,-0.0135159141893970,-0.0133183847721507,-0.0117314514765957,-0.0088329189234603,-0.0048459221009567,-0.0001232158250872,0.0048837149938801,0.0096680778553808,0.0137195341098129,0.0165789638233212,0.0178903118599852,0.0174436522975187,0.0152043335317103,0.0113244013729469,0.0061343135538643,0.0001150678565127,-0.0061469611180756,-0.0120181883656648,-0.0168831175207452,-0.0202099218564704,-0.0216092075118743,-0.0208793906867327,-0.0180333949515915,-0.0133032695303891,-0.0071216486525055,-0.0000814675096998,0.0071222518254061,0.0137626997041129,0.0191563866117297,0.0227351202843601,0.0241071081284681,0.0231004857065963,0.0197843647677042,0.0144648478539781,0.0076561189341496,0.0000294013994965,-0.0076550186151438,-0.0146256412959852,-0.0201792994512465,-0.0237539929269980,0.9750127221960708,-0.0237539929269980,-0.0201792994512465,-0.0146256412959852,-0.0076550186151438,0.0000294013994965,0.0076561189341496,0.0144648478539781,0.0197843647677042,0.0231004857065963,0.0241071081284681,0.0227351202843601,0.0191563866117297,0.0137626997041129,0.0071222518254061,-0.0000814675096998,-0.0071216486525055,-0.0133032695303891,-0.0180333949515915,-0.0208793906867327,-0.0216092075118743,-0.0202099218564704,-0.0168831175207452,-0.0120181883656648,-0.0061469611180756,0.0001150678565127,0.0061343135538643,0.0113244013729469,0.0152043335317103,0.0174436522975187,0.0178903118599852,0.0165789638233212,0.0137195341098129,0.0096680778553808,0.0048837149938801,-0.0001232158250872,-0.0048459221009567,-0.0088329189234603,-0.0117314514765957,-0.0133183847721507,-0.0135159141893970,-0.0123912150958553,-0.0101409269984312,-0.0070629599518906,-0.0035193504333161,0.0001053400669057,0.0034453596342308,0.0061904123965458,0.0081139702738372,0.0090907379538161,0.0091021062349281,0.0082298250869960,0.0066394125108034,0.0045558495491448,0.0022347930844576,-0.0000671897708197,-0.0021200045223263,-0.0037421830301847,-0.0048157655121297,-0.0052928489822895,-0.0051937725260009,-0.0045978697208798,-0.0036284987472194,-0.0024345821147888,-0.0011711072134873,0.0000190482922570,0.0010199632250395,0.0017527602862087,0.0021801999890144,0.0023057764653131,0.0021679805132536,0.0018309281895384,0.0013728937238789,0.0008744059311998,0.0004074693745616,0.0000271842699248,-0.0002333866931914,-0.0003647560591893,-0.0003791090967164,-0.0003055590399293,-0.0001834867559374,-0.0000550716758918,0.0000418604532548,0.0000796811308439,0.0000447598151640,-0.0000611735162575,-0.0002221300434536,-0.0004111061172667,-0.0005948664932619,-0.0007394830559167,-0.0008157836216176,-0.0008039239652070,-0.0006964600877342,-0.0004995386970487,-0.0002321048737491,0.0000766934492105,0.0003914824095416,0.0006754260236328,0.0008950989702105,0.0010248032141060,0.0010497657664069};

Passe-bande BP[50-100Hz], fe=1khz,

#define SIZEFILTER_RIF 101 
const double CoeffB[SIZEFILTER_RIF] = {-0.00113754135600039,0.00145573551019635,0.00204757132637837,0.00263156294182860,0.00271333596419554,0.00209181864289657,0.000931323096882042,-0.000295253526426151,-0.00102072553574198,-0.000889774336090301,4.43083314764723e-06,0.00107786715387792,0.00148437917329484,0.000533571368047096,-0.00188920791601188,-0.00513244322430680,-0.00796026431933224,-0.00907718729044520,-0.00777797980615596,-0.00439743526773444,-0.000287855091834307,0.00274023252764250,0.00330580413329246,0.00125505519932939,-0.00206543063390858,-0.00429236660220396,-0.00315158375829297,0.00229242971458052,0.0108761998865694,0.0195399514396670,0.0245421070503854,0.0232190058461234,0.0154734208240917,0.00418954936970178,-0.00580409548159399,-0.00992700646926349,-0.00626756928856083,0.00294530495786515,0.0117123531725929,0.0125971807940101,0.000217527153081935,-0.0255798746481075,-0.0583939852764639,-0.0866769663502052,-0.0976561065793289,-0.0823880423296246,-0.0399479214643191,0.0211017548056543,0.0847783033982685,0.132738261013397,0.150547020990727,0.132738261013397,0.0847783033982685,0.0211017548056543,-0.0399479214643191,-0.0823880423296246,-0.0976561065793289,-0.0866769663502052,-0.0583939852764639,-0.0255798746481075,0.000217527153081935,0.0125971807940101,0.0117123531725929,0.00294530495786515,-0.00626756928856083,-0.00992700646926349,-0.00580409548159399,0.00418954936970178,0.0154734208240917,0.0232190058461234,0.0245421070503854,0.0195399514396670,0.0108761998865694,0.00229242971458052,-0.00315158375829297,-0.00429236660220396,-0.00206543063390858,0.00125505519932939,0.00330580413329246,0.00274023252764250,-0.000287855091834307,-0.00439743526773444,-0.00777797980615596,-0.00907718729044520,-0.00796026431933224,-0.00513244322430680,-0.00188920791601188,0.000533571368047096,0.00148437917329484,0.00107786715387792,4.43083314764723e-06,-0.000889774336090301,-0.00102072553574198,-0.000295253526426151,0.000931323096882042,0.00209181864289657,0.00271333596419554,0.00263156294182860,0.00204757132637837,0.00145573551019635,-0.00113754135600039};
tptns2016.txt · Dernière modification : 2021/02/19 21:20 de 127.0.0.1