Il est en effet possible de faire comme vous le suggérez mais non sans quelques effets secondaires. Disons que nous formons un signal de test simples ( t ) =sl o w( t ) +sh i g h( t ) où:
sl o w( t ) = cos( 2 πF0t ) + cos( 2 πF1t +π3)
sh i g h( t ) =12⋅ cos( 2 πF2t + 0,2 )
où nous disons que les deux f0,f1 sont en dessous d'une fréquence de coupure passe-bas choisie fcut tel que f0<f1<fcut, et nous choisissons f2>fcut. Nous pouvons bien sûr choisir les amplitudes comme nous le souhaitons et je viens de choisir celles ci-dessus pour garder les choses simples. En ayant deux contributions de fréquence en dessous de la fréquence de coupure et une au-dessus, il est facile de suivre et de comparer les signaux.
Dans ce qui suit, je suppose que nous avons N échantillons prélevés avec la fréquence fs>2⋅f2. En réalité, nous choisissonsfs≫2⋅f2pour rendre le signal observé lisse. On suppose également que nous ne considérons qu'un seul groupe d'échantillons de données. Si vous devez gérer plusieurs délais, consultez le document de Fred Harris intitulé "Sur l'utilisation de Windows pour l'analyse harmonique avec la transformation de Fourier discrète" de Proc. IEEE en 1978.
J'ai combiné un petit programme Python pour illustrer certains des concepts - le code est assez horrible mais je viens de prendre du vieux code que j'avais pour des problèmes similaires. Bien qu'il n'y ait pratiquement aucun commentaire, il devrait être assez facile à suivre en raison des petits modules. Ce sont deux fonctions dft / idft ; deux fonctions fshiftn / fshiftp pour décaler en fréquence le signal dans le domaine DFT pour le filtrage; une fonction dftlpass pour effectuer le filtrage dans le domaine DFT; une fonction zpblpass pour effectuer le filtrage en utilisant un filtre Butterworth; une fonction bbdftsig pour former le signal de test et effectuer le filtrage; et enfin une petite fonction tramepour tracer les signaux. A la fin du script, les différents paramètres sont définis et les différentes figures sont faites.
"""
Test of DFT versus scipy.signal.butter filtering with respect to
signal reconstruction.
"""
# import ############################################################ import #
import matplotlib as mpl; mpl.rcParams['backend'] = 'Agg'
import matplotlib.pyplot as mplpp
import matplotlib.mlab as mplml
import numpy as np
import scipy.signal as sps
# initialize #################################################### initialize #
try:
mpl.rc('text', usetex=False)
mpl.rc('font', family='serif')
mpl.rc('font', serif='STIXGeneral')
mpl.rc('font', size=8)
except AttributeError:
None
# dft ################################################################## dft #
def dft(xt, fs, t0):
N, d = len(xt), -2j*np.pi/len(xt)
w = np.arange(N, dtype=np.float).reshape((N,1))
c = np.exp(d*t0*fs*w)
W = np.exp(d*np.dot(w,np.transpose(w)))
xf = np.multiply(c,np.dot(W,xt)) / float(N)
f = w*fs/float(N)
return xf, f
# idft ################################################################ idft #
def idft( X, FS, T0 ):
N, d = len(X), 2j*np.pi/len(X)
w = np.arange(N, dtype=float).reshape((N,1))
cc = np.exp(d*T0*FS*w)
Wc = np.exp(d*np.dot(w, np.transpose(w)))
Y = np.dot(Wc, np.multiply(cc, X))
return Y
# fshiftn ########################################################## fshiftn #
def fshiftn( xf, f ):
assert type(f) == np.ndarray, "f must be a np.ndarray"
assert f.shape[1] == 1, "f must be a column array"
assert xf.shape[1] == 1, "xf must be a column array"
assert sum(f<0) == 0, "All frequency components must be 0 or positive"
# Determine sampling rate, tolerance, and allocate output array
fs, tol = len(f)*(np.abs(f[1,0]-f[0,0])), 1.E-2
fshift = np.zeros((len(f),1), dtype=float)
xfshift = np.zeros((len(f),1), dtype=complex)
# Determine index where f > fs/2
Nm = np.floor(len(f)/2.0)
Np = np.floor((len(f)-1.0)/2.0)
# Compute output frequency array such that -fs/2 <= f < fs/2 and the
# corresponding Fourier coefficients
fshift[:Nm,0] = f[Np+1:,0] - fs
fshift[Nm,0] = f[0,0]
fshift[Nm+1:,0] = f[1:Np+1,0]
xfshift[:Nm,0] = xf[Np+1:,0]
xfshift[Nm,0] = xf[0,0]
xfshift[Nm+1:,0] = xf[1:Np+1,0]
return xfshift, fshift
# fshiftp ########################################################## fshiftp #
def fshiftp(xf, f):
assert type(f) == np.ndarray, "f must be a np.ndarray"
assert f.shape[1] == 1, "f must be a column array"
assert xf.shape[1] == 1, "xf must be a column array"
assert sum(f<0) > 0, "Some input frequencies must be negative"
# Determine sampling rate, tolerance, and allocate output array
fs, tol = len(f)*(np.abs(f[1,0]-f[0,0])), 1.E-2
fshift = np.zeros((len(f),1), dtype=float)
xfshift = np.zeros((len(f),1), dtype=complex)
# Determine index where f > fs/2
#Nx = np.floor((len(f)+1+tol)/2)
Nm = np.floor(len(f)/2.0)
Np = np.floor((len(f)-1.0)/2.0)
# Compute output frequency array such that -fs/2 <= f < fs/2 and the
# corresponding Fourier coefficients
fshift[Np+1:,0] = f[:Nm:,0] + fs
fshift[0,0] = f[Nm,0]
fshift[1:Np+1:,0] = f[Nm+1:,0]
xfshift[Np+1:,0] = xf[:Nm:,0]
xfshift[0,0] = xf[Nm,0]
xfshift[1:Np+1:,0] = xf[Nm+1:,0]
return xfshift, fshift
# dftlpass ######################################################## dftlpass #
def dftlpass(xt, fs, fcut):
# Perform Discrete Fourier Transform
xf, f = dft(xt, fs, 0.0)
# Shift frequencies to -fs/2 <= f < fs/2 ... and coefficients
xfshift, fshift = fshiftn(xf, f)
# Perform filtration
xfshift = xfshift * (np.abs(fshift) <= fcut)
# Re-shift frequencies to 0 <= f < fs ... and coefficients
xfrecon, frecon = fshiftp(xfshift, fshift)
# Perform inverse Discrete Fourier Transform
yt = idft(xfrecon, fs, 0.0)
return yt.real
# zpblpass ######################################################## zpblpass #
def zpblpass(xn, fcal, fs, fcut):
bz, az = sps.butter(5, fcut/(fs/2))
# Gain calibration
Ncal = np.max([np.int(20*fs/fcal), 30000])
Nguard = np.int(0.1*Ncal)
t = np.arange(Ncal) / fs
x0_cal = 1.0 * np.cos(2*np.pi*fcal*t)
yi_cal = sps.filtfilt(bz, az, 2.0*x0_cal*np.cos(2*np.pi*fcal*t))
k = 1.0/np.mean(yi_cal[Nguard:Ncal-Nguard])
# Scaled output
yn = k * sps.filtfilt(bz, az, xn)
return yn
# bbdftsig ######################################################## bbdftsig #
def bbdftsig(f0, f1, f2, fcut, fs, N):
t = np.arange(N).reshape((N,1)) / fs
s0 = np.sin(2*np.pi*f0*t)
s1 = np.sin(2*np.pi*f1*t + 0.2)
s2 = 0.7 * np.sin(2*np.pi*f2*t + np.pi/3.0)
slow = s0 + s1
s = slow + s2
sf = dftlpass(s, fs, fcut)
sfdftv = sf.reshape((N))
sv = s.reshape((N))
slowv = slow.reshape((N))
sv = s.reshape((N))
sfzpbv = zpblpass(sv, f1, fs, fcut)
#sfzpbv = sfzpb.reshape((N))
return sv, slowv, sfdftv, sfzpbv
# plotsigs ######################################################## plotsigs #
def plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname):
n = np.arange(s.shape[0])
# Plot results
mplpp.figure(1, (5.0,2.25))
mplpp.clf()
mplpp.plot(n[Nstart:Nstop], s[Nstart:Nstop], 'm-',
n[Nstart:Nstop:4], s[Nstart:Nstop:4], 'mx',
n[Nstart:Nstop], slow[Nstart:Nstop], 'g-',
n[Nstart:Nstop:10], slow[Nstart:Nstop:10], 'gx',
n[Nstart:Nstop], sfdft[Nstart:Nstop], 'r-',
n[Nstart:Nstop:15], sfdft[Nstart:Nstop:15], 'rx',
n[Nstart:Nstop], sfzpb[Nstart:Nstop], 'b-',
linewidth=1.5)
mplpp.legend([r'$s$', r'$s$', r'$s_{\rm low}$', r'$s_{\rm low}$',
r'DFT', r'DFT', r'ZPB'], loc='upper right')
mplpp.ylabel(r'Signal')
mplpp.xlabel(r'$n$')
#mplpp.axis([-10.0, 10.0, 1.0E-2, 1.0E2])
mplpp.grid(True)
mplpp.savefig(fname, dpi=600,
bbox_inches='tight', pad_inches=0.05)
mplpp.close()
# __main__ ######################################################## __main__ #
if __name__ == '__main__':
# Initialize
f0 = 3.0
f1 = 11.5
f2 = 20.0
fcut = 15.0
fs = 1000.0
N = 5000
s, slow, sfdft, sfzpb = bbdftsig(f0, f1, f2, fcut, fs, N)
n = np.arange(s.shape[0])
# Fig. 1: full data set
Nstart = 0
Nstop = N
fname = 'full.pdf'
plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname)
# Fig. 2: beginning
Nstart = 0
Nstop = 150
fname = 'beginning.pdf'
plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname)
# Fig. 3: middle
Nstart = np.floor(N/2.0) - 75
Nstop = Nstart + 100
fname = 'middle.pdf'
plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname)
# Fig. 4: ending
Nstart = N - 150
Nstop = N
fname = 'ending.pdf'
plotsigs(s, slow, sfdft, sfzpb, Nstart, Nstop, fname)
Choisir N=5000 et fs=1000 nous donne une résolution en fréquence de fs/N=0.2Hz. Si nous choisissonsf0,f1,f2selon cela, nous pouvons obtenir un accord parfait en choisissant les fréquences comme indiqué ci-dessus. Si nous choisissons d'abord les fréquences qui sont sur la grille commef0=3, f1=11, f2=21 et nous avons fcut=15nous obtenons la première série de résultats. Les première, moyenne et dernière parties des signaux concernés sont présentées ci-dessous:
Comme le montre la figure, nous avons l'entrée combinée scomme signal magenta; le signal vert comme nous ne pouvons le voir que sur les marquages «x» estslow(le signal d'entrée brut lorsque nous incluons simplement le signal d'entrée en dessous de la fréquence de coupure); le signal rouge est celui que nous obtenons lors de l'utilisation du filtrage DFT; et le signal bleu est celui que nous obtenons du filtre Butterworth. Comme vu ci-dessus, nous obtenons un accord parfait entreslow et le signal filtré DFT - mais le filtre Butterworth a un certain impact sur le signal dans la bande (en particulier le composant à f1. Comme c'est assez typique pour ce type de traitement, nous avons quelques différences au début et à la fin de la séquence en raison des effets de bord et d'un accord assez bon entre les deux types de filtrage dans la section centrale.
Si nous changeons la fréquence f1 à f1=11.5 qui n'est pas sur la grille de fréquence (et en plus elle est assez proche de la fréquence de coupure), nous voyons des résultats différents comme indiqué ci-dessous.
Nous voyons maintenant des différences substantielles entre les signaux vert, bleu et rouge qui, dans la situation idéale, devraient être identiques. Au milieu du signal, ils sont tous assez bien d'accord - la DFT et la référenceslow d'accord mieux cependant.
Ainsi, en conclusion, il est possible d'utiliser le filtrage direct en forçant les coefficients de Fourier à zéro, ce qui est également parfois fait en détection compressive pour réduire le support d'un signal pour forcer la rareté sur un signal. Cependant, cela a des conséquences comme une augmentation des erreurs, en particulier aux bords du signal. En outre, ce qui précède est un meilleur cas où le signal entier est traité comme une séquence. Si le signal doit être divisé en intervalles de temps, cela devient compliqué car nous devons alors envisager un fenêtrage ou une autre technique pour assurer la continuité du signal entre les images. Donc, mon conseil est similaire à certains des autres articles en recommandant d'utiliser normalement un filtre Butterworth / Elliptic / .. ou autre.