Main Content

This example shows how to design FIR halfband filters. Halfband filters are widely used in multirate signal processing applications when interpolating/decimating by a factor of two. Halfband filters are implemented efficiently in polyphase form, because approximately half of its coefficients are equal to zero.

Halfband filters have two important characteristics, the passband and stopband ripples must be the same, and the passband-edge and stopband-edge frequencies are equidistant from the halfband frequency Fs/4 (Or pi/2 rad/sample in normalized frequency).

The `firhalfband`

function returns the coefficients of an FIR halfband equiripple filter. As a simple example, consider a halfband filter whose dealing with data sampled at 96 kHz and a passband frequency of 22 kHz.

Fs = 96e3; Fp = 22e3; N = 100; num = firhalfband(N,Fp/(Fs/2)); fvt = fvtool(num,'Fs',Fs,'Color','white'); fvt.MagnitudeDisplay = 'Zero-phase';

By zooming in to the response, you can verify that the passband and stopband peak-to-peak ripples are the same. Also there is symmetry about the Fs/4 (24 kHz) point. The passband extends up to 22 kHz as specified and the stopband begins at 26 kHz. We can also verify that every other coefficient is equal to zero by looking at the impulse response. This makes the filter very efficient to implement for interpolation/decimation by a factor of 2.

```
fvt.Analysis = 'impulse';
```

`dsp.FIRHalfbandInterpolator`

and `dsp.FIRHalfbandDecimator`

The `firhalfband`

function provides several other design options. However, for most cases it is preferable to work directly with `dsp.FIRHalfbandInterpolator`

and `dsp.FIRHalfbandDecimator`

. These two System objects not only design the coefficients, but also provide efficient implementation of the polyphase interpolator/decimator. They support filtering double/single precision floating-point data as well as fixed-point data. They also support C and HDL code generation as well as optimized ARM® Cortex® M and Cortex A code generation.

halfbandInterpolator = dsp.FIRHalfbandInterpolator('SampleRate',Fs, ... 'Specification','Filter order and transition width', ... 'FilterOrder',N,'TransitionWidth',4000); fvtool(halfbandInterpolator,'Fs',2*Fs,'Color','white');

In order to perform the interpolation, the `dsp.FIRHalfbandInterpolator`

System object is used. Because this is a multirate filter, it is important to define what is meant by the sample rate. For this and all other System objects, the sample rate refers to the sample rate of the input signal. However, FVTool defines the sample rate as the rate at which the filter is running. In the case of interpolation, you upsample and then filter (conceptually), therefore the sampling rate of FVTool needs to be specified as 2*Fs because of the upsampling by 2.

FrameSize = 256; scope = dsp.SpectrumAnalyzer('SampleRate',2*Fs,'SpectralAverages',5); sine1 = dsp.SineWave('Frequency',10e3','SampleRate',Fs, ... 'SamplesPerFrame',FrameSize); sine2 = dsp.SineWave('Frequency',20e3','SampleRate',Fs, ... 'SamplesPerFrame',FrameSize); tic while toc < 10 x = sine1() + sine2() + 0.01.*randn(FrameSize,1); % 96 kHz y = halfbandInterpolator(x); % 192 kHz scope(y); end release(scope);

Notice that the spectral replicas are attenuated by about 40 dB which is roughly the attenuation provided by the halfband filter. Compensating for the group-delay in the filter, it is possible to plot the input and interpolated samples overlaid. Notice that the input samples are retained unchanged at the output of the filter. This is because one of the polyphase branches of the halfband is a pure delay branch which does not change the input samples.

grpDel = 50; n = 0:2:511; stem(n(1:end-grpDel/2),x(1:end-grpDel/2),'k','filled') hold on nu = 0:511; stem(nu(1:end-grpDel),y(grpDel+1:end)) legend('Input samples','Interpolated samples')

In the case of decimation, the sample rate specified in `dsp.FIRHalfbandDecimator`

corresponds to the sample rate of the filter, since you filter and then downsample (conceptually). So for decimators, the Fs specified in FVTool does not need to be multiplied by any factor.

FrameSize = 256; FsIn = 2*Fs; halfbandDecimator = dsp.FIRHalfbandDecimator('SampleRate',FsIn, ... 'Specification','Filter order and transition width', ... 'FilterOrder',N,'TransitionWidth',4000); fvtool(halfbandDecimator,'Fs',FsIn,'Color','white'); scope = dsp.SpectrumAnalyzer('SampleRate',Fs,'SpectralAverages',5); sine1 = dsp.SineWave('Frequency',10e3','SampleRate',Fs, ... 'SamplesPerFrame',FrameSize); sine2 = dsp.SineWave('Frequency',20e3','SampleRate',Fs, ... 'SamplesPerFrame',FrameSize); tic while toc < 10 x = sine1() + sine2() + 0.01.*randn(FrameSize,1); % 96 kHz y = halfbandInterpolator(x); % 192 kHz xd = halfbandDecimator(y); % 96 kHz scope(xd); end release(scope);

The filter coefficients can be extracted from the interpolator/decimator by using the `tf`

function.

```
num = tf(halfbandInterpolator); % Or num = tf(halfbandDecimator);
```

Instead of specifying the filter order and transition width, you can design a minimum-order filter that provides a given transition width as well as a given stopband attenuation.

Ast = 80; % 80 dB halfbandInterpolator = dsp.FIRHalfbandInterpolator('SampleRate',Fs, ... 'Specification','Transition width and stopband attenuation', ... 'StopbandAttenuation',Ast,'TransitionWidth',4000); fvtool(halfbandInterpolator,'Fs',2*Fs,'Color','white');

Notice that as with all interpolators, the passband gain in absolute units is equal to the interpolation factor (2 in the case of halfbands). This corresponds to a passband gain of 6.02 dB.

It is also possible to specify the filter order and the stopband attenuation.

halfbandDecimator = dsp.FIRHalfbandDecimator('SampleRate',Fs, ... 'Specification','Filter order and stopband attenuation', ... 'StopbandAttenuation',Ast,'FilterOrder',N); fvtool(halfbandDecimator,'Fs',Fs,'Color','white');

Unlike interpolators, decimators have a gain of 1 (0 dB) in the passband.

Halfband interpolators and decimators can be used to efficiently implement synthesis/analysis filter banks. The halfband filters shown so far have all been lowpass filters. With a single extra adder, it is possible to obtain a highpass response in addition to the lowpass response and use the two responses for the filter bank implementation.

The following code simulates a quadrature mirror filter (QMF) bank. An 8 kHz signal consisting of 1 kHz and 3 kHz sine waves is separated into two 4 kHz signals using a lowpass/highpass halfband decimator. The lowpass signal retains the 1 kHz sine wave while the highpass signal retains the 3 kHz sine wave (which is aliased to 1 kHz after downsampling). The signals are then merged back together with a synthesis filter bank using a halfband interpolator. The highpass branch upconverts the aliased 1 kHz sine wave back to 3 kHz. The interpolated signal has an 8 kHz sampling rate.

Fs1 = 8000; % Units = Hz Spec = 'Filter order and transition width'; Order = 52; TW = 4.1e2; % Units = Hz % Construct FIR Halfband Interpolator halfbandInterpolator = dsp.FIRHalfbandInterpolator( ... 'Specification',Spec, ... 'FilterOrder',Order, ... 'TransitionWidth',TW, ... 'SampleRate',Fs1/2, ... 'FilterBankInputPort',true); % Construct FIR Halfband Decimator halfbandDecimator = dsp.FIRHalfbandDecimator( ... 'Specification',Spec, ... 'FilterOrder',Order, ... 'TransitionWidth',TW, ... 'SampleRate',Fs1); % Input f1 = 1000; f2 = 3000; InputWave = dsp.SineWave('Frequency',[f1,f2],'SampleRate',Fs1, ... 'SamplesPerFrame',1024,'Amplitude',[1 0.25]); % Construct Spectrum Analyzer object to view the input and output scope = dsp.SpectrumAnalyzer('SampleRate',Fs1, ... 'PlotAsTwoSidedSpectrum',false,'ShowLegend',true,'YLimits', ... [-120 30], ... 'Title', ... 'Input Signal and Output Signal of Quadrature Mirror Filter'); scope.ChannelNames = {'Input','Output'}; tic while toc < 10 Input = sum(InputWave(),2); NoisyInput = Input+(10^-5)*randn(1024,1); [Lowpass,Highpass] = halfbandDecimator(NoisyInput); Output = halfbandInterpolator(Lowpass,Highpass); scope([NoisyInput,Output]); end release(scope);

All designs presented so far have been optimal equiripple designs. Using `fdesign.interpolator`

and `fdesign.decimator`

, other design algorithms are available.

Fs = 44.1e3; N = 90; TW = 1000/Fs; % Transition width filtSpecs = fdesign.interpolator(2,'halfband','N,TW',N,TW); equirippleHBFilter = design(filtSpecs,'equiripple','SystemObject',true); leastSquaresHBFilter = design(filtSpecs,'firls','SystemObject',true); kaiserHBFilter = design(filtSpecs,'kaiserwin','SystemObject',true);

You can compare the designs with FVTool. The different designs allow for trade offs between minimum stopband attenuation and more overall attenuation.

fvt = fvtool(equirippleHBFilter,leastSquaresHBFilter,kaiserHBFilter, ... 'Fs',2*Fs,'Color','white'); legend(fvt,'Equiripple design','Least-squares design', ... 'Kaiser-window design')

Alternatively, one can specify the order and the stopband attenuation. This allows for trade offs between overall stopband attenuation and transition width.

Ast = 60; % Minimum stopband attenuation filtSpecs = fdesign.interpolator(2,'halfband','N,Ast',N,Ast); equirippleHBFilter = design(filtSpecs,'equiripple','SystemObject',true); kaiserHBFilter = design(filtSpecs,'kaiserwin','SystemObject',true); fvt = fvtool(equirippleHBFilter,kaiserHBFilter,'Fs',2*Fs,'Color','white'); legend(fvt,'Equiripple design','Kaiser-window design')

Kaiser window designs can also be used in addition to equiripple designs when designing a filter of the minimum-order necessary to meet the design specifications. The actual order for the Kaiser window design is larger than that needed for the equiripple design, but the overall stopband attenuation is better in return.

Fs = 44.1e3; TW = 1000/(Fs/2); % Transition width Ast = 60; % 60 dB minimum attenuation in the stopband filtSpecs = fdesign.decimator(2,'halfband','TW,Ast',TW,Ast); equirippleHBFilter = design(filtSpecs,'equiripple','SystemObject',true); kaiserHBFilter = design(filtSpecs,'kaiserwin','SystemObject',true); fvt = fvtool(equirippleHBFilter,kaiserHBFilter,'Fs',Fs,'Color','white'); legend(fvt,'Equiripple Design','Kaiser-window design')

Instead of designing Kaiser window filters, it is also possible to obtain increasing stopband attenuation with modified 'equiripple' designs.

equirippleHBFilter1 = design(filtSpecs,'equiripple', ... 'StopbandShape','1/f','StopbandDecay',4,'SystemObject',true); equirippleHBFilter2 = design(filtSpecs,'equiripple', ... 'StopbandShape','linear','StopbandDecay',53.333,'SystemObject',true); fvt = fvtool(equirippleHBFilter1,equirippleHBFilter2, ... 'Fs',Fs,'Color','white'); legend(fvt,'Stopband decaying as (1/f)^4','Stopband decaying linearly')

A highpass halfband filter can be obtained from a lowpass halfband filter by changing the sign of every second coefficient. Alternatively, one can directly design a highpass halfband by setting the 'Type' property to 'Highpass'.

filtSpecs = fdesign.decimator(2,'halfband', ... 'Type','Highpass','TW,Ast',TW,Ast); halfbandHPFilter = design(filtSpecs,'equiripple', ... 'StopbandShape','linear','StopbandDecay',53.333,'SystemObject',true); fvt = fvtool(halfbandHPFilter,equirippleHBFilter2,'Fs',Fs,'Color','white'); legend(fvt,'Highpass halfband filter','Lowpass halfband filter')