Traitement du Signal - TP1 : Manipulation de signaux temporels¶


AVANT DE COMMENCER, TRES IMPORTANT !!

Durant ce semestre, vous aurez des TPs à rendre.

Vous aurez donc dans ces fichiers de TPs des cellules de codes à compléter, comme celle-ci :

In [1]:
# A COMPLETER
...

Ou des cellules texte à compléter (commençant par REPONSE) :

REPONSE : Ici, vous écrivez votre réponse.

Vérifiez bien avant de rendre un TP que toutes les cellules (codes et texte) soient bien complétées.


L'objectif de ce premier TP va être de prendre en main les packages python NumPy et Matplotlib pour tracer des signaux. Ces deux librairies sont essentielles et vont être utilisées durant tout le semestre, donc prenez bien le temps de lire la documentation :

  • NumPy : https://numpy.org/doc/2.3/user/index.html
  • Matplotlib : https://matplotlib.org/stable/index.html

Je vous recommande FORTEMENT :

  • de prendre des notes durant les TP, sur les fonctions que vous utilisez, comme sur les résultats obtenus
  • de vous partager le clavier si vous êtes en binôme ! Il faut que tout le monde code !
  • de ne pas utiliser ChatGPT ou tout autre IA. Vous aurez un examen pratique sans possibilité de les consulter, donc autant apprendre en TP

Correction du TP : préambule¶

Pour cette correction de TP, je vais vous donner pour chaque algorithme une version minimale attendue (c'est-à-dire ce que j'attends que vous codiez au niveau minimum) et une version optimisée. Je vous conseille dans un premier temps de vérifier vos résultats avec ceux de la correction, afin de corriger les potentielles erreurs que vous avez, mais également de jeter un coup d'oeil à la version optimisée. Certaines manipulations peuvent être complexes, mais en réalité, la plupart de celles utilisées sont à votre niveau. L'objectif est que vous puissiez vous améliorer dans votre manière de coder avec NumPy et Matplotlib, même si vous avez déjà une première base fonctionnelle.


Partie 1 : Traçons plein de signaux !¶

Dans Traitement du Signal, il y'a Signal. On va donc partir de la base : créer des signaux via des tableaux unidimensionnels NumPy et tracer les signaux dans de jolies figures via Matplotlib. Pas de panique, on va y aller pas à pas, avec pour commencer, l'importation des librairies.

In [2]:
# Importation des librairies
import numpy as np
from matplotlib import pyplot as plt

Axe temporel¶

Lorsqu'on trace un signal dans le domaine temporel, on affiche l'intensité du signal en fonction du temps. On va donc d'abord créer un vecteur symbolisant le temps en secondes.

Créez un tableau unidimensionnel NumPy $t$, allant de -5 à 5 secondes avec un pas de temps de 0.01 secondes.

In [3]:
# A COMPLETER
# Création de l'axe temporel t

# VERSION MINIMALE
t = [] # Création d'une liste vide
for i in range(-500,500): #i varie de -500 à 500 (on ne peut itérer que sur des entiers avec range)
    t.append(i/100) # On ajoute chaque élément i divisé par 100 dans la liste t, pour avoir un pas de 0.01

t = np.array(t) # On transforme la liste en tableau NumPy

# VERSION OPTIMISEE
t = np.arange(-5,5,0.01)

On a notre axe temporel, créeons maintenant notre premier signal. Il est représenté sous forme de tableau unidimensionnel de même taille que l'axe temporel. Chaque valeur du signal à l'indice i correspond alors à l'intensité du signal au moment t[i].

Notre premier signal est simple. Créez un signal proportionnel au temps, toujours sous forme de tableau NumPy unidimensionnel : $s(t) = A \cdot t$ avec $A=0.5$.

In [4]:
# A COMPLETER
# Création du signal s

# VERSION MINIMALE
A = 0.5
s = [] # s est initialisé comme une liste vide
for i in range(len(t)): # On itère sur la longueur de t (0->len(t)-1)
    s.append(t[i]*A) # On ajoute à s la valeur t[i]*A
s = np.array(s) # On transforme la liste en tableau NumPy

# VERSION OPTIMISEE
A = 0.5
# Ici, je multiplie directement le vecteur t par un scalaire A, ce qui nous donne un vecteur
s = A*t

On a notre signal, notre axe temporel, il suffit alors de tracer tout ça ! On va utiliser la fonction plot de Matplotlib (Tutoriel). Faites moi une jolie figure, avec titre, noms des axes, légende, etc.

PS: Il y'a dans le tutoriel des fonctions qui ne sont pas utiles. Veuillez sélectionner uniquement celles qui sont pertinentes...

In [5]:
# A COMPLETER
# Affichage du signal s selon l'axe temporel t

# On crée la figure
fig, ax = plt.subplots()

# On trace s en ordonnée en fonction de t en abcisse
ax.plot(t, s)

# On définit le titre de la figure, ainsi que le label des axes
ax.set(xlabel='Temps (s)', ylabel='Intensité du signal',
       title='Signal s(t)')

# On affiche la grille de fond (optionnel)
ax.grid()

# On affiche la figure
plt.show()
No description has been provided for this image

Voilà, notre premier signal est tracé. A vous de jouer !

Signal porte a(t)¶

La fonction du signal porte est définie par l'équation suivante :

\begin{equation*} \Pi(t) = \begin{cases} 1, & \text{si } |t| \leq \frac{1}{2}, \\ 0, & \text{sinon}. \end{cases} \end{equation*}

In [6]:
# A COMPLETER
# Création et affichage du signal a

# VERSION MINIMALE
a = []
for i in range(len(t)):
    if abs(t[i])<=0.5:
        a.append(1)
    else:
        a.append(0)
a = np.array(a)

# VERSION OPTIMISEE
a = np.zeros_like(t)
a[np.abs(t)<=0.5]=1

plt.figure()
plt.plot(t,a,label="Signal a")
plt.xlabel("Temps (s)")
plt.ylabel("Intensité")
plt.legend()
plt.show()
No description has been provided for this image

Signal échelon unité b(t)¶

La fonction du signal échelon unité (aussi appelée fonction de Heaviside) est définie par l'équation suivante :

\begin{equation*} \Gamma(t) = \begin{cases} 0, & \text{si } t < 0, \\ 1, & \text{si } t \geq 0. \end{cases} \end{equation*}

In [7]:
# A COMPLETER
# Création et affichage du signal 

# VERSION MINIMALE
b = []
for i in range(len(t)):
    if t[i]<0:
        b.append(0)
    else:
        b.append(1)
b = np.array(b)

# VERSION OPTIMISEE
b = np.zeros_like(t)
b[t>=0]=1

plt.figure()
plt.plot(t,b,label="Signal b")
plt.xlabel("Temps (s)")
plt.ylabel("Intensité")
plt.legend()
plt.show()
No description has been provided for this image

Signal Échelon Retardé c(t)¶

La fonction du signal échelon retardé est une version décalée dans le temps de la fonction échelon unité. Si le retard est $t_{0}$, l'équation est donnée par :

\begin{equation*} \Gamma(t - t_0) = \begin{cases} 0, & \text{si } t < t_0, \\ 1, & \text{si } t \geq t_0. \end{cases} \end{equation*}

In [8]:
# A COMPLETER
# Création et affichage du signal c

# VERSION MINIMALE
t0=0.5
c = []
for i in range(len(t)):
    if t[i]<t0:
        c.append(0)
    else:
        c.append(1)
c = np.array(c)

# VERSION OPTIMISEE
t0=0.5
c = np.zeros_like(t)
c[t>=t0]=1

plt.figure()
plt.plot(t,c,label="Signal c")
plt.xlabel("Temps (s)")
plt.ylabel("Intensité")
plt.legend()
plt.show()
No description has been provided for this image

Signal d(t)¶

Le signal d(t) est défini par l'équation suivante :

\begin{equation*} d(t)= d1(t) \cdot d2(t) \cdot d3(t) \end{equation*}

avec :

\begin{equation*} d1(t)= (-t + 3) \end{equation*}

\begin{equation*} d2(t)= \Gamma(t + 1) \end{equation*}

\begin{equation*} d3(t)= \Gamma(-t + 3) \end{equation*}

Affichez sur la même figure les signaux $d2(t)$, $d3(t)$ et $d(t)$ (n'oubliez pas les légendes !!)

In [9]:
# A COMPLETER
# Création et affichage du signal d

# VERSION MINIMALE
d1 = []
for i in range(len(t)):
    d1.append(-t[i]+3)
d1 = np.array(d1)

d2 = []
for i in range(len(t)):
    if t[i]+1<0:
        d2.append(0)
    else:
        d2.append(1)
d2 = np.array(d2)

d3 = []
for i in range(len(t)):
    if -t[i]+3<0:
        d3.append(0)
    else:
        d3.append(1)
d3 = np.array(d3)

d = []
for i in range(len(t)):
    d.append(d1[i]*d2[i]*d3[i])
d = np.array(d)

# VERSION OPTIMISEE
d1 = -t+3
d2 = np.zeros_like(t)
d2[t>=-1]=1
d3 = np.zeros_like(t)
d3[t<=3]=1

d=d1*d2*d3

plt.figure()
plt.plot(t,d2,label="Signal d2")
plt.plot(t,d3,label="Signal d3")
plt.plot(t,d,label="Signal d")
plt.xlabel("Temps (s)")
plt.ylabel("Intensité")
plt.legend()
plt.show()
No description has been provided for this image

Signal e(t)¶

Le signal e(t) est défini par l'équation suivante :

\begin{equation*} e(t) = e^{-3t} \cdot \Gamma(t - 1) \end{equation*}

In [10]:
# A COMPLETER
# Création et affichage du signal e

# VERSION MINIMALE

import math

e1 = []
for i in range(len(t)):
    e1.append(np.exp(-3*t[i]))
e1 = np.array(e1)

e2 = []
for i in range(len(t)):
    if (t[i]-1<0):
        e2.append(0)
    else:
        e2.append(1)
e2 = np.array(e2)

e = []
for i in range(len(t)):
    e.append(e1[i]*e2[i])
e = np.array(e)

# VERSION OPTIMISEE
e1 = np.exp(-3*t)
e2 = np.zeros_like(t)
e2[t-1>=0]=1
e=e1*e2

plt.figure()
plt.plot(t,e,label="Signal e")
plt.xlabel("Temps (s)")
plt.ylabel("Intensité")
plt.legend()
plt.show()
No description has been provided for this image

Signal cosinus f(t)¶

Le signal cosinus est défini par l'équation suivante :

\begin{equation*} y(t) = \cos(\omega t) \end{equation*}

où $\omega$ est la pulsation angulaire en radians par seconde.

Ici, on prendra $\omega = 2.5$

In [11]:
# A COMPLETER
# Création et affichage du signal f

# VERSION MINIMALE
omega = 2.5
f = []
for i in range(len(t)):
    f.append(math.cos(t[i]*omega))
f = np.array(f)

# VERSION OPTIMALE
omega = 2.5
f = np.cos(omega*t)

plt.figure()
plt.plot(t,f,label="Signal f")
plt.xlabel("Temps (s)")
plt.ylabel("Intensité")
plt.legend()
plt.show()
No description has been provided for this image

Tracez maintenant ce même signal mais sur un axe temporel allant de -5 à -1 puis de 1 à 5 avec un pas de temps de 0.01 secondes.

Note 1 : Il ne faut pas de valeurs à 0 entre -1 et 1, ce qui signifie qu'il faut reconstruire un autre axe temporel t', et recréer un signal f' à partir de l'axe temporel t'.

Note 2 : Pour créer l'axe temporel t', on peut créer deux axes temporels, puis les concatener...

Note 3, Infinity War : Si vous tracez le signal sous forme de courbe, vous allez avoir tous vos points reliés. Il faut donc tracer le signal comme si c'était un nuage de points. Allez donc voir les options possibles dans plt.plot (paramètre fmt).

In [12]:
# A COMPLETER
# Création de l'axe temporel t'

# VERSION MINIMALE
t_ = [] 
for i in range(-500,100): 
    t_.append(i/100) 

for i in range(100,500): 
    t_.append(i/100) 

t_ = np.array(t_) 

# VERSION OPTIMISEE
t_ = np.concatenate([np.arange(-5,-1,0.01),np.arange(1,5,0.01)])

# Création et affichage du signal f'

# VERSION MINIMALE
omega = 2.5
f_ = []
for i in range(len(t_)):
    f_.append(math.cos(t_[i]*omega))
f_ = np.array(f_)

# VERSION OPTIMALE
omega = 2.5
f_ = np.cos(omega*t_)

plt.figure()
plt.plot(t_,f_,'.',label="Signal f'") # On précise '.' en argument pour avoir des points affichés à chaque valeur au lieu d'une courbe. 
                                      # On aurait pu utiliser également la fonction plt.scatter
plt.xlabel("Temps (s)")
plt.ylabel("Intensité")
plt.legend()
plt.show()
No description has been provided for this image

Signal périodique g(t)¶

Le signal g(t), périodique en T, est défini par l'équation suivante:

\begin{equation*} g(t) = \left \{ \begin{array}{ll} t & 0 \leq t \leq \frac{T}{2}\\ -t+T & \frac{T}{2} \leq t \leq T \\ \end{array} \right. \end{equation*}

On prendra ici T = 2 secondes.

Note : Pour vous faciliter le travail, créez d'abord un vecteur qui représente l'axe temporel modulo la période. Vous saurez ainsi à chaque instant t où vous en êtes dans la période T.

In [13]:
# A COMPLETER
# Création et affichage du signal g

# VERSION MINIMALE
T = 2
g = []
for i in range(len(t)):
    if (t[i]%T<=T/2):
        g.append(t[i]%T)
    else:
        g.append(-(t[i]%T)+T)
g = np.array(g)

# VERSION OPTIMISEE
T = 2
t_modulo_T = t%T
g = t_modulo_T
# Ici, j'utilise un masque, c'est-à-dire que je sélectionne les valeurs de mon vecteur en fonction d'une condition, et je peux ensuite les modifier
g[t_modulo_T>=T/2] = -g[t_modulo_T>=T/2]+T

plt.figure()
plt.plot(t,g,label="Signal g")
plt.xlabel("Temps (s)")
plt.ylabel("Intensité")
plt.legend()
plt.show()
No description has been provided for this image

Une jolie figure qui regroupe tous les signaux !¶

Affichez maintenant tous les signaux ($a$, $b$, $c$, $d$, $e$, $f$, $f'$ et $g$) dans une seule figure divisées en 8 sous-figures avec la fonction subplot sur Matplotlib (Tutoriel). N'oubliez pas les titres/sous-titres. L'objectif est de faire une figure lisible, donc n'hésitez pas à regarder en détail la documentation Matplotlib !

In [14]:
# A COMPLETER
# Affichage des 8 signaux sur une même figure
plt.subplots(4,2)

plt.subplot(421)
plt.plot(t,a)
plt.title("Signal a")

plt.subplot(422)
plt.plot(t,b)
plt.title("Signal b")

plt.subplot(423)
plt.plot(t,c)
plt.title("Signal c")

plt.subplot(424)
plt.plot(t,d)
plt.title("Signal d")

plt.subplot(425)
plt.plot(t,e)
plt.title("Signal e")

plt.subplot(426)
plt.plot(t,f)
plt.title("Signal f")

plt.subplot(427)
plt.plot(t_,f_,'.')
plt.title("Signal f'")

plt.subplot(428)
plt.plot(t,g)
plt.title("Signal g")

# Fonction super utile qui réajuste automatiquement la figure pour que tous les labels et signaux soient bien visibles
plt.tight_layout()

plt.show()
No description has been provided for this image

Partie 2 : Vous savez tracer des signaux ? Ok !¶

Si vous en êtes là, c'est que vous avez bien tracé les précédents signaux. Le challenge va devenir plus difficile. On va maintenant tracer PLEIN de signaux particuliers, mais pour un but précis que vous devinerez à la fin...

Signal B1¶

1er signal à construire :

  • L'axe temporel T1 est défini de -0.45 à 0.45, avec un pas de 0.005
  • Le signal B1 suit l'équation suivante:

\begin{equation*} B1(t) = 2 \end{equation*}

In [15]:
# A COMPLETER
# Création de l'axe temporel T1
T1 = np.arange(-0.45, 0.45, 0.005)

# Création du signal B1
B1 = np.ones_like(T1)*2

# Affichage du signal B1 selon l'axe temporel T1
plt.figure()
plt.plot(T1,B1,'.')
plt.show()
No description has been provided for this image

Signal B2¶

2ème signal à construire :

  • L'axe temporel T2 est défini de -7 à -3, puis de 3 à 7, avec un pas de 0.001
  • Le signal B2 suit l'équation suivante:

\begin{equation*} B2(t) = 3\sqrt{-\left(\frac{t}{7}\right)^{2}+1} \end{equation*}

In [16]:
# A COMPLETER
# Création de l'axe temporel T2
T2 = np.concatenate([np.arange(-7, -3, 0.001), np.arange(3, 7, 0.001)])

# Création du signal B2
B2 = 3*np.sqrt(-(T2/7)**2+1)

# Affichage du signal B2 selon l'axe temporel T2
plt.figure()
plt.plot(T2,B2,'.')
plt.show()
No description has been provided for this image

Signal B3¶

3ème signal à construire :

  • L'axe temporel T3 est défini de -7 à -4, puis de 4 à 7, avec un pas de 0.001
  • Le signal B3 suit l'équation suivante:

\begin{equation*} B3(t) = -3\sqrt{-\left(\frac{t}{7}\right)^{2}+1} \end{equation*}

In [17]:
# A COMPLETER
# Création de l'axe temporel T3
T3 = np.concatenate([np.arange(-7, -4, 0.001), np.arange(4, 7, 0.001)])

# Création du signal B3
B3 = -3*np.sqrt(-(T3/7)**2+1)

# Affichage du signal B3 selon l'axe temporel T3
plt.figure()
plt.plot(T3,B3,'.')
plt.show()
No description has been provided for this image

Signal B4¶

4ème signal à construire :

  • L'axe temporel T4 est défini par $0.75 \leq |t| \leq 1$, avec un pas de 0.001
  • Le signal B4 suit l'équation suivante:

\begin{equation*} B4(t) = 9-8\left|t\right| \end{equation*}

In [18]:
# A COMPLETER
# Création de l'axe temporel T4
T4 = np.concatenate([np.arange(-1, -0.75, 0.001), np.arange(0.75, 1, 0.001)])

# Création du signal B4
B4 = 9-8*np.abs(T4)

# Affichage du signal B4 selon l'axe temporel T4
plt.figure()
plt.plot(T4,B4,'.')
plt.show()
No description has been provided for this image

Signal B5¶

5ème signal à construire :

  • L'axe temporel T5 est défini par $0.45 \leq |t| \leq 0.75$, avec un pas de 0.001
  • Le signal B5 suit l'équation suivante:

\begin{equation*} B5(t) = 3\left|t\right|+0.75 \end{equation*}

In [19]:
# A COMPLETER
# Création de l'axe temporel T5
T5 = np.concatenate([np.arange(-0.75, -0.45, 0.001), np.arange(0.45, 0.75, 0.001)])

# Création du signal B5
B5 = 3*np.abs(T5)+0.75

# Affichage du signal B5 selon l'axe temporel T5
plt.figure()
plt.plot(T5,B5,'.')
plt.show()
No description has been provided for this image

Signal B6¶

6ème signal à construire :

  • L'axe temporel T6 est défini par $1 \leq |t| \leq 3$, avec un pas de 0.001
  • Le signal B6 suit l'équation suivante:

\begin{equation*} B6(t) = 1.5-.5\left|x\right|-\frac{6\sqrt{10}}{14}\left(\sqrt{3-x^{2}+2\left|x\right|}-2\right) \end{equation*}

In [20]:
# A COMPLETER
# Création de l'axe temporel T6
T6 = np.concatenate([np.arange(-3,-1, 0.001), np.arange(1, 3, 0.001)])

# Création du signal B6
B6 = 1.5 - 0.5*abs(T6) - (6*np.sqrt(10)/14)*(np.sqrt(3-T6**2+2*abs(T6))-2)

# Affichage du signal B6 selon l'axe temporel T6
plt.figure()
plt.plot(T6,B6,'.')
plt.show()
No description has been provided for this image

Signal B7¶

7ème signal à construire :

  • L'axe temporel T7 est défini de -4 à 4, avec un pas de 0.0001
  • Le signal B7 suit l'équation suivante:

\begin{equation*} B7(t) = \left|\frac{t}{2}\right|-\frac{3\sqrt{33}-7}{112}t^{2}+\sqrt{1-\left(\operatorname{abs}\left(\left|t\right|-2\right)-1\right)^{2}}-3 \end{equation*}

In [21]:
# A COMPLETER
# Création de l'axe temporel T7
T7 = np.arange(-4, 4, 0.0001)

# Création du signal B7
B7 = abs(T7/2) - ((3*np.sqrt(33)-7)/112)*T7**2 + np.sqrt(1-(abs(abs(T7)-2)-1)**2) -3

# Affichage du signal B7 selon l'axe temporel T7
plt.figure()
plt.plot(T7,B7,'.')
plt.show()
No description has been provided for this image

Le reveal final¶

Il est temps de tracer les 7 signaux avec leur axes temporels respectifs sur une seule et même figure. Créez donc cette figure mystérieuse, avec les courbes/nuages de points en noir pour un effet plus dramatique...

Et répondez à la question que tout le monde se pose : qui devrait débarquer après avoir tracer ces 7 signaux ?

In [22]:
# A COMPLETER
# Affichage des 7 signaux construits sur une seule et même figure
plt.figure()
plt.plot(T1,B1,'k.')
plt.plot(T2,B2,'k.')
plt.plot(T3,B3,'k.')
plt.plot(T4,B4,'k.')
plt.plot(T5,B5,'k.')
plt.plot(T6,B6,'k.')
plt.plot(T7,B7,'k.')
plt.show()
No description has been provided for this image

REPONSE : TANANANANANANANANA BATMAN !!!!!

Rendu de TP¶

Le rendu qui vous est demandé pour les TP de TIM est la version HTML de ce notebook. Pour cela, avant de déposer votre rendu, vous devez :

  • Redémarrer le noyau/kernel Python en effaçant les sorties
  • Exécuter toutes les cellules du Notebook à la suite
  • Exporter le fichier en HTML