=====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}}