# Design Peak and Notch Filters

This example shows how to design peak and notch filters. Filters that peak or notch at a certain frequency retain or eliminate a particular frequency component of a signal. The design parameters that you can set for such filters are the frequency at which you want the peak or notch and the 3-dB bandwidth or the Q factor. Using these specifications and by increasing the filter order, you can design filters that more closely approximate an ideal filter.

### Second-Order Notch Filters

Suppose that you want to eliminate a 60 Hz interference in a signal sampled at 3000 Hz. You can use a second-order notch filter in this case and use the `designNotchPeakIIR` function to compute the coefficients of the filter. The function expects frequencies in normalized units. Scale the design parameters accordingly and specify them to the function. Use the `Response="notch"` option to design a notch filter and `Response="peak"` option to design a peak filter.

```Fs = 3000; % Sampling frequency is 3000 Hz Fnyq = Fs/2; % Nyquist frequency is half the sampling frequency F0 = 60; % Interference is at 60 Hz BW = 6; % Choose a bandwidth factor of 6Hz [num1,den1] = designNotchPeakIIR(Response="notch",CenterFrequency=F0/Fnyq,Bandwidth=BW/Fnyq,FilterOrder=2); filterAnalyzer({num1,den1,1},SampleRates=Fs)```

You can also design the filter by specifying the quality factor. Quality factor is defined as the ratio of the notch or peak frequency ${\mathit{F}}_{0}$ and the bandwidth $\mathit{BW}$: $Q={\mathit{F}}_{0}/\mathit{BW}$. The quality factor is a measure of how well you can isolate the desired frequency from the other frequencies. When you fix the filter order, you can achieve a higher $Q$ by pushing the poles closer to the zeros. However, specifying the bandwidth is a more convenient way of achieving exactly the desired shape for the designed filter.

Design another second-order filter by specifying the quality factor and using it to calculate the 3-dB bandwidth. Use the `designNotchPeakIIR` function to compute the filter coefficients. Visualize the magnitude response of the filter`.`

```Q2 = 100; % Choose a Q factor of 100 [num2,den2] = designNotchPeakIIR(Response="notch",CenterFrequency=F0/Fnyq,QualityFactor=Q2,FilterOrder=2); filterAnalyzer({num1,den1,1}, {num2,den2,1}, SampleRates=Fs, FilterNames=["notchQ10","notchQ100"])```

### Second-Order Peak Filters

Peak filters are used to retain only a single frequency component (or a small band of frequencies) from a signal. Use the same `designNotchPeakIIR` function to compute the coefficients of a peak filter.

```Fs = 3000; % Sampling frequency is 3000 Hz Fnyq = Fs/2; % Nyquist frequency is half the sampling frequency F0 = 1000; % Interference is at 60 Hz Q1 = 10; [num1, den1] = designNotchPeakIIR(Response="peak",CenterFrequency = F0/Fnyq,QualityFactor=Q1,FilterOrder=2); Q2 = 100; [num2, den2] = designNotchPeakIIR(Response="peak",CenterFrequency = F0/Fnyq,QualityFactor=Q2,FilterOrder=2); filterAnalyzer({num1,den1,1}, {num2,den2,1}, SampleRates=Fs, FilterNames=["peakQ10","peakQ100"])```

### Time Varying Second-Order Notch Filter Implementation

Using time-varying filters requires changing the coefficients of the filter while the simulation runs. This is achieved by calling the `designNotchPeakIIR` function during runtime with the changing parameters. Alternatively, the DSP System Toolbox™ provides the `dsp.NotchPeakFilter` object to design and implement time-varying (tunable) second-order notch and peak filters.

#### Dynamic Simulation with Static Filter

In order to implement a time-varying filter, create a dynamic setup to simulate the filter and implement the filter with time-varying design parameters.

Start by creating a dynamic streamed simulation with filters whose coefficients do not change. Create two second-order notch filters, the first using the `dsp.SOSFilter` object and the second using the `dsp.NotchFilter` object. In the first filter, set the center frequency to 1 kHz, and the bandwidth at –3 dB to 500 Hz. Calculate the coefficients of this filter directly using the `designNotchPeakIIR` function. In the second filter, set the center frequency to 3 kHz and the bandwidth at –3 dB to 500 Hz. The sample rate for both filters is 8 kHz. Use the `Verbose=true` option to print the default values of unspecified parameters. In this case, `FilterOrder` is not specified and defaults to 2.

```Fs = 8e3; % 8 kHz sampling frequency Fnyq = Fs/2; % Nyquist frequency % Input parameters samplesPerFrame = 256; nFrames = 8192; F01 = 1e3; % Notch at 1 kHz for Filter 1 BW = 500; % 500 Hz bandwidth for both filters [b, a] = designNotchPeakIIR(Response="notch",CenterFrequency=F01/Fnyq,Bandwidth=BW/Fnyq,Verbose=true) % Filter 1 coefficients```
```designNotchPeakIIR(FilterOrder=2, CenterFrequency=0.25, Bandwidth=0.125, HasScaleValues=false, Response="notch") ```
```b = 1×3 0.8341 -1.1796 0.8341 ```
```a = 1×3 1.0000 -1.1796 0.6682 ```
```sosFilter = dsp.SOSFilter(b,a); F02 = 3e3; % Notch at 3 kHz for Filter 2 npFilter = dsp.NotchPeakFilter(CenterFrequency=F02,Bandwidth=BW,SampleRate=Fs); scope = spectrumAnalyzer(PlotAsTwoSidedSpectrum=false, ... SampleRate=Fs, ... AveragingMethod="exponential",... ForgettingFactor=.95,... ChannelNames=["Filter 1","Filter 2"],... ShowLegend=true); for k = 1:nFrames x = randn(samplesPerFrame, 1); y1 = sosFilter(x); y2 = npFilter(x); scope([y1,y2]); end```

#### Simulation with Time-Varying Filter

The coefficients of time-varying filters change over time due to runtime changes in the design parameters (for example, the center frequency for a notch filter). Create two second-order notch filters with time-varying design parameters. As with the static filter, use the `designNotchPeakIIR` function and the `dsp.SOSFilter` object to implement the first filter, and the `dsp.NotchPeakFilter` object to implement the second filter. Vary the design parameters of both filters over time.

```% Notch filter parameters - how they vary over time Fs = 8e3; % 8 kHz sampling frequency F01 = 1e3 * [0.5, 1, 1.5, 3]; % Notch frequencies for Filter 1 F02 = 1e3 * [3.5, 3, 2.5, 2]; % Notch frequencies for Filter 2 BW = 500 * ones(1,4); % 500 Hz bandwidth for both filters myChangingParams1 = struct(f0=num2cell(F01/(Fs/2)),bw=num2cell(BW/(Fs/2))); myChangingParams2 = struct(F0=num2cell(F02),BW=num2cell(BW)); paramsChangeTimes = [0, 70, 140, 210]; % in seconds % Simulation time management nSamplesPerFrame = 256; tEnd = 300; nSamples = ceil(tEnd * Fs); nFrames = floor(nSamples / nSamplesPerFrame); % Object creation sosFilter = dsp.SOSFilter; %Filter 1 object npFilter = dsp.NotchPeakFilter(SampleRate=Fs); scope = spectrumAnalyzer(PlotAsTwoSidedSpectrum=false, ... SampleRate=Fs, ... AveragingMethod="exponential",... ForgettingFactor=.75,... ChannelNames=["Filter 1","Filter 2"],... ShowLegend=true); paramtbl1 = ParameterTimeTable(Time=paramsChangeTimes, ... Values=myChangingParams1, ... SampleRate=Fs/nSamplesPerFrame); paramtbl2 = ParameterTimeTable(Time=paramsChangeTimes, ... Values=myChangingParams2, ... SampleRate=Fs/nSamplesPerFrame); % Actual simulation loop for frameIdx = 1:nFrames % Get current F0 and BW [params1, update1] = paramtbl1(); [params2, update2] = paramtbl2(); if(update1) % Recompute filter coefficients if parameters changed [b, a] = designNotchPeakIIR(Response="notch",CenterFrequency=params1.f0,Bandwidth=params1.bw); % Set filter coefficients to new values sosFilter.Numerator = b; sosFilter.Denominator = a; end if(update2) npFilter.CenterFrequency = params2.F0; npFilter.Bandwidth = params2.BW; end % Generate vector of white noise samples x = randn(nSamplesPerFrame, 1); % Filter noise y1 = sosFilter(x); y2 = npFilter(x); % Visualize spectrum scope([y1,y2]); end```

You can implement a tunable peak filter similarly using the `dsp.NotchPeakFilter` object or by using the `iirpeak` function and `dsp.SOSFilter` object.

Note: The tunable peak and notch filters support code generation.

### Higher Order Notch and Peak Filters

As you cannot push the poles beyond a point without affecting filter stability, in order to improve the brickwall approximation of the filter, you must increase the filter order. Use the `FilterOrder` design parameter to set the notch or peak filter order, and the `SystemObject=true` option to design a `dsp.SOSFilter` System object. Compare a second order notch filter design against a sixth order notch filter.

```notchfilt2 = designNotchPeakIIR(Response="notch",FilterOrder=2,CenterFrequency=0.4,QualityFactor=100,SystemObject=true); notchfilt6 = designNotchPeakIIR(Response="notch",FilterOrder=6,CenterFrequency=0.4,QualityFactor=100,SystemObject=true); filterAnalyzer(notchfilt2, notchfilt6, FilterNames=["notch2ndOrderFilter","notch6thOrderFilter"])```

Similarly, compare a second order peak filter design against an eighth order peak filter.

```peakfilt2 = designNotchPeakIIR(Response="peak",FilterOrder=2,CenterFrequency=0.6,QualityFactor=80,SystemObject=true); peakfilt8 = designNotchPeakIIR(Response="peak",FilterOrder=8,CenterFrequency=0.6,QualityFactor=80,SystemObject=true); filterAnalyzer(peakfilt2, peakfilt8, FilterNames=["peak2ndOrderFilter", "peak8thOrderFilter"])```

### Higher Order Notch and Peak Filter with Passband and Stopband Ripple Control

For a given filter order, you can obtain sharper transitions by allowing for passband or stopband ripples or both. This can be accomplished by using the `fdesign.notch` and the `fdesign.peak` filter specification objects. All specifications and tradeoffs mentioned so far apply equally to notch and peak filters.

```N = 8; F0 = 0.4; BW = 0.1; notchfilt = designNotchPeakIIR(Response="notch",FilterOrder=N,CenterFrequency=F0,Bandwidth=BW,SystemObject=true); notchspec1 = fdesign.notch("N,F0,BW,Ap,Ast",N,F0,BW,0.5,60); notchfilt1 = design(notchspec1,SystemObject=true); filterAnalyzer(notchfilt, notchfilt1, FilterNames=["NotchMaximallyFlat8thOrderFilter",... "Notch8thOrderFilterWithPassbandorStopbandRipples"])```

```N = 6; F0 = 0.7; BW = 0.001; peakfilt = designNotchPeakIIR(Response="peak",FilterOrder=N,CenterFrequency=F0,Bandwidth=BW,SystemObject=true); peakspec1 = fdesign.peak("N,F0,BW,Ast",N,F0,BW,80); peakfilt1 = design(peakspec1,SystemObject=true); filterAnalyzer(peakfilt, peakfilt1, FilterNames=["PeakMaximallyFlat6thOrderFilter",... "Peak6thOrderFilterWith80dBStopbandAttenuation"])```