tags:

views:

90

answers:

1

Hi,

I've got a FFT magnitude spectrum and I want to create a filter from it that selectively passes periodic noise sources (e.g. sinewave spurs) and zero's out the frequency bins associated with the random background noise. I understand sharp transitions in the freq domain will create ringing artifacts once this filter is IFFT back to the time domain... and so I'm wondering if there are any rules of thumb how to smooth the transitions in such a filter to avoid such ringing.

For example, if the FFT has 1M frequency bins, and there are five spurs poking out of the background noise floor, I'd like to zero all bins except the peak bin associated with each of the five spurs. The question is how to handle the neighboring spur bins to prevent artifacts in the time domain. For example, should the the bin on each side of a spur bin be set to 50% amplitude? Should two bins on either side of a spur bin be used (the closest one at 50%, and the next closest at 25%, etc.)? Any thoughts greatly appreciated. Thanks!

+2  A: 

I like the following method:

  • Create the ideal magnitude spectrum (remembering to make it symmetrical about DC)
  • Inverse transform to the time domain
  • Rotate the block by half the blocksize
  • Apply a Hann window

I find it creates reasonably smooth frequency domain results, although I've never tried it on something as sharp as you're suggesting. You can probably make a sharper filter by using a Kaiser-Bessel window, but you have to pick the parameters appropriately. By sharper, I'm guessing maybe you can reduce the sidelobes by 6 dB or so.

Here's some sample Matlab/Octave code. To test the results, I used freqz(h, 1, length(h)*10);.

function [ht, htrot, htwin] = ArbBandPass(N, freqs)
%# N = desired filter length
%# freqs = array of frequencies, normalized by pi, to turn into passbands
%# returns raw, rotated, and rotated+windowed coeffs in time domain

if any(freqs >= 1) || any(freqs <= 0)
    error('0 < passband frequency < 1.0 required to fit within (DC,pi)')
end

hf = zeros(N,1); %# magnitude spectrum from DC to 2*pi is intialized to 0
%# In Matlabs FFT, idx 1 -> DC, idx 2 -> bin 1, idx N/2 -> Fs/2 - 1, idx N/2 + 1 -> Fs/2, idx N -> bin -1
idxs = round(freqs * N/2)+1; %# indeces of passband freqs between DC and pi
hf(idxs) = 1; %# set desired positive frequencies to 1
hf(N - (idxs-2)) = 1; %# make sure 2-sided spectrum is symmetric, guarantees real filter coeffs in time domain
ht = ifft(hf); %# this will have a small imaginary part due to numerical error
if any(abs(imag(ht)) > 2*eps(max(abs(real(ht)))))
    warning('Imaginary part of time domain signal surprisingly large - is the spectrum symmetric?')
end
ht = real(ht); %# discard tiny imag part from numerical error
htrot = [ht((N/2 + 1):end) ; ht(1:(N/2))]; %# circularly rotate time domain block by N/2 points
win = hann(N, 'periodic'); %# might want to use a window with a flatter mainlobe
htwin = htrot .* win;
htwin = htwin .* (N/sum(win)); %# normalize peak amplitude by compensating for width of window lineshape
mtrw
Thanks so much mtrw. I was thinking to hand tweak the FFT coefficients on neighboring bins, but it appears you're using the Hann window to achieve something similar (is that correct? the artifacts will appear in the time domain, and the Hann window will somehow improve it). In my case, the "ideal magnitude spectrum" would be a binary representation, where (continuing the above example), I'd have five ones and the rest zeros -- is this what you're recommending here as well? Also, I'm not sure what you mean by "rotate the block by half the blocksize". What is a block? Can you give an example?
ggkmath
Also, what's the advantage in centering the spectrum about DC?
ggkmath
Quick answers: 1. yes, I'm suggesting the window takes care of the neighboring bins. 2. Rotate the block means swap the first and second half of the time domain filter after the inverse FFT. Blocksize is the number of points in the filter. 3. You need to make sure the inverse FFT delivers real time-domain coefficients. The spectrum must be Hermitian symmetric (second N/2 points are complex conjugates of first N/2). Check your FFT documentation to see how they expect you to arrange the points. I'll create an example when I have some more time...
mtrw
Thanks mtrw. I have a waveform containing N real data points. I'm constructing a filter from the first 1...N/2 points, so filter length is M=N/2. Using Matlab's fft routine. Zeroing padding both filter and data arrays to M+N-1 to allow allow convolution space to consume. Only interested in steady-state transients, and considering the use of a window to help spurs stand out, but still debating this.
ggkmath
I added a code example that generate an N-point time domain filter whose frequency response has an arbitrary number of bins passing energy. But I have some questions: 1. I don't at all understand your comment about "steady-state transients". 2. Nor do I understand how you plan to use the filter. Generally, filters are shorter than the data to which they will be applied. What's the transition bandwidth you require?
mtrw
3. Are you planning to implement the filter using overlap-save FFT processing? If so, you might look at http://stackoverflow.com/questions/2929401/dsp-filtering-in-the-frequency-domain-via-fft and http://stackoverflow.com/questions/3775912/fourier-space-filtering. If not, you don't need to zero-pad anything. Using Matlab's built-in `filter` command will take care of these implementation details for you.
mtrw
Thanks mtrw, I'm not using overlap-save FFT processing. Just going to do it all in one shot. http://www.dspguide.com/ch9/3.htm (the 'frequency convolution' part of it, not overlap-save). The arrays are large, but I can fit it all in my RAM (thus avoiding overlap-save).
ggkmath
Oh, I understand the padding part now. And I guess since you're doing it in one shot, there's no downside to having a very long filter. So the only question I have left is what you mean by steady-state transients.
mtrw
I think it's a vocabulary issue - usually the ramp-up or ramp-down portions are referred to as the transients, while the middle part is called steady-state.
mtrw
Yes, I'm not very experienced in DSP, so I'm sure you're right about vocabulary here.
ggkmath
Hi mtrw, I'm not sure your use of array 'freqs'. Can you walk me through an example? I'm used to finding spurs in FFT and creating a spectrum where each spur freq bin has 1+0i FFT coeff (0+0i otherwise), then taking IFFT to get h(t). Not sure where you're beginning with array 'freqs'. thanks so much
ggkmath
Sorry for the delayed reply, I was traveling. If `freqs = [.25 .5 .75]`, for instance, you'll have passbands at a quarter, half, and 3/4s of the way through the spectrum. The relationship between normalized spectral distance and Hz depends on your sample frequency. Remember that the sampled spectrum maps to 0 to 2pi, where 2pi = FSample, and pi = FUseful. For my example, if the sampling frequency is 51200 Hz, the passbands will be at [.25 .5 .75]*25600 Hz. The bandwidth is controlled by the length of the filter and the window type.
mtrw
Hi mtrw, thanks, I'm using the code now and it looks very good. One question, which maybe a stupid question, is I was expecting the magnitude spectrum of array "htwin" to be 0 dB at frequencies corresponding to elements in "freqs" (i.e. pass band), but I'm seeing attenuation of around 1e-5 (ratio, not dB). I wonder if I supposed to normalize the amplitude somewhere (like the Hann window function)?
ggkmath
Yes, the window affects the amplitude. I added a calibration step, I had forgotten about that earlier. Now `freqz` reports 0 dB at the peak of the passband. But the attenuation you observe is bigger than I would have expected. Perhaps the one you're trying to identify is not quite in the passband? You might try using a window with a flatter lineshape. See http://en.wikipedia.org/wiki/Window_function#Flat_top_window, for instance.
mtrw