I'm trying to build a "perfect" low-pass filter as an exercise to test that I've understood some basics in signal processing.

I've computed the required length of a sinc function such that at either end the value is 0 for a given integer bit depth of output. For example, 16-bits would require a filter that is 41722 taps.

I'm then applying this filter to a frequency sweep file sampled at 48kHz, with Fc set at 0.5, thus it should act as a low-pass filter with the cutoff frequency at 12kHz.

When I feed this filter some data I get unexpected results. For the 16-bit filter, the low-pass effect is clear, but there is data beyond 12kHz as well as other corruptions. For a 20-bit filter, 667543 taps, the output is totally wrong.

In the 16-bit case, applying a blackman window to the sinc function improves the output and makes it look correct, but this doesn't help in the 20-bit case.

I've attached a couple of images to illustrate the problem. I'm fairly certain that my sinc function is generating correct data. I'm using vDSP_convD to apply the filter (I've also tested with a handwritten function, with no change.)

Am I misunderstanding how a sinc function can be used as a low-pass filter, or am I running into other problems?

This is the output of the 16-bit, 41722 tap filter: 16-bit result

This is the output of the 20-bit, 667543 tap filter: enter image description here

This a graph of -200..200 on the 41722 tap filter:enter image description here

This is the frequency part of the FFT of the centre of the 41722 tap filter.enter image description here

  • $\begingroup$ I suppose despite the argument about filter length and bit depth, you compute the filter coefficients in floating point precision and you also implement the convolution in float, right? With a sampling frequency of 48 kHz, your Nyquist frequency is 24 kHz, and a cut-off frequency of $0.5$ would correspond to 12 kHz (not to 24 kHz as stated in the question). This is also what I think I can see in the figures. $\endgroup$ – Matt L. Feb 27 '17 at 10:36
  • 3
    $\begingroup$ I would suggest you try with considerably smaller filter lengths and see what you get. It might well be that a filter with about 700.000 coefficients can't be implemented in a straightforward way without substantial numerical errors, even in floating point. But I'm also not too sure how to interpret your second figure. $\endgroup$ – Matt L. Feb 27 '17 at 10:39
  • $\begingroup$ Also have a look at what the impulse response looks like. Input one sample valued 1 and the rest of the samples valued 0. $\endgroup$ – Olli Niemitalo Feb 27 '17 at 14:27
  • $\begingroup$ Matt L. Smaller filter lengths don't produce the two parallel sweeps. For example, 10k taps just looks like it isn't doing much filtering. Doubling the length to 80k makes the parallel sweeps appear further away from the central. $\endgroup$ – Tim Feb 27 '17 at 17:28
  • 2
    $\begingroup$ If 10k taps don't do much filtering then there must be something wrong with your filter. How does the impulse response look like? Did you shift the sinc function such that its maximum is at the center tap? $\endgroup$ – Matt L. Feb 27 '17 at 17:58

A frequency sweep has non-zero bandwidth over the length of any FIR filter. Use a long enough filter (compared to the sweep rate) and nearly half the spectrum of the sweep could end up on each side of the cutoff.

Also, use a narrow enough transition band, and you might end up with Gibbs horn ringing right near the edge of the transition frequency.

  • 1
    $\begingroup$ I'm not sure what your first paragraph means. I'm still very much a newbie at all this. As for the second paragraph, with a sinc filter the transition bandwidth is determined by the length of the filter. Does this mean that using a really long sinc filter to create a perfect low-pass filter is a fundamentally flawed idea? $\endgroup$ – Tim Feb 28 '17 at 2:26

I'm not sure how you quantize the coefficients or the signals or if you do it at all, so I'm not considering coefficient quantization.

You are cutting off the tails of the sinc by using just the middle portion of it as the impulse response. This creates a wide-band discontinuity transient at the beginning and at the end of the impulse response. The two transients appear in the output spectrograms as the faint unfiltered copies of the sweep. For the 16-bit example those are at about -100 dB compared to the main sweep, which is very much acceptable if you were to quantize the signal to 16 bits. The spectrograms have been created using an analysis window that is shorter than the lengths of your impulse responses, so the artifacts created by the discontinuities can be observed in the spectrograms as separate entities.

If you want to get rid of the ghost sweeps, a solution is to multiply the sinc function by some window function that begins and ends at zero, optionally with also some of its early derivatives zero at those points. This way you don't need as long impulse responses for the impulse response to start and end smoothly.

The horizontal spreading where the sweep hits the cutoff frequency is also explained by the short analysis window used in the spectrogram. Locally a piece of the tail of the sinc looks like a sinusoid of the same frequency as the cutoff frequency.

Altogether, everything looks as expected.

  • $\begingroup$ Thanks for the explanation. This makes sense to me in the 16-bit case. That analysis doesn't seem to apply to the 20-bit case however, there it looks like the filter has somehow shifted the signal... $\endgroup$ – Tim Feb 28 '17 at 17:51
  • $\begingroup$ vDSP_convD doesn't seem to have a mechanism for setting the filter's "middle tap". It just assumes that the filter is anticausal. You can pad the beginning of the input sweep with zeros ($N$ zeros for $2N+1$ taps) to add enough delay to bring the output sweep into its proper place. $\endgroup$ – Olli Niemitalo Feb 28 '17 at 18:26

A sweep is not a bandlimited signal, so if you sample it with 48kHz without putting it through an "analog" (preferably analytical) 24kHz lowpass first, you will get aliasing.

As long as your sweep speed is slow compared to the filter length, you won't see much aliasing. But with the long filter lengths you aim for, this is likely to become a contributing factor.

So see whether slowing the sweep down improves your results.

  • $\begingroup$ There's no aliasing. If there would be, the "ghost sweeps" would ping pong from the 24 kHz ceiling. $\endgroup$ – Olli Niemitalo Feb 28 '17 at 11:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.