DSP System Toolbox

Designing Low Pass FIR Filters

This example shows how to design low pass FIR filters. We will use filter design objects (fdesign) throughout this example.

FIR filters are widely used due to the powerful design algorithms that exist for them, their inherent stability when implemented in non-recursive form, the ease with which one can attain linear phase, their simple extensibility to multirate cases, and the ample hardware support that exists for them among other reasons. This example showcases functionality in the DSP System Toolbox™ for the design of low pass FIR filters with a variety of characteristics. Many of the concepts presented here can be extended to other responses such as highpass, bandpass, etc.

A Simple Low Pass Filter Design

An ideal low pass filter requires an infinite impulse response. Truncating (or windowing) the impulse response results in the so-called window method of FIR filter design. Consider a simple design of a low pass filter with a cutoff frequency of 20 kHz and a sampling frequency of 96 kHz

Fc = 20e3; % 8.25 kHz cutoff frequency
Fs = 96e3; % 96 kHz sampling frequency
N  = 100;  % FIR filter order
LP = fdesign.lowpass('N,Fc',N,Fc,Fs); % Fs is always trailing argument

Note that the filter hasn't been designed yet. Hf merely contains the desired design specifications. In order to design the filter according to the specifications provided, the 'design' function must be used. In this case, two different designs are performed for the specifications provided and compared: using a Hamming window or a Hann window:

FIR_Hamming = design(LP,'window','window',@hamming,'SystemObject',true);
FIR_Hann = design(LP,'window','window',@hann,'SystemObject',true);
hfvt = fvtool(FIR_Hamming,FIR_Hann,'Color','White');
legend(hfvt,'Hamming window design','Hann window design')

The choice of a filter order of 100 was arbitrary. In general, a larger order results in a better approximation to ideal at the expense of a more costly implementation. For instance, with a Hann window, we can decrease the transition region by increasing the filter order:

LP.FilterOrder = 200; % Change filter order from 100 to 200
FIR_Hann200 = design(LP,'window','window',@hann,'SystemObject',true);
hfvt = fvtool(FIR_Hann,FIR_Hann200,'Color','White');
legend(hfvt,'Hann window design. Order = 100',...
    'Hann window design. Order = 200')

Minimum-Order Low Pass Filter Design

Instead of specifying the filter order, it is possible for the design algorithm to determine the minimum-order required to meet the design specifications. In order to do so, it is necessary to specify the amount of passband ripple and stopband attenuation that will be tolerated. It is also necessary to specify the width of the transition region around the ideal cutoff frequency. The latter is done by setting the passband edge frequency and the stopband edge frequency. The difference between the two determines the transition width.

Fp  = 20e3;
Fst = 22e3;  % Transition Width = Fst - Fp
Fs  = 96e3;
Ap  = 0.06;   % Passband ripple in dB
Ast = 80;     % Stopband attenuation in dB
LP = fdesign.lowpass('Fp,Fst,Ap,Ast',Fp,Fst,Ap,Ast,Fs);

We can still use the window method, along with a Kaiser window, to design the low pass filter.

FIR_Kaiser = design(LP,'kaiserwin','SystemObject',true);
measure(FIR_Kaiser)
 
ans =
 
Sample Rate      : 96 kHz      
Passband Edge    : 20 kHz      
3-dB Point       : 20.8039 kHz 
6-dB Point       : 21 kHz      
Stopband Edge    : 22 kHz      
Passband Ripple  : 0.0016589 dB
Stopband Atten.  : 79.7262 dB  
Transition Width : 2 kHz       
 

The Kaiser window design is not an optimal design and as a result the filter order required to meet the specifications using this method is larger than it needs to be.

cost(FIR_Kaiser)
 
ans =
 
Number of Multipliers            : 242
Number of Adders                 : 241
Number of States                 : 241
Multiplications per Input Sample : 242
Additions per Input Sample       : 241

Optimal Minimum-Order Designs

Equiripple designs result in the low pass filter with the smallest possible order to meet a set of specifications.

FIR_eqrip = design(LP,'equiripple','SystemObject',true);
cost(FIR_eqrip)
 
ans =
 
Number of Multipliers            : 173
Number of Adders                 : 172
Number of States                 : 172
Multiplications per Input Sample : 173
Additions per Input Sample       : 172

In this case, 173 coefficients are needed by the equiripple design while 242 are needed by the Kaiser window design.

Comparing the Kaiser window design to the optimal equiripple design shows that both designs meet the desired specifications. However, the Kaiser window design has a stopband response in which the minimum attenuation increases as the frequency increases. In contrast, the equiripple design has a constant minimun attenuation of about 60 dB throughout the entire stopband.

hfvt = fvtool(FIR_Kaiser,FIR_eqrip,'Color','White');
legend(hfvt,'Kaiser window design','Equiripple design')

The passband ripple can be examined by selecting the "View" menu in FVTool and then selecting "Passband". Both designs are within the specified 0.6 dB peak-to-peak ripple. However, the passband ripple of the Kaiser window design is considerably smaller than that of the equiripple design. This is one of the reasons why the equiripple design requires fewer coefficients.

Controlling the Filter order and Passband Ripple/Stopband Attenuation

When targeting custom hardware, it is common to find cases where the number of coefficients is constrained to a set number. In these cases, minimum-order designs are not useful because there is no control over the resulting filter order. As an example, suppose that only 87 coefficients could be used and the passband ripple/stopband attenuation specifications need to be met. We can still use equiripple designs for these specifications. However, you lose control over the transition width (which will increase from 2 kHz to 4 kHz given that 87 coefficients are used instead of 173) This is the price to pay for reducing the order while maintaining the same passband ripple/stopband attenuation specifications.

N   = 86; % Order = 86 -> 87 coefficients
Fc  = 21e3;
Fs  = 96e3;
Ap  = 0.06;   % Passband ripple in dB
Ast = 80;
LP = fdesign.lowpass('N,Fc,Ap,Ast',N,Fc,Ap,Ast,Fs);
FIR_eqrip86 = design(LP,'equiripple','SystemObject',true);
measure(FIR_eqrip86)
hfvt = fvtool(FIR_eqrip,FIR_eqrip86,'Color','White');
legend(hfvt,...
    'Equiripple design, 173 coefficients',...
    'Equiripple design, 87 coefficients')
 
ans =
 
Sample Rate      : 96 kHz     
Passband Edge    : 19.3651 kHz
3-dB Point       : 20.5586 kHz
6-dB Point       : 21 kHz     
Stopband Edge    : 23.3713 kHz
Passband Ripple  : 0.060012 dB
Stopband Atten.  : 79.9999 dB 
Transition Width : 4.0062 kHz 
 

Controlling the Transition Region Width

Another option when the number of coefficients is set is to maintain the transition width at the expense of control over the passband ripple/stopband attenuation.

N   = 86;
Fp  = 20e3;
Fst = 22e3;
Fs  = 96e3;
LP = fdesign.lowpass('N,Fp,Fst',N,Fp,Fst,Fs);
FIR_eqrip_TW = design(LP,'equiripple','SystemObject',true);
measure(FIR_eqrip_TW)
hfvt = fvtool(FIR_eqrip,FIR_eqrip_TW,'Color','White');
legend(hfvt,...
    'Equiripple design, 173 coefficients',...
    'Equiripple design, 87 coefficients')
 
ans =
 
Sample Rate      : 96 kHz     
Passband Edge    : 20 kHz     
3-dB Point       : 20.6878 kHz
6-dB Point       : 20.9997 kHz
Stopband Edge    : 22 kHz     
Passband Ripple  : 0.23686 dB 
Stopband Atten.  : 37.2987 dB 
Transition Width : 2 kHz      
 

Note that in this case, the differences between using 173 coefficients and using 87 coefficients is reflected in a larger passband ripple and a smaller stopband attenuation.

It is possible to increase the attenuation in the stopband while keeping the same filter order and transition width by the use of weights. Weights are a way of specifying the relative importance of the passband ripple versus the stopband attenuation. By default, passband and stopband are equally weighted (a weight of one is assigned to each). If we increase the stopband weight, we can increase the stopband attenuation at the expense of increasing the stopband ripple as well.

FIR_eqrip_wghts = design(LP,'equiripple','Wstop',5,'SystemObject',true);
measure(FIR_eqrip_wghts)
hfvt = fvtool(FIR_eqrip_TW,FIR_eqrip_wghts,'Color','White');
legend(hfvt,...
    'Passband weight = 1; Stopband weight = 1',...
    'Passband weight = 1, Stopband weight = 5')
 
ans =
 
Sample Rate      : 96 kHz     
Passband Edge    : 20 kHz     
3-dB Point       : 20.5412 kHz
6-dB Point       : 20.846 kHz 
Stopband Edge    : 22 kHz     
Passband Ripple  : 0.51499 dB 
Stopband Atten.  : 44.5178 dB 
Transition Width : 2 kHz      
 

Another possibility is to specify the exact stopband attenuation desired and lose control over the passband ripple. This is a powerful and very desirable specification. One has control over most parameters of interest. However, constrained-band designs may have numerical robustness issues. Designs should always be checked with FVTool.

N   = 86;
Fp  = 20e3;
Fst = 22e3;
Fs  = 96e3;
Ast = 80;
LP = fdesign.lowpass('N,Fp,Fst,Ast',N,Fp,Fst,Ast,Fs);
FIR_ConstrainedBand = design(LP,'equiripple','SystemObject',true);
hfvt = fvtool(FIR_eqrip_wghts,FIR_ConstrainedBand,'Color','White');
legend(hfvt,...
    'Equiripple design using weights',...
    'Equiripple design constraining the stopband')

Optimal Non-Equiripple Low Pass Filters

Equiripple designs achieve optimality by distributing the deviation from the ideal response uniformly. This has the advantage of minimizing the maximum deviation (ripple). However, the overall deviation, measured in terms of its energy tends to be large. This may not always be desirable. When low pass filtering a signal, this implies that remnant energy of the signal in the stopband may be relatively large. When this is a concern, least-squares methods provide optimal designs that minimize the energy in the stopband.

N   = 86;
Fp  = 20e3;
Fst = 22e3;
Fs  = 96e3;
LP = fdesign.lowpass('N,Fp,Fst',N,Fp,Fst,Fs);
FIR_LS = design(LP,'firls','SystemObject',true);
hfvt = fvtool(FIR_eqrip_TW,FIR_LS,'Color','White');
legend(hfvt,'Equiripple design','Least-squares design')

Notice how the attenuation in the stopband increases with frequency for the least-squares designs while it remains constant for the equiripple design. The increased attenuation in the least-squares case minimizes the energy in that band of the signal to be filtered.

Equiripple Designs with Increasing Stopband Attenuation

An often undesirable effect of least-squares designs is that the ripple in the passband region close to the passband edge tends to be large. For low pass filters in general, it is desirable that passband frequencies of a signal to be filtered are affected as little as possible. To this extent, an equiripple passband is generally preferable. If it is still desirable to have an increasing attenuation in the stopband, equiripple design options provide a way to achieve this.

FIR_eqrip_slope = design(LP,'equiripple','StopbandShape','1/f',...
    'StopbandDecay',4,'SystemObject',true);
hfvt = fvtool(FIR_LS,FIR_eqrip_slope,'Color','White');
legend(hfvt,'Least-squares design',...
    'Equiripple design with stopband decaying as (1/f)^4')

Notice that the stopbands are quite similar. However the equiripple design has a significantly smaller passband ripple in the vicinity of the passband-edge frequency, 20 kHz:

mls = measure(FIR_LS);
meq = measure(FIR_eqrip_slope);
mls.Apass
meq.Apass
ans =

    0.4837


ans =

    0.2661

Yet another possibility is to use an arbitrary magnitude specification and select two bands (one for the passband and one for the stopband). Then, by using weights for the second band, it is possible to increase the attenuation throughout the band. For more information on this and other arbitrary magnitude designs see Arbitrary Magnitude Filter Design.

N   = 86;
B   = 2; % Number of bands
Fp  = 20e3;
Fst = 22e3;
Fs  = 96e3;
F   = [0 Fp linspace(Fst,Fs/2,40)];
A   = [1 1 zeros(1,length(F)-2)];
W   = linspace(1,100,length(F)-2);
LP_ARB = fdesign.arbmag('N,B,F,A',N,B,F(1:2),A(1:2),F(3:end),A(3:end),Fs);
FIR_ARB = design(LP_ARB,'equiripple','B2Weights',W,'SystemObject',true);
fvtool(FIR_ARB,'Color','White');

Minimum-Phase Low Pass Filter Design

So far, we have only considered linear-phase designs. Linear phase is desirable in many applications. Nevertheless, if linear phase is not a requirement, minimum-phase designs can provide significant improvements over linear phase counterparts. However, minimum-phase designs are not always numerically robust. Always check the design with FVTool.

As an example of the advantages of minimum-phase designs, consider the comparison of a linear-phase design with a minimum-phase design that meets the same design specifications:

Fp  = 20e3;
Fst = 22e3;
Fs  = 96e3;
Ap  = 0.06;
Ast = 80;
LP = fdesign.lowpass('Fp,Fst,Ap,Ast',Fp,Fst,Ap,Ast,Fs);
FIR_eqrip_linphase =  design(LP,'equiripple','SystemObject',true);
FIR_eqrip_minphase =  design(LP,'equiripple','minphase',true,...
    'SystemObject',true);
hfvt = fvtool(FIR_eqrip_linphase,FIR_eqrip_minphase,'Color','White');
legend(hfvt,...
    'Linear-phase equiripple design',...
    'Minimum-phase equiripple design')

Notice that the number of coefficients has been reduced from 173 to 141. The group-delay plot also reveals advantages of the minimum-phase design. Notice how the group-delay is much smaller (in particular in the passband region).

hfvt = fvtool(FIR_eqrip_linphase,FIR_eqrip_minphase,...
    'Analysis','grpdelay','Color','White');
legend(hfvt,...
    'Linear-phase equiripple design',...
    'Minimum-phase equiripple design')

Minimum-Order Low Pass Filter Design Using Multistage Techniques

A different approach to minimizing the number of coefficients that does not involve minimum-phase designs is to use multistage techniques. Here we show an interpolated FIR (IFIR) approach. This approach breaks down the design problem into designing two filters in cascade. For this example, the design requires 151 coefficients rather than 173. For more information on this, see Efficient Narrow Transition-Band FIR Filter Design.

Fp  = 20e3;
Fst = 22e3;
Fs  = 96e3;
Ap  = 0.06;
Ast = 80;
LP = fdesign.lowpass('Fp,Fst,Ap,Ast',Fp,Fst,Ap,Ast,Fs);
IFIR = design(LP,'ifir','SystemObject',true);
cost(IFIR)
hfvt = fvtool(FIR_eqrip_linphase,IFIR,'Color','White');
legend(hfvt,...
    'Linear-phase equiripple design',...
    'Interpolated FIR equiripple design (two stages)')
 
ans =
 
Number of Multipliers            : 151
Number of Adders                 : 149
Number of States                 : 238
Multiplications per Input Sample : 151
Additions per Input Sample       : 149

In this case, the group-delay plot reveals disadvantages of the IFIR design. While IFIR designs do provide linear phase, the group delay is in general larger than a comparable single-stage design.

hfvt = fvtool(FIR_eqrip_linphase,IFIR,'Analysis','grpdelay',...
    'Color','White');
legend(hfvt,...
    'Linear-phase equiripple design',...
    'Interpolated FIR equiripple design (two stages)')

Low Pass Filter Design for Multirate Applications

Low pass filters are extensively used in the design of decimators and interpolators. See Design of Decimators/Interpolators for more information on this and Multistage Design Of Decimators/Interpolators for multistage techniques that result in very efficient implementations.