===== Planning TPs 2017 ====== - 4h TP: Codage de l'équation de récurrence générale dans filtreUnEchantillon(). calculReponseFiltreEnLigne() fournie ? Vérification sur des réponses connues / fournies ? - 2h TP: Ajout des convertisseurs fournis et codage de la correction d'offset (conforme au schéma P10/11 du cours). Ajout de la saturation ? - 8h TP : passage sur cible - configuration des périphériques - réutilisation du calcul de l'equation de récurrence telle quelle si possible - mesure des perf - réalisations de filtres sympas ==== TP1 Codage du calcul de l'équation de récurrence ==== Pour réaliser un filtre, il faut que le processeur effectue le calcul de l'équation de récurrence échantillon par échantillon. Le premier objectif est donc de réaliser le calcul de cette équation de récurrence. Pour l'instant, vous simulez le filtre grâce aux étapes suivantes: - définition du filtre (coefficients stockés dans des tableaux, initialisation d'un sFiltre avec initFiltre() ) - définition des signaux (création de tableaux pour l'impulsion, l'échelon, sinus...) pour une durée fixée - calcul de la réponse du filtre (avec calculReponseFiltre() ) - affichage des signaux d'entrée et de sortie - fin du programme Lors de la réalisation concrète du filtre, le programme doit assurer les tâches suivantes: - définition du filtre - acquisition d'un échantillon ek en effectuant une conversion analogique->numérique - calcul de sk en appliquant l'équation de récurrence sur cet échantillon - restituer sk en effectuant une conversion numérique->analogique - recommencer indéfiniment en 2. jusqu'à l'arrêt du système ! Pour arriver au test sur la cible il faut donc modifier progressivement les fonctions utilisées jusqu'à présent. Cela commence par remplacer l'appel à calculReponseFiltre(sFiltre * pFiltre, double * ek, double * sk, int nbPts) par une nouvelle fonction calculReponseFiltreEnLigne() void calculReponseFiltreEnLigne(sFiltre * pFiltre, double * ek, double * sk, int nbEch) { initFiltreUnEchantillon(pFiltre); for (int k=0; k < nbEch; k++) { sk[k] = filtreUnEchantillon(ek[k]); } } Si on veut pouvoir réaliser des filtres différents et les chainer (egaliseurs, visu puissance) il ne faut pas utiliser des variables globales pour le calcul du filtre. On crée une structure buffer à côté ? Vous pouvez constater en lisant le code que le tableau sk est rempli echantillon par échantillon. Pour l'instant ce code simule l'écoulement du temps en appelant filtreUnEchantillon qui calcule l'équation de récurence (je préfèrerais calcEquationRecurrence() qui est plus explicite) pour tous les échantillons $k\in[0,nbEch-1]$. Il vous reste à coder filtreUnEchantillon(double ek, sFiltre *pFiltre,sBuffer *pbuffer) qui est le coeur du filtre numérique.(On doit sans doute ajouter une explication détaillée de la structure buffer en début de séance ou au début du TP ?) Vous devez coder la forme canonique du filtre utilisant un seul buffer. =====Travail à effectuer===== Compiler et exécuter le projet. - 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). - Dans le fichier **filtre.cpp**, implémenter la fonction filtreUnEchantillon qui applique l'équation de récurrence pour un échantillon du signal comme décrit en TD. - Vérifier l'exactitude des réponses impulsionnelles et indicielles pour le filtre fourni jusque $k=6$. - Bonus: appliquer ce filtre à deux sinusoides de fréquence 50 Hz et 500 Hz. Expliquez le résultat. - Déclarer les variables nécessaires pour ajouter un second filtre d'équation de récurrence: $ s_k=e_k+0.3s_{k-7} $ - Calculer les réponses impulsionnelles et indicielles 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 1 Architecture pour le TNS: Mise en oeuvre sur PC avec type double===== sujet TD1 architecture pour le TNS {{https://bvdp.inetdoc.net/files/iut/tp_tns/TDcalculVfixeV2.doc}} cours architecture pour le TNS {{https://bvdp.inetdoc.net/files/iut/tp_tns/cours_Archi_TNS_intro5_6ppf.pdf}} ==== 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 !" Solution: ///////////////////////////////////////////////////////////////// const int OFFSET_ENTREE=0; const int OFFSET_SORTIE=0; const int VALEUR_MAX=10000; const int VALEUR_MIN=-10000; int NB_COEFF_B; int NB_COEFF_A; int MEMORYSIZE; double *b; //Coefficients du numérateur de la fonction de transfert du filtre double *a; //Coefficients du dénominateur de la fonction de transfert du filtre //le premier élement est a1 //le tableau denCoeffs est de taille 1 pour un RIF double *memoireVk; //buffer rotatif pour stocker les valeurs de vk int indice_ecr; //indice d'écriture dans le buffer rotatif memoireVk ///////////////////////////////////////////////////////////////// void initFiltreUnEchantillon(sFiltre * pFiltre){ int i; //met à jour les variables globales NB_COEFF_B= pFiltre->nbCoeffB; b= pFiltre->pCoeffB; NB_COEFF_A= pFiltre->nbCoeffA; a= pFiltre->pCoeffA; MEMORYSIZE=pFiltre->nbMemoireVk; memoireVk=pFiltre->memoireVk; for (i=0;i=MEMORYSIZE) indice_ecr=0; if (skout > VALEUR_MAX ) // gestion des saturations du résultat skout = VALEUR_MAX ; else if (skout < VALEUR_MIN ) skout = VALEUR_MIN ; //ajout offset numérique return skout + OFFSET_SORTIE; } ///////////////////////////////////////////////////////////////// void calculReponseFiltreEnLigne(sFiltre * pFiltre, double * ek, double * sk, int nbPts) { initFiltreUnEchantillon(pFiltre); for (int k=0; k < nbPts; k++) { sk[k] = filtreUnEchantillon(ek[k]); } } ///////////////////////////////////////////////////////////////// =====Travail à effectuer===== Compiler et exécuter le projet. - 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). - Dans le fichier **filtre.cpp**, implémenter les fonctions: - filtreUnEchantillon qui applique l'équation de récurrence pour un échantillon du signal comme décrit en TD. - 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. - Vérifier l'exactitude des réponses temporelles pour le filtre fourni jusque $k=6$. - Déclarer les variables nécessaires pour ajouter un second filtre d'équation de récurrence: $ s_k=e_k+0.3s_{k-7} $ - 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: - **simuADC** dont le rôle est de convertir un nombre représentant une tension en une valeur numérique quantifiée. - **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**. {{https://bvdp.inetdoc.net/files/iut/tp_tns/quantif1.png}} 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**: - appeler **simuADC** et stocker sa valeur de retour dans une variable du bon type. - appeler **filtreUnEchantillon** en lui fournissant en paramètre la variable précédemment calculée (qui sera convertie automatiquement en double). - 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: - **OFFSET_ENTREE** et **OFFSET_SORTIE** doivent être réglées en fonction de **nbBitsQuantDAC** et **nbBitsQuantADC**. - **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. Solution: const int OFFSET_ENTREE=(1<<(nbBitsQuantADC-1)); const int OFFSET_SORTIE=(1<<(nbBitsQuantDAC-1)); const int VALEUR_MAX=(1<<15)-1 - OFFSET_SORTIE; const int VALEUR_MIN=-(1<<15) - OFFSET_SORTIE; for (int k=0; k < nbPts; k++){ unsigned short int eki= simuADC( ek[k], (1< ===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: - un tableau **tabDouble** des coefficients à convertir. - un tableau **tabInt** pour ranger les coefficients convertis en virgule fixe. - 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. - un nombre de coefficients **nbCoeffs** indiquant le nombre de case du tableau **tabDouble** à traiter. - 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**. void quantCoeffsShortInt(double * tabDouble, short int * tabInt, double * tabDoubleQuant, int nbCoeffs, unsigned int nbBitsFractionnaire){ int i; long int temp; for (i=0;i( (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< ===Solution:=== temp= floor( (tabDouble[i] * (1< 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: - exacte - approchée correctement (par une valeur dont l'erreur inférieure ou égale au pas de quantification /2 du format en virgule fixe.) - non correcte (à cause des saturations) ===Solution:=== double val[]={ 0, 0.999, 0.0001, 1, 2, -1, 0.5, -0.5}; double valq[8]; short int vali[8]; quantCoeffsShortInt(val, vali,valq, 8, 15); quantCoeffsShortInt(val, vali,valq, 8, 12); ====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=== - Pour les RIF, le tableau **memoireVk** contient les échantillons d'entrée $\hat{ek}$ = $ek$ - OFFSET_ENTREE, interprété au format Qn.0. - On considère que les coefficients du numérateur de la fonction de transfert (b[i]) sont codés en Q0.15. - 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. {{https://bvdp.inetdoc.net/files/iut/tp_tns/quantif2.png}} ===Solution:=== double skout; //valeur pour la sortie calculée //calcul de sk skout = 0; //valeur par défaut pour le résultat indice_lec = indice_ecr; for (i = 0; i short int skout = 0; //valeur pour la sortie calculée long int temp; // calcul intermediaire sur 32 bits //calcul de sk temp = 0; //valeur par défaut pour le résultat indice_lec = indice_ecr; for (i = 0; i > NB_BITS_FRACTIONNAIRE ) + 1; //oui alors ajouter 1 au résultat tronqué else skout = (temp >> NB_BITS_FRACTIONNAIRE ) ; //sinon utiliser le résultat tronqué if (skout > (1<<(nbBitsQuantDAC-1)) -1 ) // saturation du résultat sur 10 bits skout = (1<<(nbBitsQuantDAC-1)) -1 ; else if (skout < -(1<<(nbBitsQuantDAC-1)) ) skout = -(1<<(nbBitsQuantDAC-1))) ; return skout + OFFSET_SORTIE; SOLUTION 2015 qui ne respecte pas les noms inline short int execFiltre_i_filtre_rif(short int e) { long int temp; // calcul intermediaire sur 32 bits int i; //indice de lecture pour les valeurs des coefficients du filtre int indice_lec_i_filtre_rif = indice_ecr_i_filtre_rif; //indice de lecture pour les échantillons d'entrée short int skout = 0; //valeur pour la sortie calculée ek_filtre_i_RIF[indice_ecr_i_filtre_rif] = e - 512; //rangement de l'echantillon d'entrée dans le buffer circulaire des entrées en référencant par rapport au 0 // indice_ecr_i_filtre_rif = (indice_ecr_i_filtre_rif + 1) % SIZEFILTER_RIF; indice_ecr_i_filtre_rif = (indice_ecr_i_filtre_rif + 1); if (indice_ecr_i_filtre_rif>=SIZEFILTER_RIF) indice_ecr_i_filtre_rif=0; temp = 0; //valeur par défaut pour le résultat for (i = 0; i < SIZEFILTER_RIF; i++) { temp += (long int) numCoeff_i_FILTRE_RIF[i] * (long int)(ek_filtre_i_RIF[indice_lec_i_filtre_rif]); // calcul sur 32 bits indice_lec_i_filtre_rif--; if (indice_lec_i_filtre_rif < 0) indice_lec_i_filtre_rif = SIZEFILTER_RIF - 1; } if (temp & (1<<(NB_BITS_FRACTIONNAIRE-1)) ) // calcul de l'arrondi skout = (temp >> NB_BITS_FRACTIONNAIRE ) + 1; else skout = (temp >> NB_BITS_FRACTIONNAIRE ) ; if (skout > 511 ) // saturation du résultat sur 10 bits skout = 511 ; else if (skout < -512 ) skout = -512 ; return skout + 512; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 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: 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 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. 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: {{https://bvdp.inetdoc.net/files/iut/tp_tns/rejecteur.png}} 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. fournir fonction qui genere signal avec 3 sinus 40, 50 et 60Hz +chirp ===Solution:=== //////////////////////////////////////////////////////////////////////////////////////////////////// void GenereEchantillonsSinus(double * tabech , int nbEchantillons, double Amplitude, double Offset, double Frequence, double freqEch) { long long int i; for (i=0;i =====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 : - Un arduino Uno R3 - Un "shield" DAC TLV5637 Dans ce TP nous allons mettre en place l'architecture qui permet de : - Acquérir un signal analogique sur la voie 0 au rythme de 2kHz - Appliquer l'équation de récurrence du filtre RIF en point fixe - 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 #include 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 : - configurer la conversion sur le canal 0 avec une fréquence ADC de 125kHz - Démarrer une conversion sur le canal 0 - 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. {{https://bvdp.inetdoc.net/files/iut/tp_dacspi/TPCOMDACSPI.png}} === 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 === - Mesurez $T_{ech}$. Est-elle conforme à ce que vous avez configuré ? - Mesurez le temps passé dans le sous-programme d'interruption. Est-il cohérent avec le réglage choisi pour l'ADC ? - 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}$ : 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. void quantCoeffsShortInt(double * tabDouble, short int * tabInt, int nbCoeffs, unsigned int nbBitsFractionnaire){ int i; long int temp; for (i=0;i( (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); } } - 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. 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 =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=== - 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. - Mesurez la bande passante à -3dB ===Analyse des performances=== - 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. - 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 ? - 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}$ - Refaites ces mesures pour le filtre passe-bas à 11 coefficients ci-dessous. - 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: {{https://bvdp.inetdoc.net/files/iut/tp_tns/capture_fdatool_rejecteur.png}} #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};