=====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 ==== * Prise en main de l'environnement de développement QT * Génération d'échantillons pour différents signaux * Utilisation des structure de contrôle et de données pour le filtrage * Test sur des filtres RIF simples ====Outils==== ===Qtcreator=== Qtcreator est un outils de développement présenté durant le cours de Programmation Orienté Objet. Nous allons l'utiliser pour développer des filtres et les tester sur PC. Vous devrez développer une application à partir d'une structure de programme capable de visualiser des signaux temporels. ===Récupération du projet=== Notes pour Hugues, en cas de problème, récupérer le fichier https://bvdp.inetdoc.net/files/iut/tp_tns/TNSQTold.zip Pour récupérez le fichier zip du projet la première fois, ouvrir une console (alt+F2 et taper lxterm) puis copier coller dedans: wget https://bvdp.inetdoc.net/files/iut/tp_tns/TNSQT.zip unzip TNSQT.zip cd TNSQT rm TNSQT.pro.user qmake TNSQT.pro make clean qtcreator TNSQT.pro & echo "fini" **IMPORTANT: A l'ouverture du projet, cocher Debug et décocher Release pour avoir exactement les mêmes réglages que sur l'image suivante, en remplacant bertrand.vandeportae par votre login. Veillez à bien remplir les noms des dossiers!!!** {{https://bvdp.inetdoc.net/files/iut/tp_tns/configprojet.png}} Au cas ou vous vous seriez trompé, fermer qtcreator et tapez dans la console: rm TNSQT.pro.user qtcreator TNSQT.pro & Une fois les réglages effectués, cliquer sur Configurer le projet Pour ouvrir le projet les fois suivantes , vous ouvrirez ce projet en ouvrant une console et en tapant juste cd TNSQT qtcreator TNSQT.pro & Compilez et exécutez le programme en tapant **CTRL+R**, vous devez voir s'ouvrir une fenêtre sur laquelle on peut zoomer et se déplacer à l'aide de la souris: {{https://bvdp.inetdoc.net/files/iut/tp_tns/applidebut.png}} ===Structure du projet=== Le projet est constitué des fichiers sources d'une application graphique sous QT. Votre code sera saisi dans l'implémentation de la fenêtre principale, à l'intérieur du fichier Sources->mainwindow.cpp. C'est le seul fichier que vous aurez à modifier jusqu'à nouvel ordre. Pour information le projet contient également: TNSQT.pro fichier décrivant le projet QT, les règles de compilation, les librairies à utiliser etc... mainwindow.h fichier décrivant la classe MainWindow utilisée pour la fenêtre principale de l'application qcustomplot.h et qcustomplot.cpp fichiers décrivant et implémentant les classes permettant le tracés de courbes dans une fenêtre main.cpp fichier décrivant le programme principal, qui se contente de lancer la fenêtre principale. mainwindow.ui fichier contenant la description de l'interface graphique (minimaliste) de l'application ===Notice d'utilisation de l'outils QTCreator=== Une fois le projet ouvert, l'utilisateur peut choisir entre plusieurs mode d'affichages, notamment "Editer" et "Debogage" à l'aide des boutons sur la gauche de la fenêtre. La fenêtre Projets fait apparaître les fichiers du (des) projet(s) ouvert(s), triés selon leur type. Sur la droite, la fenêtre affiche le contenu d'un fichier sélectionné. **CTRL+Click souris gauche** sur un mot du programme permet de se déplacer à la déclaration d'une variable, d'un objet, d'une fonction, d'un type ou autre. Cela sera très utile pour connaître par exemple les méthodes utilisables sur un objet d'une certaine classe. **CTRL+B** permet de compiler l'application, la fenêtre Sortie de compilation permet alors d'observer les éventuelles erreurs, sur lesquelles vous pourrez cliquer pour aller à la ligne correspondante dans le code. **CTRL+R** permet d’exécuter le programme. Le programme devra être fermé en cliquant sur le bouton croix de la fenêtre avant de le recompiler ou de l’exécuter à nouveau. **F5** permet d'exécuter le programme en mode DEBUG. L'utilisateur passe alors en mode Debug, ce qui ajoute à l'affichage une zone à droite dans laquelle l'utilisateur pourra observer les variables et leurs valeurs. L'utilisateur pourra placer ou retirer des points d'arrêt en cliquant à droite du numéro de ligne d'un fichier. Il pourra exécuter en pas à pas sans rentrer dans les fonctions à l'aide de **F10** ou bien en entrant dans les fonctions à l'aide de **F11**. Il pourra ajouter une variable particulière à la liste des variables affichées en Debug en selectionant le nom de la variable dans le fichier programme et en cliquant droit dessus puis clic gauche sur **ajouter un évaluateur d'expression**. =====Travail à réaliser===== ====Écriture des fonctions de génération de signaux pour le test==== Dans la démarche de développement proposée, il est nécessaire d'avoir des échantillons d'entrées pour "alimenter le filtre". Dans cet exercice, vous devrez définir et remplir des tableaux contenant des échantillons de différents signaux. Nous utiliserons le type double, permettant de manipuler des nombres flottants en double précision (64bits) afin de minimiser les problèmes liés au codage numérique. Les différentes fonctions à implémenter dans la suite du TP doivent remplir le tableau passé en paramètre via le pointeur ptr_tabech avec nbEchantillons échantillons et __devront être testées une par une__. Nous utiliserons les déclarations suivantes que vous devez ajouter à votre programme: #define SIZEDONNEE 1000 double entree_d[SIZEDONNEE]; //pour stocker les échantillons d'entrée double sortie_d[SIZEDONNEE]; //pour stocker les échantillons de sortie double date_d[SIZEDONNEE]; //pour stocker les dates associées à chaque échantillons (en numéro ou en seconde) **Les appels aux différentes fonctions devront être commentés et non remplacés au fur et à mesure du développement pour permettre leur réutilisation facile dans les TP suivants: DONC TOUT GARDER EN COMMENTAIRE!!!!** ===Signal Zéro=== Implémenter la fonction suivante: void GenereEchantillonsZero(double * ptr_tabech , int nbEchantillons) afin de remplir le tableau avec tous les échantillons à 0. Appeler cette fonction dans la méthode MainWindow::Filtrage à l'emplacement indiqué "génération du signal de sortie" pour remplir le tableau sortie_d. Pour le test, il faut modifier la partie notée "gestion de la recopie des valeurs" en remplaçant la ligne suivante qui donne une valeur par défaut au signal de sortie affiché: st[i]=-i; par st[i]=sortie_d[i]; ===Signal Rampe=== Implémenter la fonction suivante: void GenereEchantillonsNumero(double * ptr_tabech , int nbEchantillons, double Increment) afin de remplir le tableau avec dans chaque case la valeur correspondant à son indice multiplié par incrément. Cette fonction sera notamment utilisée pour dater les échantillons, en associant à chacun soit un numéro (incrément = 1) , soit une date en unité de temps (incrément = période d'échantillonnage). Appeler cette fonction dans la méthode MainWindow::Filtrage à l'emplacement indiqué "génération du signal de datation des échantillons" avec une valeur Increment=1 pour remplir le tableau date_d. Pour le test, il faut modifier l'affectation par défaut du signal utilisé pour légender l'axe x: t[i]=date_d[i]; ===Signal Impulsion=== Implémenter la fonction suivante: void GenereEchantillonsImpulsion(double * ptr_tabech , int nbEchantillons, double Amplitude) afin de remplir le tableau avec un premier échantillon à la valeur Amplitude et tous les suivants à 0. Appeler cette fonction dans la méthode MainWindow::Filtrage à l'emplacement indiqué "génération du signal d'entrée" pour remplir le tableau entree_d. Pour le test, il faut modifier l'affectation du signal de sortie affiché: et[i]=entree_d[i]; ===Signal Indice=== Implémenter la fonction suivante: void GenereEchantillonsIndice(double * ptr_tabech , int nbEchantillons, double Amplitude) afin de remplir le tableau avec tous les échantillons à Amplitude. Appeler cette fonction dans la méthode MainWindow::Filtrage à l'emplacement indiqué "génération du signal d'entrée" pour remplir le tableau entree_d (donc mettre en commentaire l'appel précédent) et exécuter le programme pour observer le signal temporel généré en vert. ===Signal Sinusoïdal=== Implémenter la fonction suivante: void GenereEchantillonsSinus(double * tabech , int nbEchantillons, double Amplitude, double Offset, double Frequence, double freqEch) afin de remplir le tableau avec des échantillons d'un signal sinusoïdal paramétré par Amplitude et Offset. Ce signal doit être de fréquence Frequence et échantillonné à une fréquence freqEch. Appeler cette fonction dans la méthode MainWindow::Filtrage à l'emplacement indiqué "génération du signal d'entrée" pour remplir le tableau entree_d et exécuter le programme pour observer le signal temporel généré en vert. ====Mise en oeuvre de filtres simples==== ===Structure imposée pour chaque filtre=== Chaque filtre sera implémenté tout d'abord en utilisant le type flottant double précision à l'aide de deux fonctions nommées en fonction du filtre: void initFiltre_d_nomdufiltre(); //initialise les variables nécessaires au bon fonctionnement du filtre si besoin et double execFiltre_d_nomdufiltre(double e); //application de l'équation de récurrence. e est l'échantillon d'entrée et la fonction renvoie l'échantillon de sortie. La fonction execFiltre_d_nomdufiltre devra gérer en interne les éventuelles structures de données nécessaires à son bon fonctionnement. **TOUTES Les variables globales utilisées pour le fonctionnement du filtre devront être nommées spécifiquement en fonction du nom du filtre et on indiquera avec _d_ si le type est double (pour virgule flottante) ou _i_ si le type est int (pour virgule fixe) ** Chaque filtre devra respecter la structure de cet exemple: ===Exemple de filtre=== //////////////////////////////////////////////////////////////////////////////////// //Nom du filtre: filtre_exemple //Description du filtre: dire ce que fait le filtre //////////////////////////////////////////////////////////////////////////////////// //Partie commune aux implémentations en virgule flottante et fixe: //////////////////////////////////////////////////////////////////////////////////// #DEFINE SIZENUM_FILTRE_EXEMPLE 5 //////////////////////////////////////////////////////////////////////////////////// //Implémentation en virgule flottante double précision: //////////////////////////////////////////////////////////////////////////////////// //mettre ici toutes les constantes et variables définies en double, en indiquant le type dans le nom avec _d_ double numCoeff_d_FILTRE_EXEMPLE[SIZENUM_FILTRE_EXEMPLE]; //Coefficients du numérateur de la fonction de transfert du filtre au format double double ek_d_filtre_exemple[SIZENUM_FILTRE_EXEMPLE]; //tableau pour stocker les échantillons d'entrée au format double int indice_ecr_d_filtre_exemple; void initFiltre_d_filtre_exemple() { //à vous de remplir } double execFiltre_d_filtre_exemple(double e) { //à vous de remplir } //////////////////////////////////////////////////////////////////////////////////// //Implémentation en virgule fixe: //////////////////////////////////////////////////////////////////////////////////// //mettre ici toutes les constantes et variables définies en int, en indiquant le type dans le nom avec _i_ short int numCoeff_i_FILTRE_EXEMPLE[SIZENUM_FILTRE_EXEMPLE]; //Coefficients du numérateur de la fonction de transfert du filtre au format virgule fixe short int ek_filtre_i_exemple[SIZENUM_FILTRE_EXEMPLE]; //tableau pour stocker les échantillons d'entrée au format virgule fixe int indice_ecr_i_filtre_exemple; void initFiltre_i_filtre_exemple() { //à vous de remplir } short int execFiltre_i_filtre_exemple(short int e) { //à vous de remplir } //////////////////////////////////////////////////////////////////////////////////// ===Filtres RIF=== Commencez par implémenter les filtres RIF simples qui suivent. Observer la réponse impulsionnelle des filtres pour vérifier les résultats. ==Filtre recopie== Équation du filtre: s(k)=e(k) Pour ce filtre, la fonction execFiltre_d_filtre_recopie doit renvoyer la valeur de l'échantillon fournit en argument. ==Filtre retard fixe et gain== Équation du filtre: s(k)=A.e(k-r) La valeur de A est fournie à la compilation à l'aide de la macro: #define A_d_FILTRE_RETARDETGAIN 0.8 Pour ce filtre on considérera que r est dans un premier temps fixé à 5. Il vous incombe de dimensionner le buffer circulaire de manière adéquate. La mise en œuvre du buffer circulaire devra être réalisée comme indiquée en cours (page 32 de cours architecture pour le TNS:https://bvdp.inetdoc.net/files/iut/tp_tns/cours_Archi_TNS_intro5_6ppf.pdf ), à l'aide d'une variable servant d'indice d'écriture qui est incrémentée à chaque nouvelle écriture, et remise à zéro si la fin du buffer est dépassée. La lecture des valeurs du buffer doit se faire à partir de l'indice d'écriture, en utilisant un indice de lecture que l'on décrémente pour remonter le temps, en gérant également le fait que le buffer est circulaire. Dans la fonction d'initialisation du filtre, vous veillerez à bien initialiser le contenu du buffer circulaire pour les échantillons d'entrée à zéro afin de pouvoir générer correctement les réponses impulsionnelle et indicielle. ==Filtre retard réglable et gain== Équation du filtre: s(k)=A.e(k-r) Adapter le code précédent pour tenir compte d'une valeur quelconque de r fournie à la compilation via la macro #define R_d_FILTRE_RETARDETGAIN 500 ==Filtre écho== Équation du filtre: s(k)=A.e(k) + B.e(k-r) Les valeurs de A et B et r sont fournies à la compilation à l'aide des macros: #define A_d_FILTRE_ECHO 0.8 #define B_d_FILTRE_ECHO 0.5 #define R_d_FILTRE_ECHO 500 ===Filtres RII=== ==Filtre Feedback delai== Équation du filtre: s(k)=e(k) + (A-B).e(k-r) + B.s(k-r) Implémenter ce filtre pour une valeur quelconque de A,B et r fournie à la compilation via la macro #define A_d_FILTRE_FEEDBACKDELAY 0.8 #define B_d_FILTRE_FEEDBACKDELAY 0.5 #define R_d_FILTRE_FEEDBACKDELAY 500 Le filtre sera réalisé en utilisant la structure canonique de 2nde forme, et il vous incombe donc de stocker dans le buffer circulaire les valeurs v(k). ==Filtre général== Équation générale du filtre: s(k)=.... Les coefficients sont stockés dans 2 tableaux dont la taille sera réglée par les macros suivantes: #define SIZENUM_d_NOMDUFILTRE 5 #define SIZEDEN_d_NOMDUFILTRE 6 #define MAXSIZE_d_NOMDUFILTRE (SIZENUM_d_NOMDUFILTRE>SIZEDEN_d_NOMDUFILTRE? SIZENUM_d_NOMDUFILTRE : SIZEDEN_d_NOMDUFILTRE) double numCoeff_d_NOMDUFILTRE[SIZENUM_NOMDUFILTRE]; //Coefficients du numérateur de la fonction de transfert du filtre double denCoeff_d_NOMDUFILTRE[SIZEDEN_NOMDUFILTRE]; //Coefficients du dénominateur de la fonction de transfert du filtre Coefficients pour un filtre RIF Passe-Bas fc(-3dB)=1300 Hz si Fech=10 kHz : #define SIZEFILTER 11 const double num[SIZEFILTER] = {-0.0349249458233189,-0.0370641891927234,0.0209236117413746,0.1368055347416942,0.2562279031324722,0.3071029997932009,0.2562279031324722,0.1368055347416942,0.0209236117413746,-0.0370641891927234,-0.0349249458233189}; =====TP 2 Architecture pour le TNS: Mise en oeuvre sur PC avec calculs en virgule fixe===== Pour cette partie du TP, vous devez réaliser, à coté des implémentations de filtre en virgule flottante, des implémentations en virgule fixe. Pour cela, vous implémenterez les fonctions: void initFiltre_i_nomdufiltre(); //initialise les variables nécessaires au bon fonctionnement du filtre si besoin et short int execFiltre_i_nomdufiltre(short int e); //application de l'équation de récurrence. e est l'échantillon d'entrée et la fonction renvoie l'échantillon de sortie. ====Calcul des échantillons d'entrées==== Tout d'abord, il faut générer des échantillons d'entrée quantifiés sur des valeurs entières et simuler la quantification et la saturation effectuées par l'ADC. Pour cela, copiez coller dans votre code la fonction suivante: void QuantifieEchantillons(double * tabin, short int * tabout,int nbBitsQuantADC, int nbEchantillons ) { for (int i=0;i(1< Il vous faut ensuite définir des tableaux permettant de gérer les signaux en échantillons codés sur des entiers: short int entree_i[SIZEDONNEE]; //pour stocker les échantillons d'entrée short int sortie_i[SIZEDONNEE]; //pour stocker les échantillons de sortie Le tableau d'entrée **entree_i** sera initialisé après que le tableau **entree_d** ait été rempli par une de vos fonctions en appelant: QuantifieEchantillons(entree_d, entree_i,10, SIZEDONNEE ); Ceci permet de simuler un ADC sur 10 bits et les échantillons seront donc codés par des valeurs entre **0 et 1023**. (pensez à générer des valeurs en entrée de **QuantifieEchantillons** qui soient aussi dans cet intervalle). ====Passage en virgule fixe des coefficients du filtre==== Dans la fonction initFiltre_i_nomdufiltre(), vous devrez convertir en virgule fixe les coefficients du filtre exprimés en double. Pour cela, vous commencerez par définir une macro indiquant le nombre de bits servant à coder la partie fractionnaire, en le réglant par défaut à 14: #define NB_BITS_FRACTIONNAIRE 14 Ensuite, vous devrez, pour chaque coefficient codé en double appliquer la conversion par arrondi. Le facteur multiplicatif (2 puissance NB_BITS_FRACTIONNAIRE) sera obtenu par (1< Solution: //permet de choisir le nombre de bits pour coder la partie fractionnaire #define NB_BITS_FRACTIONNAIRE 14 void initFiltre_i_nomdufiltre() { for (int i=0;i ====Codage de l'équation de récurrence en virgule fixe==== Il faut maintenant implémenter la fonction: short int execFiltre_i_nomdufiltre(short int e); Pour cela, vous vous baserez sur la version traitant des doubles. Les opérandes seront stockés sur 16 bits (type short int) et les résultats intermédiaires de multiplication-additions seront quand à eux gérés sur 32 bits (type long int) Vous veillerez: - à bien retrancher la valeur de la moitié du max en sortie de l'ADC pour avoir les échantillons d'entrée référencés autour de la valeur 0 et passer ainsi du format UQM.0 à Q(M-1).0. - à effectuer tous les calculs sur 32 bits (en "castant" correctement les opérandes en long int lors du calcul de l'échantillon de sortie en virgule fixe) - à interpréter correctement le résultat de l'échantillon de sortie en virgule fixe en QW.Y (pour rappel, les échantillons d'entrées sont considérés en Q(M-1).0 et les coefficients du filtre en QX.Y donc vous devez savoir calculer le résultat de l'application de l'équation de récurrence.) - à réaliser l'arrondi sur les valeurs en sortie de l'équation de récurrence pour passer de QW.Y à QW.0. Pour cela, comme indiqué dans le cours, il faut tester le bit de poids fort de la partie fractionnaire, et s'il est à 1, ajouter 1 au résultat. Ce bit est à la position (NB_BITS_FRACTIONNAIRE-1) dans le résultat. - à gérer les saturations sur les sorties en veillant à ce que les valeurs calculées ne dépassent pas les bornes pour passer du format QW.0 à QN.0. - à adapter la sortie pour utiliser l'intervalle acceptable pour la commande d'un DAC. Ceci se traduit par l'ajout d'une valeur permettant de passer du format QN.0 à UQ(N+1).0. Solution pour un RIF: //////////////////////////////////////////////////////////////////////////////////// //Nom du filtre: filtre_RIF //Description du filtre: Un filtre RIF général //////////////////////////////////////////////////////////////////////////////////// //Partie commune aux implémentations en virgule flottante et fixe: //////////////////////////////////////////////////////////////////////////////////// //passe ricrac pour un filtre avec 100 coefficients et à 2khz sur l'arduino en Virgule fixe #define SIZEFILTER_RIF 41 //////////////////////////////////////////////////////////////////////////////////// //Implémentation en virgule flottante double précision: //////////////////////////////////////////////////////////////////////////////////// //mettre ici toutes les constantes et variables définies en double, en indiquant le type dans le nom avec _d_ 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}; /*double numCoeff_d_FILTRE_EXEMPLE[SIZEFILTER_RIF]; //Coefficients du numérateur de la fonction de transfert du filtre au format double double ek_d_filtre_exemple[SIZEFILTER_RIF]; //tableau pour stocker les échantillons d'entrée au format double int indice_ecr_d_filtre_rif; void initFiltre_d_filtre_rif() {} double execFiltre_d_filtre_rif(double e) {} */ //////////////////////////////////////////////////////////////////////////////////// //Implémentation en virgule fixe: //////////////////////////////////////////////////////////////////////////////////// //mettre ici toutes les constantes et variables définies en int, en indiquant le type dans le nom avec _i_ short int numCoeff_i_FILTRE_RIF[SIZEFILTER_RIF]; //Coefficients du numérateur de la fonction de transfert du filtre au format virgule fixe short int ek_filtre_i_RIF[SIZEFILTER_RIF]; //tableau pour stocker les échantillons d'entrée au format virgule fixe int indice_ecr_i_filtre_rif; //pour gestion buffer circulaire #define NB_BITS_FRACTIONNAIRE 14 //////////////////////////////////////////////////////////////////////////////////// void initFiltre_i_filtre_rif() { int i; for (i = 0; i < SIZEFILTER_RIF; i++) { numCoeff_i_FILTRE_RIF[i] = floor( (numCoeff_d_FILTRE_RIF[i] * (1<< NB_BITS_FRACTIONNAIRE) )+ 0.5) ; ek_filtre_i_RIF[i]=0; } } //////////////////////////////////////////////////////////////////////////////////// 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; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ====Comparaison avec le filtre réalisé en virgule flottante==== Pour pouvoir visualiser les signaux quantifiés en virgule fixe et les comparer aux signaux en virgules flottantes, il va falloir modifier la méthode **void MainWindow::Filtrage**. Ajoutez les définitions suivantes: bool affichageVirguleFixe=TRUE; //mettre à TRUE pour un affichage des entrées et sorties quantifiées en virgule fixe QVector etvfixe(nbEchantillons),stvfixe(nbEchantillons); Ajoutez ensuite dans la boucle de recopie des échantillons l'affectation des valeurs aux cases du tableau. for (int i=0; i ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// // NE PAS MODIFIER JUSQU'A NOUVEL ORDRE!!!! ///////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////// // create graph and assign data to it: customPlot->addGraph(); customPlot->graph(0)->setPen(QPen(Qt::green)); customPlot->graph(0)->setData(t, et); // give the axes some labels: customPlot->xAxis->setLabel("t"); customPlot->yAxis->setLabel("valeur "); // set axes ranges, so we see all data: customPlot->xAxis->setRange(0, t[nbEchantillons-1]); customPlot->yAxis->setRange(0,MaxQuantADC); customPlot->addGraph(); customPlot->graph(1)->setPen(QPen(Qt::red)); customPlot->graph(1)->setData(t, st); if (affichageVirguleFixe) { customPlot->addGraph(); customPlot->graph(2)->setPen(QPen(Qt::black)); customPlot->graph(2)->setData(t, etvfixe); customPlot->addGraph(); customPlot->graph(3)->setPen(QPen(Qt::blue)); customPlot->graph(3)->setData(t, stvfixe); } QColor c(128,128,128); QBrush b(c); customPlot->setBackground(b); if (affichageTemporel==FALSE) { customPlot->graph(0)->setLineStyle(QCPGraph::lsImpulse); customPlot->graph(1)->setLineStyle(QCPGraph::lsImpulse); customPlot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle:: ssCircle, 5)); customPlot->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle:: ssCross, 5)); if (affichageVirguleFixe) { customPlot->graph(2)->setLineStyle(QCPGraph::lsImpulse); customPlot->graph(3)->setLineStyle(QCPGraph::lsImpulse); customPlot->graph(2)->setScatterStyle(QCPScatterStyle(QCPScatterStyle:: ssSquare, 5)); customPlot->graph(3)->setScatterStyle(QCPScatterStyle(QCPScatterStyle:: ssPlus, 5)); } } else { customPlot->graph(0)->setLineStyle(QCPGraph::lsStepLeft); customPlot->graph(1)->setLineStyle(QCPGraph::lsStepLeft); if (affichageVirguleFixe) { customPlot->graph(2)->setLineStyle(QCPGraph::lsStepLeft); customPlot->graph(3)->setLineStyle(QCPGraph::lsStepLeft); } } customPlot->legend->setVisible(true); customPlot->legend->setFont(QFont("Helvetica",9)); // set locale to english, so we get english decimal separator: customPlot->setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom)); customPlot->graph(0)->setName("e(t)"); customPlot->graph(1)->setName("s(t)"); if (affichageVirguleFixe) { customPlot->graph(2)->setName("e(t) vfixe"); customPlot->graph(3)->setName("s(t) vfixe"); } // configure right and top axis to show ticks but no labels: // (see QCPAxisRect::setupFullAxesBox for a quicker method to do this) customPlot->xAxis2->setVisible(true); customPlot->xAxis2->setTickLabels(false); customPlot->yAxis2->setVisible(true); customPlot->yAxis2->setTickLabels(false); // make left and bottom axes always transfer their ranges to right and top axes: connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis2, SLOT(setRange(QCPRange))); connect(customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->yAxis2, SLOT(setRange(QCPRange))); // let the ranges scale themselves so graph 0 fits perfectly in the visible area: customPlot->graph(0)->rescaleAxes(); // same thing for graph 1, but only enlarge ranges (in case graph 1 is smaller than graph 0): customPlot->graph(1)->rescaleAxes(true); if (affichageVirguleFixe) { customPlot->graph(2)->rescaleAxes(true); customPlot->graph(3)->rescaleAxes(true); } // Note: we could have also just called customPlot->rescaleAxes(); instead // Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking: customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); } /////////////////////////////////////////////////////////////////////// =====TP 3 Architecture pour le TNS: Mise en oeuvre sur PC Application aux signaux Audionumériques===== TODO: CABLER et numéroter casques audio de bruno de quoi lire les wav en ligne de commande cvlc --play-and-exit nom.wav 1° installation audio et observation des filtres précédemment codés sur des fichiers wav présentation de la librairie audio indiquer quoi faire pour intégrer la lib: décommenter le include et le link de la lib code fourni pour charger/sauver les échantillons cvlc lancé en system() pour lire le résultat spectogramme? =====TP 4 Architecture pour le TNS: Mise en oeuvre sur ARDUINO===== Installer tout d'abord les librairies SPI et Tlv dans ~/Arduino/librairies et lancer la version 1.6.0 d'arduino, en tapant dans une console lancée à l'aide de ALT+F2 lxterm: cd ~ mkdir Arduino cd Arduino mkdir libraries cd ~/Arduino/libraries/ wget https://bvdp.inetdoc.net/files/iut/tp_dacspi/Tlv5637.zip wget https://bvdp.inetdoc.net/files/iut/tp_dacspi/SPI.zip unzip -o Tlv5637.zip unzip -o SPI.zip rm Tlv5637.zip SPI.zip echo "version 1.6.0 installée en /usr/share/arduino-1.6.0/" /usr/share/arduino-1.6.0/arduino echo "FINI" Les fois suivantes, vous lancerez juste arduino 1.6.0 en tapant dans une console lancée à l'aide de ALT+F2 lxterm: /usr/share/arduino-1.6.0/arduino 1° test d'une fonction simple en double et on génère juste les echantillons en sortie à partir d'échantillon d'entrée rampe. Une gpio est commutée à chaque appel de filtre et on regarde le temps de calcul. 2° faire la même chose avec le même filtre en virgule fixe et comparer 3° ajouter gestion timer et visualiser le temps avec la gpio (mise à 1 en entrée de la fonction de calcul, mise à 0 en sortie: on doit observer un signal rectangulaire de fréquence réglée grâce au timer) 4° ajouter lecture des échantillons avec le DAC ====Installation librairie arduino==== http://arduino.cc/en/Guide/Libraries lancer une fois arduino pour créer l'arborescence ~/sketchbook arduino fermer arduino installer la librairies dans ~/sketchbook/libraries/ pour arduino 1.0.5 cd ~/sketchbook/libraries/ ou installer la librairies dans ~/Arduino/libraries/ pour arduino 1.6 cd ~/Arduino/libraries/ ensuite: wget https://bvdp.inetdoc.net/files/iut/tp_dacspi/Tlv5637.zip unzip -r Tlv5637.zip relancer arduino arduino Croquis->Importer Bibliothèque->Importé et choisir la bibliothèque que l'on vient de copier cela doit ajouter un #include dans le fichier .ino Solution pour un RIF: // created 2015 March 29th by Bertrand VANDEPORTAELE #include #include const int chipSelectPin = 3; //connect to the Chip Select of the TLV5637 const int boutonPin = 9; const int itpin = 8; //used to monitor the activity of the processor during the interrupt, on arduino it is PORTB bit 0 const int filtrepin = 10; //used to monitor the activity of the processor during the interrupt, on arduino it is PORTB bit 2 TLV5637 DAC(chipSelectPin, REF_2048MV_TLV5637); //////////////////////////////////////////////////////////////////////////////////// //Nom du filtre: filtre_RIF //Description du filtre: Un filtre RIF général //////////////////////////////////////////////////////////////////////////////////// //Partie commune aux implémentations en virgule flottante et fixe: //////////////////////////////////////////////////////////////////////////////////// //passe ricrac pour un filtre avec 100 coefficients et à 2khz sur l'arduino en Virgule fixe #define SIZEFILTER_RIF 41 //////////////////////////////////////////////////////////////////////////////////// //Implémentation en virgule flottante double précision: //////////////////////////////////////////////////////////////////////////////////// //mettre ici toutes les constantes et variables définies en double, en indiquant le type dans le nom avec _d_ 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}; /*double numCoeff_d_FILTRE_EXEMPLE[SIZEFILTER_RIF]; //Coefficients du numérateur de la fonction de transfert du filtre au format double double ek_d_filtre_exemple[SIZEFILTER_RIF]; //tableau pour stocker les échantillons d'entrée au format double int indice_ecr_d_filtre_rif; void initFiltre_d_filtre_rif() {} double execFiltre_d_filtre_rif(double e) {} */ //////////////////////////////////////////////////////////////////////////////////// //Implémentation en virgule fixe: //////////////////////////////////////////////////////////////////////////////////// //mettre ici toutes les constantes et variables définies en int, en indiquant le type dans le nom avec _i_ short int numCoeff_i_FILTRE_RIF[SIZEFILTER_RIF]; //Coefficients du numérateur de la fonction de transfert du filtre au format virgule fixe short int ek_filtre_i_RIF[SIZEFILTER_RIF]; //tableau pour stocker les échantillons d'entrée au format virgule fixe int indice_ecr_i_filtre_rif; //pour gestion buffer circulaire #define NB_BITS_FRACTIONNAIRE 14 //////////////////////////////////////////////////////////////////////////////////// void initFiltre_i_filtre_rif() { int i; for (i = 0; i < SIZEFILTER_RIF; i++) { numCoeff_i_FILTRE_RIF[i] = floor( (numCoeff_d_FILTRE_RIF[i] * (1<< NB_BITS_FRACTIONNAIRE) )+ 0.5) ; ek_filtre_i_RIF[i]=0; } } //////////////////////////////////////////////////////////////////////////////////// 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; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ISR(TIMER0_COMPA_vect) { //timer0 interrupt 2kHz toggles pin 8 static short int skprec = 0; static short int eksuiv = 0; PORTB |= 1; // visu du temps passé dans l'interruption eksuiv = finishReadADCPolling() ; // valeur de l'echantillon sans valeur moyenne. startReadADCPolling(); //avec prescaler réglé à 1/128, l'acquisition d'un échantillon prend 120us, mais ce temps est utilisé pour commander le dac et calculer la valeur de l'échantillon en cours DAC.writeDACAB(skprec , eksuiv ); //prend 40us PORTB |= 4; // visu du temps passé dans la fonction filtre skprec = execFiltre_i_filtre_rif(eksuiv); PORTB &= (0xFF -4); //fin visu PORTB &= 0xFE; //fin visu } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void setupADCPolling() { ADCSRB=0; //ADCSRA = 1 << ADPS2 | 1 << ADPS1 | 1 << ADPS0 | 1 << ADEN; // prescaler 1/128, enabled, in that case, we use the best resolution, as the ADC conversion will be interlaced with the processing, the time needed is not lost, otherwise, we could use: //ADCSRA = 1 << ADPS2 | 1 << ADPS1 | 1 << ADEN; // prescaler 1/64, enabled ADCSRA = 1 << ADPS2 | 1 << ADPS0 | 1 << ADEN; // prescaler 1/32, enabled ADMUX = 1 << 6; //choix entrée analogique 0 et référence de tension =VCC } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*! \brief ADC Conversion Routine start */ inline void startReadADCPolling(void) { ADCSRA |= (1 << ADSC); // Start ADC Conversion } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*! \brief ADC Conversion Routine wait and read */ inline unsigned int finishReadADCPolling(void) { unsigned int result; while ((ADCSRA & (1 << ADIF)) != 0x10); // Wait till conversion is complete result = ADC; // Read the ADC Result ADCSRA |= (1 << ADIF); // Clear ADC Conversion Interrupt Flag return result ; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void setup() { Serial.begin(9600); Serial.println("Bonjour"); pinMode(itpin, OUTPUT); pinMode(filtrepin, OUTPUT); pinMode(boutonPin, INPUT); DAC.powerOn(); // start the tlv5637 library: DAC.speedFast(); initFiltre_i_filtre_rif(); //initialise le filtre cli();//stop interrupts //set timer0 interrupt at 2kHz TCCR0A = 0;// set entire TCCR2A register to 0 TCCR0B = 0;// same for TCCR2B TCNT0 = 0;//initialize counter value to 0 // set compare match register for 2khz increments OCR0A = 124;// = (16*10^6) / (2000*64) - 1 (must be <256) // turn on CTC mode TCCR0A |= (1 << WGM01); // Set CS01 and CS00 bits for 64 prescaler TCCR0B |= (1 << CS01) | (1 << CS00); // enable timer compare interrupt TIMSK0 |= (1 << OCIE0A); setupADCPolling(); startReadADCPolling(); //lance la demande d'acquisition du premier échantillon pour que la première interruption timer ne soit pas bloquée sei();//allows interrupts } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void loop() { Serial.println("Je suis vivant!"); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ===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 pourra avoir une amplitude crête à crête inférieur à 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 0. La durée à l'état haut indique la durée nécessaire au traitement de l'interruption TIMER. Sur la broche 10 de l'Arduino, un signal périodique indique la fréquence de l'interruption Timer 0. La durée à l'état haut indique la durée nécessaire au calcul de l'équation de récurence. ===Caractérisation du filtre=== - Identifier le Gain max - Identifier la bande passante à -3db ===Mesures=== - Mesurer le temps de calcul d'une équation de récurence - Mesurer le temps total de l'interruption timer - Déduire la fréquence d'échantillonnage maximale pour un filtre réalisant l'équation s(k)=1/2.( e(k) + e(k-1) ). (pour cela, commenter la ligne **Serial.println("Je suis vivant!");** dans la fonction **loop()**.) - Déduire le nombre de coefficients maximal de l'équation de récurrence pour une fréquence d'échantillonnage de 2Khz. Vérifier avec un exemple pris dans le wiki. ===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}; analyse de la réponse théorique du filtre: n=[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] fvtool(n,1) {{https://bvdp.inetdoc.net/files/iut/tp_tns/reponse_fv_tool.png}}