CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

| Download
Views: 363
Image: ubuntu2204
Kernel: Python 3 (system-wide)

Lab IV : Identification des paramètres d'une sinusoïde de fréquence connue

  • Cours "Physique du Numérique" - Portail René Descartes - AMU

Préparé par :

  • Jean-Marc Themlin (v. 2021-09), Aix-Marseille Université © Contenus à diffusion restreinte, dans le cadre de ce cours.


Remarque

A toutes fins utiles, on rappelle ici qu'une solution de secours (moins pratique, et probablement moins stable) permet d'accéder aux énoncés sur des notebooks fonctionnels sur un serveur distant (Binder) uniquement depuis votre navigateur. Vous pourrez exécuter, modifier et compléter les cellules de code et remplir les cellules de texte ; à l'issue de votre travail, vous pourrez importer (commande Download dans le menu File) le notebook complété pour le stocker localement sur votre PC et le déposer sur AMeTICE. Pour accéder à Binder pour travailler ce premier TP, il suffit de cliquer sur l'icône ci-dessous et de sélectionner dans le répertoire des fichiers accessibles, le notebook Enonce_Lab_4_Identification_Sinusoides.ipynb ; soyez patient, ça peut prendre quelques minutes pour lancer l'environnement :

Binder


Introduction

Le produit scalaire, qui permet de quantifier la ressemblance entre deux signaux numériques x[n]x[n] et y[n]y[n] définis sur le même domaine, est défini par : <s,r>=n=1Ns[n] r[n] Δt <s,r>=\sum_{n=1}^{N} s[n] \ r^*[n] \ \Delta t Δt\Delta t est la période d'échantillonnage commune aux deux signaux numériques.

Dans ce TP, vous allez utiliser le produit scalaire pour déterminer (identifier) l'amplitude AA et la phase à l'origine ϕ\phi d'une (co)sinusoïde s(t)=A cos(2πf0t+ϕ)s(t)=A \ \cos (2\pi f_0 t + \phi) de fréquence f0f_0 connue, qu'on supposera échantillonnée sur l'intervalle T0T_0 entre les temps t1t_1 et t2t_2 par la (co)sinusoïde discrète s[n]=A cos(2πf0nΔt+ϕ)s[n] = A \ \cos (2\pi f_0 n \Delta t +\phi), avec une période d'échantillonnage Δt\Delta t. Le signal (co)sinusoïdal d'entrée à analyser est donc représenté par N échantillons de la sinusoïde numérique s[n]s[n], avec N=t2t1Δt=T0ΔtN = \frac{t_2-t_1}{\Delta t}=\frac{T_0}{\Delta t}.

Nous avons montré au cours l'intérêt d'utiliser le produit scalaire entre s[n]s[n] et une exponentielle complexe de référence r[n]=ej2πf0tr[n]=e^{j2\pi f_0t} (elle fixera notamment l'instant t=0t=0 qui détermine le déphasage recherché) à la fréquence f0f_0, la même que celle de s[n]s[n].

Avec l'hypothèse que la durée du signal est un multiple de T0=1f0T_0=\frac{1}{f_0}, le résultat du produit scalaire entre s[n]s[n] et r[n]r[n], qui est un nombre complexe, s'exprime en effet : s,rA2  ejϕ  (t2t1)=A2  ejϕ  NΔt \left\langle {s,r} \right\rangle \simeq {A \over 2}\;e^{j\phi } \;(t_2 - t_1 ) = {A \over 2}\;e^{j\phi } \;N\Delta t Par conséquent, le calcul numérique du produit scalaire fournit directement les paramètres estimés : ParseError: KaTeX parse error: Undefined control sequence: \eqalign at position 2: \̲e̲q̲a̲l̲i̲g̲n̲{ & A_{Est} \…

Programmation : Quelques fonctions Python utiles

Exécutez Help(nom_de_la_fonction) ou nom_de_la_fonction? pour afficher l'aide des fonctions ci-dessous et comprendre ce qu'elles réalisent.

Dans la bibliothèque standard : len(x), abs(z)z est un nombre complexe qui s'écrit z = a*1j+b ou z = c exp(1j*d).

Dans la bibliothèque math : floor(x)

Dans la bibliothèque numpy : np.size(t), np.zeros(N), np.angle(z)z est un nombre complexe.

Dans la bibliothèque matplotlib.pyplot : plt.subplot(2,1,n=1,2)

Boucles for : Examinez l'exemple ci-dessous, qui contient deux exemples de boucles for. Dans le second, on remplit séquentiellement une liste avec un élément calculé dans la boucle, ce qui nous sera très utile par la suite.

klong = [0,1,2,3,4,5,6,7,8,9] # déclaration d'un objet liste klong2 = [] # déclaration d'un objet liste vide for k in klong: k2 = k**2 print(k2) for k in klong: klong2.append(k**2) # Ajout d'un élément à l'objet liste print(klong2)
0 1 4 9 16 25 36 49 64 81 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Problème IV-1 : Paramètres d'une sinusoïde

Utilisez la cellule de code ci-dessous pour générer une (co)sinusoïde discrète s[n]s[n]

import numpy as np import matplotlib.pyplot as plt t=np.linspace(-7.5,27,1000) v = 25*np.cos(2*np.pi*(1/6.3)*t-2) s1=np.sqrt(624)*np.cos(t) plt.plot(t+2,s1) plt.plot(t,v) plt.xlabel('t (s)') plt.ylabel('Signal s(t)') plt.grid()
Image in a Jupyter notebook

Q1: A partir du graphe, déterminez visuellement les paramètres (A,f0,ϕ)(A,f_0,\phi) de cette (co)sinusoïde d'expression générale =A cos(2πf0t+ϕ)=A \ \cos (2\pi f_0 t + \phi).

Q1-R: Votre réponse ici : A = 25 f0 = 1/6 Phi = -2

Q2: En utilisant le vecteur tt comme support temporel, vérifiez la validité de votre estimation en superposant au graphe précédent la sinusoïde reconstituée à partir de vos estimations

Q2-R: Votre réponse ici : déterminer les paramètres graphiquement n'est jamais assez précis.


Problème IV-2 : Estimer l'amplitude et la phase d'un signal sinusoïdal de fréquence connue f0f_0

Utilisez la cellule de code ci-dessous pour construire une fonction Python apc pour calculer l'amplitue AA et la phase à l'origine ϕ\phi d'un signal (co)sinusoïdal discret s[n]s[n] de fréquence connue f0f_0.

Les paramètres d'entrée de la fonction seront :

  • le vecteur support temporel du signal

  • le signal à analyser (dont vous allez estimer les paramètres)

  • la fréquence f0f_0 de ce signal

Les paramètres retournés par la fonction seront :

  • AEstA_{Est}, l'amplitude estimée de la sinusoïde

  • ϕEst\phi_{Est}, la phase à l'origine estimée de la sinusoïde

f_0 = 1/6 x= 25* np.cos(2*np.pi*f_0*t-2) def apc(t,s,f_0): x = [] somme=0 for i in range (len(s)): somme += s[i]*np.exp(-1j*2*np.pi*f_0*t[i]) Te = t[1] - t[0] T = t[-1] - t[0] A = 2/T*np.abs(somme*Te) phi= np.angle(somme) return A,phi print(apc(t,x,1/6))
(25.552487373916517, -2.0176943010216557)

Test de la fonction apcapc

Test 1

Dans la cellule ci-dessous, utilisez le signal généré au IV-1 pour vérifier vos estimations initiales.

Q3 : Remplissez la table ci-dessous avec les résultats de l'estimation visuelle (cfcf IV-1) et de l'estimation numérique.

AEstA_{Est}ϕEst\phi_{Est}
Estimation "manuelle"25-2
Estimation "automatique"25.552487373916517-2.0176943010216557

Dans la cellule ci-dessous, pour vérifier la validité de l'estimation, superposez dans un graphe la sinusoïde initiale (cfcf IV-1) et la (co)sinusoïde générée à partir des paramètres estimés numériquement (avec apc).

import numpy as np import matplotlib.pyplot as plt t=np.linspace(-7.5,27,1000) v = 25*np.cos(2*np.pi*(1/6.3)*t-2) s1= 25.552487373916517*np.cos(t-2.0176943010216557) plt.plot(t,s1) plt.plot(t,v) plt.xlabel('t (s)') plt.ylabel('Signal s(t)') plt.grid()
Image in a Jupyter notebook

Test 2

Vous allez à présent tester le bon fonctionnement de votre fonction apc sur un signal de paramètres inconnus, hormis bien sûr sa fréquence f0f_0. Les premières instructions de la cellule ci-dessous vont télécharger dans l'environnement le fichier lab_data.mat, qui contient les variables suivantes :

  • le vecteur support t_samp

  • le vecteur signal s_samp, qui contient les échantillons d'une sinusoïde discrète d'une fréquence f0=100 Hzf_0=100 \ Hz.

Les valeurs retournées par votre fonction seront les suivants :

  • la période d'échantillonnage Δt\Delta t

  • le nombre total NN de points que contiennent chacun de ces deux vecteurs

  • la valeur de l'amplitude estimée AEstA_{Est}

  • la valeur de la phase à l'origine estimée ϕEst\phi_{Est}

Pour vérifier la validité de l'estimation, complétez la cellule de code ci-dessous avec les instructions Python permettant de superposer dans un graphe la sinusoïde initiale (s_samp en fonction de t_samp) avec la (co)sinusoïde générée à partir des paramètres estimés numériquement.

# # Imports from a remote or a local .mat (MatLab) file # import requests from scipy.io import loadmat data = loadmat('lab_data_apc.mat') # %whos print("Vérification") t=data['t_samp'] t_samp=t[0,:] s=data['s_samp'] s_samp=s[0,:] f0 = 100 print(t_samp.ndim, t_samp.shape, t_samp.size, t_samp[0]) print(s_samp.ndim, s_samp.shape, s_samp.size, s_samp[0]) # Compléter ci-dessous pour comparer les deux sinusoïdes (signal et estimée) def verif(t,s,f0): delta = t_samp[1] - t_samp[0] c = apc(t_samp,s_samp,100) N = (1/f0)/delta return c,delta,N print(verif(t_samp,s_samp,f0))
Vérification 1 (200,) 200 0.0 1 (200,) 200 -20.250405381018634 ((20.42010050251256, -3.060000000000001), 0.0005, 20.0)
Vérification 1 (200,) 200 0.0 1 (200,) 200 -20.250405381018634

Problème IV-3 : Tests de robustesse de la fonction d'estimation apc

Pour qu'un programme informatique soit opérationnel, il doit être testé de façon très approfondie lors de son développement. Ceci implique notamment d'explorer la plus large zone possible de l'espace des paramètres d'entrée du programme.

Cas non-idéal : Durée du signal différente d'un multiple de T0=1f0T_0=\frac{1}{f_0}

Les formules qui donnent les paramètres estimés ont été établies dans l'hypothèse que la durée du signal est un multiple (entier) de la période du signal s[n]s[n], soit T0=1f0T_0=\frac{1}{f_0}.

Mais que se passe-t'il si vous appliquez la fonction apc sur un signal sinusoïdal qui ne contient pas un nombre entier de périodes T0=1f0T_0=\frac{1}{f_0} ?

Dans la cellule de code ci-dessous, générez le signal indiqué :

D=8 N=2**8 t=np.arange(0,D,1/N) s=8*np.cos(2*np.pi*t/3-np.pi/2) print(apc(t,s,1/3)) plt.plot(t,s)
(7.802284479558461, -1.5250792646745612)
[<matplotlib.lines.Line2D at 0x7f7986dda770>]
Image in a Jupyter notebook

Q4 : Que vaut la période de s[n]s[n] ? Combien de périodes (chiffre décimal !) contient le signal s[n]s[n] ?

Q4-R (votre réponse ici) : la période s[n] est 3. le signal s[n] contient D/T = 8/3 = 2,67périodes.

Q5 : Que valent les paramètres (A,f0,ϕ)(A,f_0,\phi) de cette (co)sinusoïde d'expression générale =A cos(2πf0t+ϕ)=A \ \cos (2\pi f_0 t + \phi) ?

Q5-R (votre réponse ici) : d'après la fonction apc A = 7.802284479558461 phi = -1.5250792646745612 f0 = 1/3

Complétez la cellule de code ci-dessus pour estimer avec apc l'amplitude AEstA_{Est} et la phase à l'origine ϕEst\phi_{Est} de s[n]s[n].

Complétez la table ci-dessous en y incluant les écarts absolus (AEstA|A_{Est}-A| et ϕEstϕ|\phi_{Est}-\phi|) et relatifs (AEstAA\frac{|A_{Est}-A|}{A} et ϕEstϕϕ|\frac{\phi_{Est}-\phi}{\phi}|) :

Valeur exacteValeur estiméeEcart absoluEcart relatif
Amplitude> 7.802284479558461

8 | environ 0,2 | | | Phase à l'origine | -1.5250792646745612 | -pi/2 | | |

Visualiser la dépendance de l'estimation en fonction de la durée de la sinusoïde

Il est instructif de rechercher les tendances de l'évolution des valeurs estimées par apc en fonction de la durée D du signal sinusoïdal. Le but est de produire une figure montrant comment les résultats de l'estimation évoluent avec la longueur du signal.

A cet effet, dans la cellule de code ci-dessous, l'idée est d'initialiser un vecteur dur (un numpy.array de dimension 1) contenant des valeurs croissantes de la longueur D du signal et de son support temporel.

Ensuite, on utilisera une boucle for d'indice n pour construire des vecteurs t et s de longueur croissante, t variant entre 0 et une durée variable contenue dans Dur[n]. Dans la même boucle, on applique l'estimateur apc et on stocke les valeurs estimées dans deux vecteurs distincts, qui contiendront toutes les valeurs estimées de AestA_{est} et de ϕEst\phi_{Est}, qu'on pourra tracer ensuite dans une figure à deux graphes.

Pour fixer les idées, vous pouvez utiliser les paramètres suivants :

  • Paramètres de la sinusoïde : (A,f0,ϕ)=(3,1,π/2)(A,f_0,\phi)=(3,1,-\pi/2)

  • Un vecteur D contenant des valeurs croissantes entre 0.25 et DMax=10sD_{Max}=10 s

import numpy as np D = np.arange(0.25, 10, 0.01) A_estimation = [] Phi_estimation = [] A = 3 f_0 = 1 phi = -np.pi/2 for n in D: t = np.linspace(0, n, 1000) s = A*np.cos(2*np.pi*f_0*t + phi) A_prime, Phi_prime = apc(t,s,f_0) A_estimation.append(A_prime) Phi_estimation.append(Phi_prime) plt.plot(D, A_estimation) plt.ylim(A,A*1.01) plt.show() plt.plot(D, Phi_estimation) plt.show()
--------------------------------------------------------------------------- NameError Traceback (most recent call last) /tmp/ipykernel_227/2591129225.py in <cell line: 8>() 9 t = np.linspace(0, n, 1000) 10 s = A*np.cos(2*np.pi*f_0*t + phi) ---> 11 A_prime, Phi_prime = apc(t,s,f0) 12 A_estimation.append(A_prime) 13 Phi_estimation.append(Phi_prime) NameError: name 'apc' is not defined

Le résultat devrait faire apparaître des oscillations ainsi qu'une décroissance asymptotique de l'erreur des estimations fournies par apc. En vous basant sur l'examen de la figure, répondez aux questions suivantes :

Q6 : Pour quelles valeurs (relatives à f0f_0) les estimations de la phase et de l'amplitude égalent-elles les valeurs vraies des paramètres ? De ce fait, proposez une modification de la condition sur la durée (qui doit être égale à un multiple entier de la période T0=1f0T_0=\frac{1}{f_0}) pour obtenir une estimation exacte.

Q6-R (votre réponse ici) : Les multiples de 1 / f0 sont égalent au vrais valeursD = n*T0

Q7 : A partir de quelle durée l'estimation de l'amplitude diffère-t'elle de moins de 1% de la valeur vraie ?

Indication : L'instruction plt.ylim(A,A*1.01) vous permet de restreindre l'échelle en y entre les deux valeurs spécifiées.

Q7-R (votre réponse ici) : A partir de 9 secondes

Q8 : On considérant l'allure des courbes, pouvez-vous dire si on on a en moyenne plus de chances d'obtenir une valeur estimée en excès (supérieure à la valeur vraie) ou une valeur estimée par défaut (inférieure à la valeur vraie) ?

Q8-R (votre réponse ici) : Nous avons plus de chances d'obtenir une valeur estimée en excès

Effet d'une constante ajoutée à s[n]s[n]

  • Dans la cellule ci-dessous, générez quelques périodes d'une sinusoïde discrète d'amplitude 1 à laquelle vous ajouterez une constante réelle CC (positive ou négative). Affichez le résultat dans une figure.

  • Testez le comportement de votre fonction apc sur ce signal x[n]=C+s[n]x[n]=C+s[n]. Vous testerez plusieurs valeurs de la constante pour valider vos observations et votre conclusion.

t = np.linspace(0,20, 2000) x = np.cos(2*np.pi*1*t+2)+10000 plt.plot(t,x) A,phi = apc(t,x,1) print(A,phi)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) /tmp/ipykernel_227/3042458683.py in <cell line: 3>() 1 t = np.linspace(0,20, 2000) 2 x = np.cos(2*np.pi*1*t+2)+10000 ----> 3 plt.plot(t,x) 4 A,phi = apc(t,x,1) 5 print(A,phi) NameError: name 'plt' is not defined

Q9 : Dans quelle mesure l'estimation réalisée par apcapc est-elle affectée par l'addition d'une valeur constante ?

Q9-R (votre réponse ici) :


IV-4 : Compléments : Autres tests de robustesse de la fonction d'estimation apc

Vous n'aurez probablement pas le temps d'effectuer ces exercices de compléments lors de la séance de TP en présentiel. Nous vous conseillons pourtant vivement d'essayer de les résoudre par vous-même, car ils sont très instructifs, et qu'ils inspireront certainement les questions de l'examen de TP terminal.

Effet d'une incertitude sur la fréquence f0f_0

La fréquence f0f_0 de la sinusoïde est connue avec une certaine précision. Dans cette dernière partie, que vous travaillerez en devoir, le but est de tester la validité de l'estimation de l'amplitude et de la phase lorsque la fréquence supposée connue ftf_t, celle que l'on fournit à apc, est légèrement différente de la véritable f0f_0.

Dans la cellule ci-dessous, à l'aide d'une boucle for, générez un graphe qui permet de visualiser la valeur de l'erreur (absolue ou relative) sur les paramètres estimés AestA_{est} et ϕEst\phi_{Est} en fonction de la valeur de la fréquence ftf_t fournie à apc ; on maintiendra ftf_t à ±25%\pm 25\% de sa valeur vraie f0f_0.

Q10 : Dans quelle plage de fréquence l'erreur relative sur l'estimation de l'amplitude reste-t'elle inférieure à 40% ?

Q10-R (votre réponse ici) :

Image in a Jupyter notebook

Effet d'un bruit additif gaussien

Si apc devait être utilisée pour estimer phase et amplitude de signaux réels (issus de capteurs ou d'un canal de communication), il y a de fortes chances que ces signaux soient affectés d'un bruit plus ou moins intense.

On peut très facilement simuler un bruit additif gaussien en ajoutant à chaque échantillon du signal des valeurs aléatoires réparties selon une loi normale, i.e. une gaussienne de moyenne 00 et de variance σ2=1\sigma^2=1. En Python, l'instruction nois=np.random.randn(N) génère un vecteur nois comprenant N valeurs (pseudo-)aléatoires distribuées selon la loi normale. On admettra que la puissance de ce bruit est donnée par la variance σ2\sigma^2. Pour obtenir un bruit additif gaussien de puissance PNP_N, il suffira de multiplier nois par la valeur PNP_N.

Le rapport signal-sur-bruit (en abrégé SNR pour Signal-to-Noise ratio) quantifie en décibels l'amplitude relative du signal utile et du bruit. Il s'exprime ici : SNRdB=10log10PSignalPNoise=10log10A2σ2 SNR_{dB}= 10 \log_{10}\frac{P_{Signal}}{P_{Noise}} = 10 \log_{10}\frac{A²}{σ²} AA est l'amplitude de la sinusoïde s[n]s[n].

  • Dans la cellule ci-dessous, générez quelques périodes d'une sinusoïde discrète d'amplitude 1 affectée d'un bruit additif gaussien de puissance PN=1P_N=1, et affichez le résultat dans une figure. Répétez le processus pour les puissances croissantes PN=2,5,20P_N={2,5,20}, et affichez le résultat dans une unique figure (Indication : utilisez plt.subplot).

  • Testez le comportement de votre fonction apc sur une sinusoïde d'amplitude fixée à 11 et une puissance de bruit prenant les valeurs successives PN=1,2,5,20P_N={1,2,5,20}. Stockez le résultat des estimations successives de l'amplitude et de la phase dans deux vecteurs, et montrez dans une seconde figure leur évolution en fonction de PNP_N.


Conclusions personnelles

Indiquez ci-dessous le temps approximatif que vous passé à travailler ce TP en-dehors de la séance.

Q11-R (votre réponse ici) : 5h

Ecrivez ci-dessous, en guise de conclusions, quelques phrases décrivant ce que ce TP vous a appris.

Q12 - R : Conclusions personnelles (votre réponse ici) : je n'ai pas bien compris