È davvero possibile fare come suggerisci, ma non senza alcuni effetti collaterali. Supponiamo che formiamo un semplice segnale di provas ( t ) =Sl o w( t ) +Sh i g h( t ) dove:
Sl o w( t ) = cos( 2 πf0t ) + cos( 2 πf1t +π3)
Sh i g h( t ) =12⋅ cos( 2 πf2t + 0,2 )
dove diciamo che entrambi f0,f1 sono al di sotto di una frequenza di taglio passa basso scelta fc u t tale che f0<f1<fc u te scegliamo f2>fc u t. Naturalmente possiamo scegliere le ampiezze che desideriamo e ho appena scelto quelle sopra per mantenere le cose semplici. Avendo due contributi di frequenza al di sotto della frequenza di taglio e uno sopra, è facile seguire e confrontare i segnali.
Nel seguito presumo che abbiamo N campioni prelevati con la frequenza fS> 2 ⋅f2. In realtà scegliamofS≫ 2 ⋅f2per rendere uniforme il segnale osservato. Si presume inoltre che prendiamo in considerazione solo un gruppo di campioni di dati. Se è necessario gestire diversi intervalli di tempo, consultare il documento di Fred Harris intitolato "Sull'uso di Windows per l'analisi armonica con la trasformata discreta di Fourier" di Proc. IEEE nel 1978.
Ho combinato un piccolo programma Python per illustrare alcuni dei concetti: il codice è piuttosto orribile, ma ho appena preso del vecchio codice che avevo per problemi simili. Sebbene non ci siano quasi commenti, dovrebbe essere abbastanza facile da seguire a causa dei piccoli moduli. Ci sono due DFT / IDFT funzioni; due funzioni fshiftn / fshiftp per spostare la frequenza del segnale nel dominio DFT per il filtraggio; una funzione dftlpass per eseguire il filtraggio nel dominio DFT; una funzione zpblpass per fare il filtraggio usando un filtro Butterworth; una funzione bbdftsig per formare il segnale di prova ed eseguire il filtraggio; ed infine una piccola funzione plotsigsper tracciare i segnali. Alla fine dello script, vengono impostati i diversi parametri e vengono realizzate le diverse figure.
"""
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)
scelta N= 5000 e fS= 1000 ci dà una risoluzione in frequenza di fS/ N= 0,2Hz. Se scegliamof0,f1,f2in base a ciò possiamo ottenere un perfetto accordo scegliendo le frequenze come mostrato sopra. Se scegliamo prima le frequenze che sono sulla griglia comef0= 3, f1= 11, f2= 21 e noi abbiamo fc u t= 15otteniamo la prima serie di risultati. Di seguito sono mostrate la prima, la parte centrale e l'ultima dei segnali pertinenti:
Come si vede dalla figura, abbiamo l'input combinato Scome segnale magenta; il segnale verde come possiamo vedere solo dai segni "x" èSl ow(il segnale di input non elaborato quando includiamo semplicemente il segnale di input al di sotto della frequenza di taglio); il segnale rosso è quello che otteniamo usando il filtro DFT; e il segnale blu è quello che otteniamo dal filtro Butterworth. Come visto sopra, otteniamo un perfetto accordo traSl o w e il segnale filtrato DFT - ma il filtro Butterworth ha un certo impatto sul segnale in banda (in particolare il componente a f1. Come è abbastanza tipico per questo tipo di elaborazione, abbiamo alcune differenze all'inizio e alla fine della sequenza a causa di effetti di margine e di ragionevole accordo tra entrambi i tipi di filtro nella sezione centrale.
Se cambiamo la frequenza f1 per f1= 11.5 che non si trova sulla griglia di frequenza (e inoltre è abbastanza vicino alla frequenza di taglio) vediamo alcuni risultati diversi come mostrato di seguito.
Ora vediamo differenze sostanziali tra segnali verdi, blu e rossi che nella situazione ideale dovrebbero essere identici. Nel mezzo del segnale sono tutti abbastanza d'accordo: il DFT e il riferimentoSl o w d'accordo comunque.
Quindi, in conclusione, è possibile utilizzare il filtraggio diretto forzando a zero i coefficienti di Fourier, cosa che a volte viene eseguita anche nel rilevamento a compressione per ridurre il supporto di un segnale per forzare la scarsità su un segnale. Tuttavia, ci sono conseguenze di ciò come un aumento degli errori, in particolare ai bordi del segnale. Inoltre, quanto sopra è il caso migliore in cui l'intero segnale viene trattato come una sequenza. Se il segnale deve essere suddiviso in intervalli di tempo, diventa complicato in quanto è necessario considerare alcune finestre o altre tecniche per garantire la continuità del segnale tra i frame. Quindi il mio consiglio è simile ad alcuni degli altri post nel raccomandare di usare normalmente un Butterworth / Elliptic / .. o qualunque altro filtro.