Efficient Narrow Transition-Band FIR Filter Design
This example shows how to design efficient FIR filters with very narrow transition-bands using multistage techniques. The techniques can be extended to the design of multirate filters. See Multistage Rate Conversion for an example of that.
Design of a Lowpass Filter with Narrow Transition Bandwidth
One of the drawbacks of using FIR filters is that the filter order tends to grow inversely proportional to the transition bandwidth of the filter. Consider the following design specifications (where the ripples are given in linear units):
Fpass = 2866.5; % Passband edge Fstop = 3087; % Stopband edge Apass = 0.0174; % Passband ripple, 0.0174 dB peak to peak Astop = 66.0206; % Stopband ripple, 66.0206 dB of minimum attenuation Fs = 44.1e3; lowpassSpec = fdesign.lowpass(Fpass,Fstop,Apass,Astop,Fs);
A regular linear-phase equiripple design that meets the specs can be designed with:
eqripFilter = design(lowpassSpec,"equiripple",SystemObject=true);
cost(eqripFilter)
ans = struct with fields:
NumCoefficients: 695
NumStates: 694
MultiplicationsPerInputSample: 695
AdditionsPerInputSample: 694
The filter length required turns out to be 694 taps.
Interpolated FIR (IFIR) Design
The IFIR design algorithm achieves an efficient design for the above specifications in the sense that it reduces the total number of multipliers required. To do this, the design problem is broken into two stages, a filter which is upsampled to achieve the stringent specifications without using many multipliers, and a filter which removes the images created when upsampling the previous filter.
interpFilter = design(lowpassSpec,"ifir",SystemObject=true);
Apparently we have made things worse. Instead of a single filter with 694 multipliers, we now have two filters with a total of 804 multipliers. However, close examination of the second stage shows that only about one multiplier in 5 is non-zero. The actual total number of multipliers has been reduced from 694 to 208.
cost(interpFilter)
ans = struct with fields:
NumCoefficients: 208
NumStates: 802
MultiplicationsPerInputSample: 208
AdditionsPerInputSample: 206
Let's compare the responses of the two designs:
hFiltAnalyzer = filterAnalyzer(eqripFilter,interpFilter); setLegendStrings(hFiltAnalyzer,["Equiripple design" "IFIR design"])
Manually Controlling the Upsampling Factor
In the previous example, we automatically determined the upsampling factor used such that the total number of multipliers was minimized. It turned out that for the given specifications, the optimal upsampling factor was 5. However, if we examine the design options:
opts = designopts(lowpassSpec,"ifir")
opts = struct with fields:
FilterStructure: 'dffir'
UpsamplingFactor: 'auto'
JointOptimization: 0
SystemObject: 0
we can see that we can control the upsampling factor. For example, if we wanted to upsample by 4 rather than 5:
opts.UpsamplingFactor = 4;
opts.SystemObject = true;
upfilter = design(lowpassSpec,"ifir",opts);
cost(upfilter)
ans = struct with fields:
NumCoefficients: 217
NumStates: 767
MultiplicationsPerInputSample: 217
AdditionsPerInputSample: 215
We would obtain a design that has a total of 217 non-zero multipliers.
Using Joint Optimization
It is possible to design the two filters used in IFIR conjunctly. By doing so, we can save a significant number of multipliers at the expense of a longer design time (due to the nature of the algorithm, the design may also not converge altogether in some cases):
opts.UpsamplingFactor = "auto"; % Automatically determine the best factor opts.JointOptimization = true; ifirFilter = design(lowpassSpec,"ifir",opts); cost(ifirFilter)
ans = struct with fields:
NumCoefficients: 172
NumStates: 726
MultiplicationsPerInputSample: 172
AdditionsPerInputSample: 170
For this design, the best upsampling factor found was 6. The number of non-zero multipliers is now only 152
Using Multirate/Multistage Techniques to Achieve Efficient Designs
For the designs discussed so far, single-rate techniques have been used. This means that the number of multiplications required per input sample (MPIS) is equal to the number of non-zero multipliers. For instance, the last design we showed requires 152 MPIS. The single-stage equiripple design we started with required 694 MPIS.
By using multirate/multistage techniques which combine decimation and interpolation we can also obtain efficient designs with a low number of MPIS. For decimators, the number of multiplications required per input sample (on average) is given by the number of multipliers divided by the decimation factor.
multistageFilter = design(lowpassSpec,"multistage",SystemObject=true)
multistageFilter = dsp.FilterCascade with properties: Stage1: [1×1 dsp.FIRDecimator] Stage2: [1×1 dsp.FIRDecimator] Stage3: [1×1 dsp.FIRDecimator] Stage4: [1×1 dsp.FIRInterpolator] Stage5: [1×1 dsp.FIRInterpolator] Stage6: [1×1 dsp.FIRInterpolator] CloneStages: true
cost(multistageFilter)
ans = struct with fields:
NumCoefficients: 396
NumStates: 352
MultiplicationsPerInputSample: 73
AdditionsPerInputSample: 70.8333
The first stage has 21 multipliers, and a decimation factor of 3. Therefore, the number of MPIS is 7. The second stage has a length of 45 and a cumulative decimation factor of 6 (that is the decimation factor of this stage multiplied by the decimation factor of the first stage; this is because the input samples to this stage are already coming in at a rate 1/3 the rate of the input samples to the first stage). The average number of multiplications per input sample (reference to the input of the overall multirate/multistage filter) is thus 45/6=7.5. Finally, given that the third stage has a decimation factor of 1, the average number of multiplications per input for this stage is 130/6=21.667. The total number of average MPIS for the three decimators is 36.5.
For the interpolators, it turns out that the filters are identical to the decimators. Moreover, their computational cost is the same. Therefore the total number of MPIS for the entire multirate/multistage design is 73.
Now we compare the responses of the equiripple design and this one:
hFiltAnalyzer = filterAnalyzer(eqripFilter,multistageFilter)
hFiltAnalyzer = filterAnalyzer with no properties.
setLegendStrings(hFiltAnalyzer,["Equiripple design", "Multirate/multistage design"]);
Notice that the stopband attenuation for the multistage design is about double that of the other designs. This is because it is necessary for the decimators to attenuate out of band components by the required 66 dB in order to avoid aliasing that would violate the required specifications. Similarly, the interpolators need to attenuate images by 66 dB in order to meet the specifications of this problem.
Manually Controlling the Number of Stages
The multirate/multistage design that was obtained consisted of 6 stages. The number of stages is determined automatically by default. However, it is also possible to manually control the number of stages that result. For example:
lp4stage=design(lowpassSpec,"multistage",NStages=4,SystemObject=true);
cost(lp4stage)
ans = struct with fields:
NumCoefficients: 516
NumStates: 402
MultiplicationsPerInputSample: 86
AdditionsPerInputSample: 84.5000
The average number of MPIS for this case is 86
Group Delay
We can compute the group delay for each design. Notice that the multirate/multistage design introduces the most delay (this is the price to pay for a less computationally expensive design). The IFIR design introduces more delay than the single-stage equiripple design, but less so than the multirate/multistage design.
hFiltAnalyzer = filterAnalyzer(eqripFilter,interpFilter,multistageFilter,... Analysis="groupdelay"); setLegendStrings(hFiltAnalyzer,["Equiripple design" "IFIR design",... "Multirate/multistage design"]);
Filtering a Signal
We now show by example that the IFIR and multistage/multirate design perform comparably to the single-stage equiripple design while requiring much less computation. In order to perform filtering for cascades, it is necessary to call generateFilteringCode(ifirFilter) and generateFilteringCode(multistageFilter). This has been done here already in order to create HelperIFIRFilter and HelperMultiFIRFilter.
scope = spectrumAnalyzer(SampleRate=Fs,... PlotAsTwoSidedSpectrum=false,YLimits=[-90 10],... ShowLegend=true,ChannelNames=["Equiripple design",... "IFIR design","Multirate/multistage design"]); tic, while toc < 20 % Run for 20 seconds x = randn(6000,1); % Filter using single stage FIR filter y1 = eqripFilter(x); % Filter using IFIR filter y2 = HelperIFIRFilter(x); % Filter multi-stage/multi-rate FIR filters y3 = HelperMultiFIRFilter(x); % Compare the output from both approaches scope([y1,y2,y3]) end