uploaded all materials
This commit is contained in:
35
lessons/lesson07/bandstop2500.m
Normal file
35
lessons/lesson07/bandstop2500.m
Normal file
@ -0,0 +1,35 @@
|
||||
function Hd = bandstop2500
|
||||
%BANDSTOP2500 Returns a discrete-time filter object.
|
||||
|
||||
% MATLAB Code
|
||||
% Generated by MATLAB(R) 9.9 and Signal Processing Toolbox 8.5.
|
||||
% Generated on: 24-Mar-2021 17:32:55
|
||||
|
||||
% Elliptic Bandstop filter designed using FDESIGN.BANDSTOP.
|
||||
|
||||
% All frequency values are in Hz.
|
||||
Fs = 8192; % Sampling Frequency
|
||||
|
||||
Fpass1 = 2000; % First Passband Frequency
|
||||
Fstop1 = 2300; % First Stopband Frequency
|
||||
Fstop2 = 2700; % Second Stopband Frequency
|
||||
Fpass2 = 3000; % Second Passband Frequency
|
||||
Apass1 = 0.5; % First Passband Ripple (dB)
|
||||
Astop = 60; % Stopband Attenuation (dB)
|
||||
Apass2 = 1; % Second Passband Ripple (dB)
|
||||
match = 'both'; % Band to match exactly
|
||||
|
||||
% Construct an FDESIGN object and call its ELLIP method.
|
||||
h = fdesign.bandstop(Fpass1, Fstop1, Fstop2, Fpass2, Apass1, Astop, ...
|
||||
Apass2, Fs);
|
||||
Hd = design(h, 'ellip', 'MatchExactly', match);
|
||||
|
||||
% Get the transfer function values.
|
||||
[b, a] = tf(Hd);
|
||||
|
||||
% Convert to a singleton filter.
|
||||
Hd = dfilt.df2(b, a);
|
||||
|
||||
|
||||
|
||||
% [EOF]
|
131
lessons/lesson07/filter_design.m
Normal file
131
lessons/lesson07/filter_design.m
Normal file
@ -0,0 +1,131 @@
|
||||
%% Lesson 7a. Designing filters the modern way
|
||||
% * Learn how to use `filterDesigner`
|
||||
% * Learn how to use `fdesign`
|
||||
close all; clear; clc;
|
||||
|
||||
%% Filter Overview
|
||||
% Remember order = # of delay elements, want to minimize to meet
|
||||
% some spec.
|
||||
%
|
||||
% There are 4 main types of filters:
|
||||
% * Lowpass - passes low frequencies through
|
||||
% * Highpass - passes high frequencies through
|
||||
% * Bandpass - passes a specific band
|
||||
% * Bandstop - stops a specific band from going through
|
||||
%
|
||||
% There are four primary implementations of filters that optimize
|
||||
% different properties:
|
||||
% * Butterworth - maxflat passband, monotonic passband & stopband
|
||||
% * Chebychev I - equiripple passband, monotonic stopband
|
||||
% * Chebychev II - monotonic passband, equiripple stopband
|
||||
% * Elliptic - minimum order, equiripple passband & stopband
|
||||
%
|
||||
% In this lesson, you will be able to very clearly see these
|
||||
% properties and filter types!
|
||||
|
||||
%% Filter design (GUI)
|
||||
% Note: Ask Prof. Keene about `filterDesigner`, it'll make him
|
||||
% very happy.
|
||||
filterDesigner; % then export to workspace as numerator and
|
||||
% denominator (not SOS for now)
|
||||
|
||||
%% Applying a GUI-designed filter
|
||||
% Note that if a filter is generated using a `filterDesigner` function,
|
||||
% it will be a filter object. You can apply a filter object using the
|
||||
% `filter()` function. If your filter is given as an impulse response, you
|
||||
% can either use the `filter()` function or convolve the impulse response
|
||||
% and the input signal.
|
||||
|
||||
% Load handel again
|
||||
load handel;
|
||||
|
||||
% Get a generated filter (this should decimate a narrow frequency band
|
||||
% centered at 2.5kHz, assuming a 8192Hz sampling frequency).
|
||||
flt = bandstop2500;
|
||||
|
||||
% Perform the filter
|
||||
y1 = filter(flt, y);
|
||||
|
||||
%% Filter design via `fdesign`
|
||||
% Filter specifications
|
||||
Fsample = 44100;
|
||||
Apass = 1;
|
||||
Astop = 80;
|
||||
Fpass = 1e3;
|
||||
Fstop = 1e4;
|
||||
|
||||
% Use the appropriate `fdesign.*` object to create the filter; in this case,
|
||||
% we're designing a lowpass filter.
|
||||
specs = fdesign.lowpass(Fpass, Fstop, Apass, Astop, Fsample);
|
||||
|
||||
% Note: the arguments to `fdesign.*` can be altered by passing in a SPEC option
|
||||
% -- the following call to `fdesign.lowpass` will produce the same filter. If
|
||||
% you want to do more complex stuff (like setting the 3 dB point directly or
|
||||
% setting the filter order), you can change this argument. See `doc
|
||||
% fdesign.lowpass` for more info.
|
||||
equivalent_specs = fdesign.lowpass('Fp,Fst,Ap,Ast', ...
|
||||
Fpass, Fstop, Apass, Astop, Fsample);
|
||||
|
||||
% For a given filter type, there are a myriad of different ways to design it
|
||||
% depending on what properties you want out of the filter (equiripple
|
||||
% passband/stopband, monotonic passband/stopband, minimum-order, max-flat,
|
||||
% etc.), which our lowpass filter object supports.
|
||||
%
|
||||
% Note: `'SystemObject'` should be set to `true` by Mathworks' recommendation
|
||||
% for these calls, even though they will often work without it.
|
||||
designmethods(specs, 'SystemObject', true);
|
||||
|
||||
% There are also various options we can set when designing the filter. In this
|
||||
% example, we'll tell it to match the passband amplitude exactly.
|
||||
designoptions(specs, 'butter', SystemObject=true);
|
||||
|
||||
% Finally, let's make this thing!
|
||||
butter_filter = design(specs, 'butter', ...
|
||||
MatchExactly='passband', SystemObject=true);
|
||||
|
||||
%% Examining a filter
|
||||
% Filters can be visualized with `fvtool`, drawing a nice box around the
|
||||
% transition zone and lines at the edge frequencies & important amplitudes:
|
||||
h = fvtool(butter_filter); % note no call to `figure`!
|
||||
|
||||
% `measure` is another way to check:
|
||||
measurements = measure(butter_filter);
|
||||
|
||||
% `h` is a handle to the graphics object we've created with `fvtool`, and we
|
||||
% can change its properties (listed with `get(h)`) if we wish:
|
||||
h.FrequencyScale = 'Log';
|
||||
ax = h.Children(15); % the axes object, found by looking at `h.Children`
|
||||
ax.YLim = [-60 10]; % in dB
|
||||
|
||||
%% Using a filter
|
||||
% Filter objects have a lot of methods to them (things like `freqz`, `impz`,
|
||||
% `islinphase`, `isallpass`, etc -- check `methods(butter_filter)` if you want
|
||||
% the list) that are rather useful. One method they *don't* have is `filter`,
|
||||
% so if you want to apply a filter to a signal, you do it like this:
|
||||
load handel; % of course; loads `y` and `Fs` into the workspace
|
||||
y1 = butter_filter(y); % use like a function!
|
||||
sound(y, Fs);
|
||||
sound(y1, Fs);
|
||||
|
||||
%% Plotting the result
|
||||
% Obtain the FFT of the original and the filtered signal.
|
||||
shifted_fft_mag = @(sig, len) fftshift(abs(fft(sig, len))) / len;
|
||||
N = 2^15;
|
||||
S1 = shifted_fft_mag(y, N);
|
||||
S2 = shifted_fft_mag(y1, N);
|
||||
F = linspace(-Fsample/2, Fsample/2, N); % not quite accurate but close
|
||||
|
||||
% Plot the FFTs.
|
||||
figure;
|
||||
|
||||
subplot(2, 1, 1);
|
||||
plot(F, S1);
|
||||
title 'Fourier Transform of Original Audio';
|
||||
xlabel 'Frequency (Hz)';
|
||||
ylabel 'Magnitude';
|
||||
|
||||
subplot(2, 1, 2);
|
||||
plot(F, S2);
|
||||
title 'Fourier Transform of Filtered Audio';
|
||||
xlabel 'Frequency (Hz)';
|
||||
ylabel 'Magnitude';
|
103
lessons/lesson07/old_filter_design.m
Normal file
103
lessons/lesson07/old_filter_design.m
Normal file
@ -0,0 +1,103 @@
|
||||
%% Lesson 7c. Designing filters the old fashioned way
|
||||
% (notes kept for historical reasons; the new notes are in `filter_design.m`.
|
||||
%
|
||||
% * Learn how to use the `filterDesigner` GUI.
|
||||
% * Learn how to manually design a filter.
|
||||
clc; clear; close all;
|
||||
|
||||
%% Filter Overview
|
||||
% Remember order = # of delay elements, want to minimize to meet
|
||||
% some spec.
|
||||
%
|
||||
% There are 4 main types of filters:
|
||||
% * Lowpass - passes low frequencies through
|
||||
% * Highpass - passes high frequencies through
|
||||
% * Bandpass - passes a specific band
|
||||
% * Bandstop - stops a specific band from going through
|
||||
%
|
||||
% There are four primary implementations of filters that optimize
|
||||
% different properties:
|
||||
% * Butterworth - maxflat passband, monotonic passband & stopband
|
||||
% * Chebychev I - equiripple passband, monotonic stopband
|
||||
% * Chebychev II - monotonic passband, equiripple stopband
|
||||
% * Elliptic - minimum order, equiripple passband & stopband
|
||||
%
|
||||
% In this lesson, you will be able to very clearly see these
|
||||
% properties and filter types!
|
||||
|
||||
%% Filter Design (GUI)
|
||||
% Note: Ask Prof. Keene about `filterDesigner`, it'll make him
|
||||
% very happy.
|
||||
filterDesigner; % then export to workspace as numerator and
|
||||
% denominator (not SOS for now)
|
||||
|
||||
%% Filter Design (Code)
|
||||
% Manually specify specifications
|
||||
Fs = 44100;
|
||||
Apass = 1;
|
||||
Astop = 80;
|
||||
Fpass = 1e3;
|
||||
Fstop = 1e4;
|
||||
wpass = Fpass / (Fs/2); % normalized to nyquist freq.
|
||||
wstop = Fstop / (Fs/2); % normalized to nyquist freq.
|
||||
|
||||
% Compute the minimum order for Butterworth filter
|
||||
% that meets the specifications.
|
||||
% Alternatively, see `butter1ord`, `cheby1ord`,
|
||||
% `cheby2ord`, `ellipord`.
|
||||
n = buttord(wpass, wstop, Apass, Astop);
|
||||
|
||||
% Generate Butterworth filter with order `n` (which is
|
||||
% designed to meet the specification).
|
||||
% Alternatively, see `cheby1`, `cheby2`, `ellip`.
|
||||
[b, a] = butter(n, wpass);
|
||||
[H, W] = freqz(b, a);
|
||||
|
||||
% Plot the frequency response. (At this point, recall that
|
||||
% the frequency response is the FFT of the impulse response.
|
||||
% Can you generate the frequency response without using
|
||||
% the `freqz` function (instead using the `impz` and `fft`
|
||||
% functions)?
|
||||
f = W .* Fs/(2*pi);
|
||||
figure;
|
||||
semilogx(f, 20*log10(abs(H)));
|
||||
xlim([1e2 1e5]);
|
||||
ylim([-100 5]);
|
||||
grid on;
|
||||
title 'Butterworth Lowpass Filter';
|
||||
xlabel 'Frequency (Hz)';
|
||||
ylabel 'Magnitude (dB)';
|
||||
|
||||
%% Applying a filter
|
||||
% Note that if a filter is generated using a `filterDesigner` function,
|
||||
% it will be a filter object. You can apply a filter object using the
|
||||
% `filter()` function. If your filter is given as an impulse response, you
|
||||
% can either use the `filter()` function or convolve the impulse response
|
||||
% and the input signal.
|
||||
|
||||
% Load handel again
|
||||
load handel;
|
||||
|
||||
% Get a generated filter (this should decimate a narrow frequency band
|
||||
% centered at 2.5kHz, assuming a 8192Hz sampling frequency).
|
||||
flt = bandstop2500;
|
||||
|
||||
% Perform the filter
|
||||
y1 = filter(flt, y);
|
||||
|
||||
%% How does it sound now?
|
||||
sound(y1, Fs);
|
||||
|
||||
%% Plotting the result
|
||||
% Obtain the FFT of the filtered signal.
|
||||
N = 2^15;
|
||||
S = fft(y1, N);
|
||||
S = fftshift(abs(S)) / N;
|
||||
F = linspace(-Fs/2, Fs/2, N);
|
||||
|
||||
% Plot the FFT.
|
||||
figure;
|
||||
plot(F, S);
|
||||
title 'Fourier Transform of Audio';
|
||||
xlabel 'Frequency (Hz)';
|
||||
ylabel 'Magnitude';
|
93
lessons/lesson07/transfer.m
Normal file
93
lessons/lesson07/transfer.m
Normal file
@ -0,0 +1,93 @@
|
||||
%% Lesson 7c. Transfer function objects
|
||||
close all; clear; clc;
|
||||
|
||||
%% Transfer function overview
|
||||
% Transfer functions are abstract representations of LTI systems in terms of
|
||||
% rational functions (numerator and denominator). We used to work with transfer
|
||||
% functions in terms of MATLAB-style polynomials, but Mathworks has come up
|
||||
% with a new, supposedly improved way: transfer function objects! These are
|
||||
% created via `tf` in a variety of ways. For more info, see Mathworks'
|
||||
% documentation -- these models are most helpful when working with cascades of
|
||||
% systems, such as for controls modeling.
|
||||
|
||||
%% Digital transfer function objects
|
||||
% To create a digital tf. object, specify a numerator, denominator, and
|
||||
% sampling time (which may be set to -1 if you want to leave it undefined). The
|
||||
% numerator and denominator are specified in decreasing power order, with s^0
|
||||
% or z^0 on the right.
|
||||
dig_num = [1]; % 1z⁰ = 1
|
||||
dig_den = [2 -1]; % 2z¹ - 1z⁰ = 2z - 1
|
||||
timestep = 0.1; % 0.1 s
|
||||
dig_sys = tf(dig_num, dig_den, timestep);
|
||||
|
||||
%% Analog transfer function objects
|
||||
% Analog transfer functions are created the same way as digital ones, just
|
||||
% without the timestep.
|
||||
an_num = [1 -1]; % 1s¹ - 1s⁰ = 1s - 1
|
||||
an_den = [1 2 1]; % s² + 2s + 1
|
||||
an_sys = tf(an_num, an_den);
|
||||
|
||||
%% Alternate ways of specifying
|
||||
% MATLAB provides a syntax for creating transfer functions out of coded
|
||||
% rational expressions, as this can be clearer:
|
||||
s = tf('s'); % create an s variable that may then be used mathematically
|
||||
an_sys_2 = (s - 1)/(s + 1)^2; % same as above
|
||||
|
||||
%% Investigating the transfer function
|
||||
% One property of interest is the step response, which we can view thus:
|
||||
figure;
|
||||
stepplot(an_sys);
|
||||
|
||||
% MATLAB has a built-in function to get a Bode plot, too (though you should
|
||||
% modify this in post if you want to highlight specific details in the plot).
|
||||
figure;
|
||||
bodeplot(an_sys);
|
||||
|
||||
%% One application
|
||||
% This example is stolen from Mathworks' doc page "Control System Modeling with
|
||||
% Model Objects," as it's an interesting perspective on what you can do with
|
||||
% these. In it, we model the following system:
|
||||
%
|
||||
% x ---> F(s) ---> +( ) ---> C(s) ---> G(s) ---+---> y
|
||||
% - |
|
||||
% ^ |
|
||||
% \---------- S(s) ---------/
|
||||
|
||||
% specify the components of a system
|
||||
G = zpk([], [-1, -1], 1); % no zeroes, double pole at -1, analog
|
||||
C = pid(2, 1.3, 0.3, 0.5); % PID controller object
|
||||
S = tf(5, [1 4]);
|
||||
F = tf(1, [1 1]);
|
||||
|
||||
% find the open-loop transfer function (by breaking the loop at the subtract)
|
||||
open_loop = F*S*G*C; % multiplication in the "frequency" domain
|
||||
|
||||
% check out where the poles & zeroes are
|
||||
figure;
|
||||
pzplot(open_loop);
|
||||
title('Open loop pole/zero plot');
|
||||
xlim([-6 1]);
|
||||
ylim([-2 2]);
|
||||
|
||||
% see what this thing does with a step input
|
||||
figure;
|
||||
stepplot(open_loop);
|
||||
title('Open-loop system step response');
|
||||
% note that this is *not* stable with a step input, as the PID controller loses
|
||||
% its shit!
|
||||
|
||||
% find the closed-loop, whole_system response using `feedback` to specify a
|
||||
% feedback connection
|
||||
full_system = F*feedback(G*C, S);
|
||||
|
||||
% see what this thing looks like now in the s-plane
|
||||
figure;
|
||||
pzplot(full_system);
|
||||
title('Full system pole/zero plot');
|
||||
xlim([-6 1]);
|
||||
ylim([-2 2]);
|
||||
|
||||
% now stable with a step input, as we have feedback
|
||||
figure;
|
||||
stepplot(full_system);
|
||||
title('Full system step response');
|
Reference in New Issue
Block a user