Les coefficients de Fourier d'un signal créneau sont :
La série de Fourier s'écrit :
Les coefficients de Fourier sont définis par une fonction :
import math,cmath import numpy as np from matplotlib.pyplot import * def cnCreneau(n): if n==0: return 0 else: return -1j/(n*math.pi)*(1-(-1)**n)
Tracé du spectre en décibel :
def plotSpectre(cn,nmax): indices = np.arange(start=1,stop=nmax,step=2) n = indices.size spectre = np.zeros(n) for k in range(n): spectre[k] = 20*math.log10(abs(cn(indices[k]))) stem(indices,spectre) xlabel('n') ylabel('AdB') grid() figure(figsize=(8,4)) plotSpectre(cnCreneau,20)figA.pdf
La fonction suivante calcule la somme partielle de la série de Fourier, avec une période 1 :
def serie(cn,nmax,t): s = 0.0 for k in range(1,nmax): z = cn(k)*cmath.exp(1j*2*math.pi*k*t) s += 2*z.real return s
Tracé de la somme partielle à N=20 :
def plotSomme(cn,nmax,step): t = np.arange(start=0.0,stop=2.0,step=step) n = t.size u = np.zeros(n) for k in range(n): u[k] = serie(cn,nmax,t[k]) plot(t,u) xlabel('t') ylabel('u') grid() figure(figsize=(8,4)) plotSomme(cnCreneau,20,0.001)figB.pdf
Cette somme partielle est très éloignée du signal créneau. Essayons N=100 :
figure(figsize=(8,4)) plotSomme(cnCreneau,100,0.001)figC.pdf
Il y a des oscillations à l'approche des discontinuités (phénomène de Gibbs). On peut essayer d'augmenter encore N, mais il faut aussi augmenter la fréquence d'échantillonnage du tracé car les oscilations sont de plus en plus rapides.
figure(figsize=(8,4)) plotSomme(cnCreneau,1000,0.0001)figD.pdf
Cela est très couteux en calculs car il y 1000x1000 termes à calculer.
On peut aussi obtenir le signal en appliquant la transformée de Fourier discrète inverse au spectre. Dans ce cas, un spectre à 1000 termes donne 1000 échantillons, ce qui ne permet pas de restituer les oscillations :
import numpy.fft def calculerSignal(cn,nmax): indices = np.arange(start=0,stop=nmax,step=1) n = indices.size spectre = np.zeros(n,dtype=numpy.complex) for k in range(n): spectre[k] = cn(indices[k]) u = numpy.fft.ifft(spectre)*nmax return u u = calculerSignal(cnCreneau,1000) figure(figsize=(8,4)) plot(u) grid()figE.pdf
Le calcul est beaucoup plus rapide (algorithme FFT) mais le résultat correspond à un signal dont les hautes fréquences ont été atténuées, ce qui fait disparaitre le phénomène de Gibbs.
On effectue un filtrage dans le domaine fréquentiel, c'est-à-dire en agissant sur les coefficients de Fourier. En pratique, le filtrage numérique du signal dans les sytèmes temps-réel se fait dans le domaine temporel (par convolution). Voir à ce sujet la page Filtre à réponse impulsionnelle finie..
Le filtre est défini par une fonction de transfert de la forme H(f), f étant la fréquence. Dans le cas présent, les fréquences sont définies par rapport à la fréquence du signal, qui vaut 1 par convention.
La fonction suivante calcule les coefficients de Fourier résultant du filtrage, en les multipliant par la valeur de la fonction de transfert aux fréquences correspondantes. La fréquence de l'harmonique de rang n est simplement n puis la fréquence du fondamental est 1.
def filtrage(H,cn_in): def cn_out(n): return H(1.0*n)*cn_in(n) return cn_out
La fonction de transfert d'un filtre passe-bas du premier ordre est :
où la variable s est définie par :
Pour une fréquence de coupure de 10 :
def H(f): x = f/10 s = 1j*x return 1.0/(1+s)
Effet du filtre sur le créneau :
cnSortie = filtrage(H,cnCreneau) figure(figsize=(8,4)) plotSomme(cnSortie,100,0.001)figF.pdf
On voit que la série de Fourier peut être arrêtée au rang 100 en raison de l'atténuation des hautes fréquences; le phénomène de Gibbs est à peine visible.
Dans le cas suivant, toutes les fréquences du créneau sont dans le domaine intégrateur du filtre :
def H(f): x = f/0.1 s = 1j*x return 1.0/(1+s) cnSortie = filtrage(H,cnCreneau) figure(figsize=(8,4)) plotSomme(cnSortie,100,0.001)figG.pdf
Un filtre de Butterworth d'ordre n a le gain suivant (à une constante multiplicative près) :
Le gain présente donc une décroissance de -20n dB par décade pour x>1.
Voir la page Filtres passe-bas de Butterworth pour le calcul de la fonction de transfert et pour un exemple de réalisation électronique.
La fonction de transfert d'un filtre passe-bas de Butterworth d'ordre 2p est :
avec :
def butterworth(f,fc,p): s = 1j*f/fc h = 1.0 n = 2*p for k in range(p): c = 2*math.sin(math.pi/n*(k+0.5)) h = h*(s**2+c*s+1) return 1.0/h
Diagramme de Bode d'un filtre de Butterworth d'ordre 2 :
def H(f): return butterworth(f,1,1) def reponseFrequentielle(H,start,stop): freq = np.logspace(start=start,stop=stop,num=1000) n = freq.size gdb = np.zeros(n) phi = np.zeros(n) for k in range(n): h = H(freq[k]) gdb[k] = 20*math.log10(abs(h)) phi[k] = cmath.phase(h) return [np.log10(freq),gdb,phi] [logf,gdb,phi] = reponseFrequentielle(H,-2,2) figure(figsize=(8,4)) plot(logf,gdb) xlabel('log(f)') ylabel('GdB') grid()figH.pdf
figure(figsize=(8,4)) plot(logf,phi) xlabel('log(f)') ylabel('phi') grid()figI.pdf
Voici l'effet sur le spectre d'un signal en créneaux :
cn_out = filtrage(H,cnCreneau) figure(figsize=(8,4)) plotSpectre(cn_out,20)figJ.pdf
et sur sa représentation temporelle :
figure(figsize=(8,4)) plotSomme(cn_out,100,0.001) grid()figK.pdf
Le signal de sortie n'est pas très loin d'une sinusoïde. Comme on le voit sur le spectre, il y a environ 30 dB entre le fondamental et l'harmonique de rang 3.
La même chose avec un filtre de Butterworth d'ordre 4 :
def H(f): return butterworth(f,1,2) cn_out = filtrage(H,cnCreneau) figure(figsize=(8,4)) plotSpectre(cn_out,20)figL.pdf
figure(figsize=(8,4)) plotSomme(cn_out,100,0.001) grid()figM.pdf
L'écart entre le fondamental et l'harmonique de rang 3 est supérieur à 40 dB et la sortie est pratiquement sinusoïdale. Ce type de filtrage peut être utile pour transformer un signal numérique TTL en sinusoïde.