1
\$\begingroup\$

I have an ADC with 1 kS/s and would like to append a digital anti-aliasing filter to it as I have to downsample the data to 10 Hz.

As it's on an FPGA and I don't have any multipliers, I thought the easiest way would be a recursive averaging IIR filter:

$$ y = \alpha x[n] + (1-\alpha)y[n-1]$$

But how can I calculate the cutoff frequency of such a filter depending on \$\alpha\$ and \$f_s\$?

\$\endgroup\$
  • 1
    \$\begingroup\$ Makes me wonder where you learned how to make an IIR filter, if they didn't spend one line explaining that relationship. It's hardly useful without knowing the frequency. \$\endgroup\$ – pipe May 7 at 23:20
  • 3
    \$\begingroup\$ It's been a decade since then... people forget :) \$\endgroup\$ – po.pe May 8 at 5:38
6
\$\begingroup\$

With you needing an anti-alias filter for 1/100 of the bandwidth, I'd say that both your single-pole IIR (exponentially weighted moving average) and actual moving averages are out of the question; you'd need suppression in 99% of your band sufficiently high enough to mitigate aliases.

Simultaneously, you need a filter with a steep transition between pass- and stopband, relative to the original Nyquist rate.

Neither filter approaches will achieve such a steepness.

Higher-order CICs can achieve sufficient suppression through offering a very steep (potentiated sinc) impulse response, and their use in decimators (like your 100-fold decimation) is very popular due to the elegant nature of the math: you can drag the decimation stage between the integrators and the differentiators, like

-1 kHz-->(+)------·--->[↓100]---·------(+)---10 Hz->
          ^       |             |       ^
          --[z⁻¹]--             --[z⁻ⁿ]--

As you can see: extremely low component effort!

In all honesty, though: You're building a massively decimating filter, at a sample rate that is ridiculously low for FPGAs, aiming for a sampling rate that's even lower.

Do yourself a favor. Calculate a proper 1/100-band FIR filter. At these rates, you really need but a single multiplier that you use for all coefficients. And even then it doesn't need to be a fast one. That way, you gain significantly more freedom in designing your filter response.

Found it: You should really read this article of Richard Lyons, whom you can also work with over on our Signal Processing StackExchange sister site.

| improve this answer | |
\$\endgroup\$
5
\$\begingroup\$

What you propose is often called an "exponentially weighted moving average", and armed with that term, you can probably find a formula (wikipedia discusses this in some way).

Generally, what you propose doesn't even work without a multiplier (how do you multiply with \$\alpha\$ and \$1-\alpha\$ without a multiplier– they can't both be a power of two at the same time?), so I doubt it's actually a solution to your problem.

(An actual moving average would work – just add up a window of \$N\$ input items, tada, convolution with a rectangle corresponds to a sinc in spectrum of \$\frac1N\$ width. But that's not a great filter, honestly, because of the sidelobes.)

What you will be interested in is Cascaded Integrator-Comb filters (CICs), which

  1. do actually not require any multipliers, because all the coefficients are 1 or 0,
  2. are actually FIR although having feedback structure, and
  3. have an easy to calculate frequency response that's probably closer to what you had in mind when you said "low-pass filter".
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Well I can subtract, so if I chose \$\alpha\$ as e.g. 0.03 I'd then shift everything by 5 bits and subtract 1. Of course there are some limitations... Regarding your point 3, I think I actually need an anti-aliasing filter \$\endgroup\$ – po.pe May 7 at 15:01
  • 1
    \$\begingroup\$ but an anti-aliasing filter is on the analog side of an ADC, otherwise it's too late. (Unless you want to resample the digital signal to a lower rate, but I'd guess you would've mentioned that) \$\endgroup\$ – Marcus Müller May 7 at 15:09
  • \$\begingroup\$ No that's actually the case yes, I need to downsample by factor 100 \$\endgroup\$ – po.pe May 7 at 15:15
  • \$\begingroup\$ ... Well, then edit your question to include that crucial info, @po.pe. This is what you need your filter for – so that defines what kind of filter you need. \$\endgroup\$ – Marcus Müller May 7 at 15:17
  • \$\begingroup\$ Information added... I'll have a dive into CIC filters \$\endgroup\$ – po.pe May 7 at 15:19
5
\$\begingroup\$

I thought the easiest way would be a recursive averaging IIR filter

Your equation represents a simple low-pass, first-order filter hence, \$\alpha\$ equals: -

$$\dfrac{t_{SAMPLE}}{CR}$$

Where CR is the equivalent CR time for a resistor-capacitor low-pass filter. So, with 1 kHz sampling and a required frequency cut-off (\$F_C\$) of (say) 10 Hz you'd calculate CR as follows: -

$$F_C = \dfrac{1}{2\pi RC}$$

For 10 Hz, CR therefore equals 0.015915 and T/CR = 0.06283.


Linear Simulation with VIN at 1 volt peak and 10 Hz

enter image description here

enter image description here

enter image description here

Discretely sampled at 1 kHz (prior to feeding into 1st summing block):

enter image description here

10th order, 10 Hz low pass filter (fairly Butterworthesque):

enter image description here

AC response: -

enter image description here

Response with 10 Hz input (down 3 dB): -

enter image description here

Response with 13 Hz input (down 22 dB): -

enter image description here

Response with 16 Hz input (down 39.5 dB): -

enter image description here

Response with 20 Hz input (down 58.7 dB): -

enter image description here

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ If I need higher order, would I just cascade them? \$\endgroup\$ – po.pe May 7 at 15:20
  • \$\begingroup\$ Cascading them is the usual way but you can cascade in ways that give an improved frequency response. Maybe take a look at this old document. It shows you how to cascade in a way that gives better performance around the cut-off frequency. \$\endgroup\$ – Andy aka May 7 at 15:23
4
\$\begingroup\$

Besides what the others have said, your difference equation leads to this transfer function:

$$H(z)=\frac{\alpha}{1-(1-\alpha)z^{-1}}$$

and evaluating it is done by substituting \$z^{-1}=e^{-j\Omega_p}\$:

$$H(\Omega_p)=\frac{\alpha}{1-(1-\alpha)e^{-j\Omega_p}}$$

If you consider the corner frequency to be the -3dB point, then this results back in this difference equation, followed by solving for the exact (sampled) frequency, \$\Omega_p=\pi\frac{f_p}{f_0}\$:

$$\begin{align} \left|H(\Omega_{-3\mathrm{dB}})\right|^2=\frac12&=\left|\frac{\alpha}{1-(1-\alpha)(\cos\Omega_p-j\sin\Omega_p)}\right|^2 \\ \Rightarrow 2\alpha^2&=\left[1-(1-\alpha)\cos\Omega_p\right]^2+\left[(1-\alpha)\sin\Omega_p\right]^2 \\ &=(\alpha^2-2\alpha+1)(\cos^2\Omega_p+\sin^2\Omega_p)+2(\alpha-1)\cos\Omega_p+1 \\ &=2(\alpha-1)\cos\Omega_p+\alpha^2-2\alpha+2 \\ \Rightarrow \Omega_p& =\arccos{\frac{\alpha^2+2\alpha-2}{2(\alpha-1)}} \end{align}$$

To verify, let's say \$\alpha=0.45\$, then

$$\Omega_p=0.6165 \\ |H(0.6165)|=\left|\frac{0.45}{1-0.55e^{-j0.6165}}\right|=0.7071$$

A picture might be worth a thousand words, so here's a quick check with LTspice, two versions of it:

test

| improve this answer | |
\$\endgroup\$
2
\$\begingroup\$

Here's a 1st order IIR Lowpass (also known as an exponential average) in C, using only shifts and no multiply:

//1st-order IIR lowpass
//2^(-SHIFT) = 1 - e^(-2*pi * Fc/Fs)
//Fc = Fs * (-ln(1 - 2^(-SHIFT)) / (2*pi))
//Fs = sample rate, Fc = -3dB cutoff frequency

void IIR_lowpass_1(int in, *out, SHIFT)
{
    *out -= (*out >> SHIFT);
    *out += (  in >> SHIFT);
}

It's become a standard block of code that I've used in a bunch of different projects now. You can do the math to see that this sequential operation has the exact same effect as the canonical version.
It must be atomic though - no task switching in the middle of it!

I'll leave it to the reader, both to optimize it as needed, and to translate it from a microcontroller to an FPGA. My point is simply that it can be done without a multiply.


For higher orders, you can cascade a bunch of 1st orders:

void IIR_lowpass_2(int in, *mid, *out, SHIFT)
{
    IIR_lowpass_1(  in, *mid, SHIFT);
    IIR_lowpass_1(*mid, *out, SHIFT);
}

void IIR_lowpass_3(int in, *mid1, *mid2, *out, SHIFT)
{
    IIR_lowpass_1(   in, *mid1, SHIFT);
    IIR_lowpass_1(*mid1, *mid2, SHIFT);
    IIR_lowpass_1(*mid2,  *out, SHIFT);
}

Etc.

out and mid_ must each have their own dedicated storage.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ HI AaronD, um, the question is about an FPGA implementation, not a software implementation! \$\endgroup\$ – Marcus Müller May 8 at 10:16
  • \$\begingroup\$ @MarcusMüller I understood the FPGA part to only justify the requirement of "no multiply". The question is really about "no multiply" and "calculating the cutoff", both of which I have covered. \$\endgroup\$ – AaronD May 8 at 14:20
  • \$\begingroup\$ true! I didn't mean to put your answer down here. \$\endgroup\$ – Marcus Müller May 8 at 14:22

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.