UE731 Travaux pratiques signal

的 Joseph Razik, last modified 在 2024-10-03

1   Préambule

Pour effectuer les quelques exercices de ce TP, vous allez travailler dans l'environnement notebook d'IPython/Jupyter. Pour cela, dans votre répertoire UE731 (à créer s'il n'existe pas), exécutez les commandes suivantes :

$> jupyter-notebook

Créez un nouveau projet Python3, que vous nommerez tp_signal.

Au début, dans la première cellule, pour activer le remplissage de l'espace de nommage avec les modules Numpy et Matplotlib (et math), écrivez la commande suivante et exécutez la cellule avec la séquence de touche Ctrl-Enter ou Shift-Enter

%pylab inline

L'option inline permet d'afficher les images dans le notebook et non dans une nouvelle fenêtre à part.

La commande %pylab d'importer dans l'espace de nommage numpy et matplotlib.

2   Échauffement, quelques manipulations simples

Chaque question se fera dans une cellule différente.

  1. Définissez la fonction delta(n) (\(\delta(n)\)) qui retourne 1 pour \(n=0\), et 0 sinon.
  2. Afficher cette fonction sur l'intervalle \([-20;20]\times[-0.1; 1.1]\) en utilisant la fonction stem.
  3. Définir la fonction unit(n) qui retourne 0 pour \(n<0\) et 1 sinon.
  4. Afficher cette fonction sur l'intervalle \([-20;20]\times[-0.1; 1.1]\) en utilisant la fonction stem.
  5. Tester et afficher sur \([-20;20]\) les fonctions \(\delta(n)\), \(\delta(-n)\), \(\delta(n-5)\) et \(\delta(n+5)\) en 4 sous-figures avec en titre la fonction.
  6. Afficher sur \([-20;20]\) les fonctions \(\alpha^n\times unit(n)\) pour les valeurs de \(\alpha\) 0.95, 0.9, 0.6.

3   On reste simple avec quelques manipulations d'objets Numpy

L'idée dans les manipulations demandées est d'utiliser les capacités de calculs vectorisés que permet Numpy, c'est-à-dire ne pas avoir à explicitement écrire des boucles.

  1. Définissez le tableau Numpy array C, des valeurs de la fonction \(\cos(2\pi x)\) sur 100 points répartis uniformément entre -1 et 1.
  2. Affichez sur \([-1;1]\) la fonction \(\cos(2\pi x)\) avec 10 puis 100 points de résolution, en utilisant C.
  3. Affichez sur \([-1;1]\) la fonction \(0.5\times\cos(2\pi x + 1)\) avec 10 puis 100 points de résolution.
  4. Définissez la matrice \(M\) de dimension 4x3, remplie uniquement de la valeur 2.
  5. Extrayez la ligne 3, puis la colonne 3.
  6. Modifiez \(M\) de telle sorte que la ligne 2 soit égale à la ligne 2 plus 3 fois la ligne 4. Puis la colonne 1 égale à la colonne 1 plus la moitié de la colonne 3.
  7. Changez les caractéristiques de \(M\) pour qu'elle soit maintenant une matrice 6x2.
  8. Créez les matrices carré \(B\) et \(C\) 4x4 et de valeur respective 2 et 3. Les résultats de B+C, B*C et B@C correspondent-ils bien avec ce que vous pensiez ?
  9. Créez une matrice \(N\) 3x3 avec les valeurs de 0 à 8.
  10. Trouvez les indices des valeurs supérieures à 3 dans le tableau [1, 0, 2, 0, 0, 3, 4, 0, 5].
  11. Trouvez les indices des valeurs de \(M\) supérieures à 3.
  12. Modifiez la matrice \(M\) afin de mettre à -1 toutes les valeurs supérieures à 3.

4   On passe la seconde !

Un signal discret à support fini (de taille fini) peut être vu comme un vecteur dans l'espace de dimension le nombre d'échantillons. Par exemple, un signal échantillonné et quantifié de telle sorte que nous ayons 64 points peut être vu comme un vecteur de \(\mathbb{C}^{64}\). Un second signal complètement différent de 64 échantillons également ne sera qu'un autre vecteur de cet espace. Il sera ainsi possible de définir et utiliser des notions de géométrie pour manipuler ces signaux : distance, égalité, orthogonalité.

  1. Définissez la fonction w_k qui aux paramètres k et N retourne le k-ième vecteur \(w_k = [e^{2i\pi kn/N}]_{n\in[0,N[}\) de la base \(\mathbb{C}^N\). La valeur par défaut de N sera de 64.
  2. Définissez la fonction affiche_w_k, qui pour un k donné affiche dans une sous-figure les parties réelles du vecteur \(w_k\) et dans une autre sous-figure les parties imaginaires du vecteur \(w_k\). Les signaux étant discrets, utilisez la fonction stem à la place de la fonction plot.
  3. Afficher en utilisant la fonction précédente les vecteurs \(w_k\) et \(w_{N-k}\) sur la base \(\mathbb{C}^{64}\) pour les valeurs suivantes de \(k\) : 0, 1, 2, 3, 4, 16, 20, 30, 31 et 32. Utilisez la fonction map pour faire ces affichages d'un coup. (Remarque: map retourne un générateur, il faut donc parcourir ce générateur. Pour cela, convertissez le simplement en liste : list(map(...))).
  4. Que remarquez-vous ?

5   On passe la troisième !

Sachant que la projection orthogonale du vecteur \(\mathbf{x}=x[n]\) sur le vecteur \(\mathbf{y}=y[n]\) dans cet espace vectoriel est défini par

\begin{equation*} <\mathbf{x}|\mathbf{y}> = \sum_{n=0}^{N-1} x[n]\overline{y[n]} \end{equation*}
  1. Définissez la fonction scalaire(x, k) qui retourne pour un \(k\) fixé le produit scalaire entre un vecteur \(\mathbf{x}=x[n]\) et le vecteur \(w_k\). Le résultat attendu est un nombre, réel ou complexe, mais pas un vecteur.
  2. Définissez la fonction decompose(x) qui retourne un vecteur formé des produits scalaires du signal \(\mathbf{x}=x[n]\) sur toute la base des \(w_k\). Le premier élément sera \(<\mathbf{x}|\mathbf{w_0}>\), le deuxième sera \(<\mathbf{x}|\mathbf{w_1}>\) etc.
  3. Définissez la fonction affiche_decompose(x) qui affiche dans quatre sous-figures le module, la phase, la partie réelle et la partie imaginaire du vecteur égale à la décomposition du vecteur x. Affichez la décomposition pour le vecteur \(\cos(\frac{2\pi}{4}n)\) sur cette base \(\mathbb{C}^{64}\).
  4. Écrivez le code permettant de d'indiquer quels sont les indices et les valeurs à ces indices pour lesquels les contributions en module sont non-nulles ?
  5. Quels sont ces indices et valeurs pour le vecteur défini par \(cos(\frac{2\pi}{4}n + \pi/4)\).
  6. Et pour le vecteur \(cos(\frac{2\pi}{15}n)\). Que constatez-vous ?

Cette décomposition n'est autre que la transformée de Fourier discrète (DFT) d'un signal de longueur 64. Créez une variable dft égale à votre fonction decompose pour la suite.

6   On explose tout !!

Dans cette question, vous allez manipuler des sons au format WAV. Pour cela, vous exécuterez dans une cellule l'instruction suivante:

from scipy.io.wavfile import read as wread

Ainsi vous aurez ajouté dans votre espace de nommage la fonction wread qui permet de lire un fichier audio au format WAV.

6.1   Analyse d'un fichier identifié

Les sons que vous allez traiter sont des signaux de codes DTMF (Dual Tone Multiple Frequency) utilisés pour distinguer par le son les touches numérotées d'un clavier téléphonique (DTMF sur wikipedia).

L'appuie sur une touche génère un son composé de deux fréquences, selon le tableau suivant :

- 1 209 Hz 1 336 Hz 1 477 Hz 1 633 Hz
697 Hz 1 2 3 A
770 Hz 4 5 6 B
852 Hz 7 8 9 C
941 Hz * 0 # D
  1. Lisez le fichier son_1 et affichez ses caractéristiques : sa fréquence d'échantillonnage, le nombre d'échantillons et sa durée.
  2. La seconde étape consiste à appliquer la DFT sur le signal en entier pour en connaître sa composition fréquentielle. Toutefois, votre fonction de calcul de la DFT n'est pas optimisé et un tel calcul serait très long. C'est pourquoi nous allons artificiellement sous-échantillonner le signal d'origine d'un facteur 4. Quelle est la nouvelle fréquence d'échantillonnage ? le nouveau nombre d'échantilons ? la nouvelle durée du signal ? la nouvelle résolution temporelle ? la nouvelle résolution fréquentielle ?
  3. Calculez et affichez le module (en dB) du spectre de la DFT de ce signal sous-échantillonné.
  4. Quelles sont les fréquences de forte intensité ? Cela correspond-t-il avec les fréquences DTMF associées à la touche appuyée ?

6.2   Analyse du signal d'une touche mystère

  1. Refaites les mêmes analyse que dans la partie précédente avec le fichier mystere.
  2. Quelle touche a été appuyée ?
  3. Mêmes questions mais avec le fichier etrange, mais appliqué cette fois un sous-échantillonnage de facteur 8. Expliquez ce que vous observez.