Lab 4
Lab 4
This lab will explore the use of MATLAB functions and investigate the effect of sampling rate on
the Fourier transform of a signal.
Functions:
MATLAB allows for user-defined functions to be defined and to be invoked from within another
M-file script. The following example shows how this is done:
Example 1: A function which returns the incremented value of its passed argument.
We define a MATLAB function by writing a M-file containing its definition. For example, the
above code is saved in a file called inc.m. By convention, the name of the M-file is usually the
same as the name of the function (inc in this particular example) . The word function in
MATLAB is a reserved keyword and is used to define a function. Let us examine the first line of
this function in detail :
inc_x : The return value of the function. The function will return the last value assigned to this
variable.
inc: The name of the function, usually the same name as the M-file containing the definition of
the function, but it may be any syntactically correct name in MATLAB.
(x): The argument(s) passed to the function. In this case there is only one argument called x. If
a function has more than one argument, each would be separated by a comma.
2
The comment line immediately after the name of the function is referred to as the H1 line and is
used to describe the behaviour of the function. The H1 line and any subsequent comment lines
up to the first non-comment line constitute the help section of the function and will be displayed
when one types help inc from the MATLAB prompt1 :
Example 2: A function may be invoked by simply typing it’s name (and passing an argument to
it):
>> x = 5
x =
ans =
clear
x = 10;
disp(‘The value of this x is before calling inc is: ‘)
disp(x)
ans = inc(x) ;
disp(‘The value of this x after calling inc is: ‘)
disp(x)
>> example2
The value of this x is before calling inc is:
10
11
This example illustrates what is known as the “call-by-value” parameter passing mechanism
which MATLAB uses when the passed argument is modified by a function. The function receives
a copy of the actual argument x and during the course of execution of the function, it modifies
only the copy of the function. The argument x inside the function is said to be a local argument
and furthermore the lifetime of any local arguments (and variables declared local to a function) is
limited to the runtime of the function. Examine your workspace after running the script given in
Example 2 and you will see there are only two variables: x with value of 10 and ans with value
of 11.
Example 3: Pass-by-reference.
array_squared = x .^ 2 ;
The function receives an array x as an argument, and it returns an array containing the square of
the elements in the passed array. Is this a call-by-value? When a function only reads the value of
an argument (and does not modify it), MATLAB employs what is know as a “pass-by-reference”
parameter passing mechanism. Instead of copying the argument (as is done in pass-by-value),
MATLAB passes only the memory address containing the argument. This is done in the interest
of efficient use of memory and run-time. It would be very time-consuming and unneccesarily use
lots of memory to copy a 100 000 000 element array into a local argument of a function. If a
function modifies a passed argument ( by assigning a value to it for example), then the pass-by-
value mechanism is used. This has the benefit of avoiding any potential mistakes whereby a func-
tion inadvertently changes the value of an argument; the function will only be changing a copy of
the argument and not the actual argument. Of course, if we wish to change the value of an argu-
ment (as a result of a function), then this must be done by assigning a new value to the argument
(through the return value of the function) as shown in the next example.
Suppose we were to define an array called y in our workspace (after having cleared it):
>> clear
>> y = [ 1 : 5 ]
y =
1 2 3 4 5
Next, we pass y (by value) to the function and assign the return value of the function back to vari-
able y:
>> y = clear_odd2(y)
y =
0 2 0 4 0
What do you suppose would be the output if we were to invoke this function 5 times within a for
loop as in:
for index = 1 : 5
count_me_up;
end loop
ans =
ans =
5
ans =
ans =
ans =
The reason for this behaviour is that the variable count which is local to function
count_me_up is initialized to 0 upon every function invocation. If we wish for a variable
within a function to be initialized only once (the first time the function is called) and for it’s value
to persist across different function invocations, then we must declare the variable to be persis-
tent as in:
persistent count = 0 ;
count = count + 1 ;
out = count;
If this file were defined in a script with filename count_me_up2.m, we would invoke it in a for
loop as in:
persistent count = 0 ;
Defining a variable to be persistent will result in an initially empty array being assigned to the
variable 2 . The above statement is equivalent to doing:
>> count = [] = 0
count = [] = 0
|
Error: The expression to the left of the equals sign is not a
valid target for an assignment.
The solution is to leave the variable initialized to an empty array, then use the isempty function
(which returns true if the argument is empty and false otherwise) to test for emptiness and if
empty to set the starting value of count to 0 as in:
persistent count ;
count = count + 1 ;
out = count;
When invoked from our for loop, the value of count now lives across the 5 different times we call
the function (assuming we saved the above function in a file called count_me_up3.m) :
ans =
ans =
2
7
ans =
ans =
ans =
It should be readily apparent that MATLAB persistent variables are analogous the static variables
in C++ functions (although the method of assigning a persistent variable in MATLAB is some-
what cumbersome compared to the initialization of static variables in C++ ).
If a variable is to be used by many functions, then it is convenient to define the variable as a global
variable. A global variable is visible to visible to all functions. The keyword global is placed
in front of a variable name in order to declare it as a global variable. The following example
defines a global variable called counter and two functions which modify the value of the global
variable.
clear;
global counter ; % declare counter to be global
counter = 0 ;
disp(’Value of counter = ’);
disp(counter);
count_up;
count_up;
disp(’Value of counter = ’);
disp(counter);
count_down;
disp(’Value of counter = ’);
disp(counter);
function [] = count_up
% add 1 to the global variable counter
function [] = count_down
% subtracts 1 to the global variable counter
Sampling Theorem:
Consider a discrete time sinusoidal signal x(n) = sin( (2*pi)/N * n ). The value of N defines the
number of samples per period, and n defines the total number of data points stored. For example,
with N = 4 and n = 8 we are sampling the signal 4 times per period over a total of 8 data points,
which is equal to two periods. We can perform this in MATLAB with the following code:
>> n = [ 0 : 7 ];
>> N = 4 ;
>> x = sin((2*pi)/N * n);
0.8
0.6
0.4
0.2
x(n)
−0.2
−0.4
−0.6
−0.8
−1
0 1 2 3 4 5 6 7
n
Figure 1: Sampling a sine wave with 4 samples per period over 2 periods.
With 8 samples per period, the sampled signal more closely resembles a sine wave as shown in
Figure 2:
9
0.8
0.6
0.4
0.2
x(n)
−0.2
−0.4
−0.6
−0.8
−1
0 5 10 15
n
Figure 2: Sampled sine wave with 8 samples per period over two periods.
The rate at which a signal is sampled has an effect on it’s Fourier transform as shown by Figure 3
and 4; the higher the sampling rate the more the replicas of X(jw) spread apart. The transforms
are plotted over the interval -3*pi to 3*pi.
0.5
x[n]
−0.5
−1
0 1 2 3 4 5 6 7
n
3
X[jw]
0
−10 −8 −6 −4 −2 0 2 4 6 8 10
w
0.5
x[n]
−0.5
−1
0 5 10 15
n
6
X[jw]
0
−10 −8 −6 −4 −2 0 2 4 6 8 10
w
In order to have no overlap ( referred to as aliasing), it is necessary to sample the signal at a rate at
least twice that of the highest frequency component in the signal. This is known as the Nyquist
rate. In terms of samples per period, the Nyquist rate corresponds to 2 samples per period.
Questions
Question 1:
(a) This question will investigate the effect of varying the sampling rate on the transform of the
sampled signal. Write a MATLAB script which will prompt the user to specify the number of
periods and the step size over which the transform is to be computed. This will define the w array
as
[ -n *pi : step_size : n*pi ] where n is the number of periods. The array w should
be declared to be global.
Re-write your code used in Lab 3 as a function which receives a signal x(n) and returns the
Discrete Time Fourier Transform of the signal. Make use of a function to compute the value of N
for a given value of sampling rate.
The program is to ask the user to input the value of the sampling rate (in terms of the number of
times of the Nyquist rate, i.e. 2 times, 3.6 times, etc). The program then plots in one figure win-
dow the sampled signal (using stem) and the Fourier transform of the sampled signal. This is to be
repeated 5 times.
11
ans =
Make use of the floor function when computing N to handle the case of non-integer values of
sampling rate. For example, if we want to sample at 2.8 times the Nyquist rate, we have
The figure function is used to plot multiple graphs in separate figure windows.
(b) To investigate the effect of changing the window size (i.e. how many samples of the signal are
obtained at some fixed sampling rate) we will rewrite the code in part (a) to ask the user to input
the sampling rate (in terms of of the Nyquist rate). This sampling rate will be kept constant and
the program is to ask the user to enter the window size (in terms of the number periods of the sig-
nal). The sampled signal (over the total number of periods as defined by the value of the window
12
size) and its Fourier transform is to be plotted in one figure window. This is to be repeated 5 times
with a different value for the window size in each loop iteration. The pseudocode is as follows:
for index = 1 : 5
input the window size (in terms of the number of periods of the signal);
determine n (total number of samples obtained based upon sampling rate and
window size. n = window size x N ) ;
w = [ -(window size) * pi : 0.05 : pi*(window_size) ]; (for simplicity keep
step size fixed )
compute x[n] = sin(2*pi/N * n) ;
plot the signal over the window size ;
compute the Fourier transform of the signal (by passing x as an argument to
your function);
plot the transform of the signal;
end
Question 2:
Polar plots are useful for plotting signals which are periodic. For example, consider a “full-wave
rectified’” sine wave (one in which the negative portions have been “inverted”). Such a signal can
be readily created and plotted in MATLAB as follows:
n = [ 0 : 0.1 : 6*pi ] ;
x = sin(n);
x_rectified = abs(x) ; % obtain a "full-wave rectified" version
% of the sine wave
subplot(2,1,1)
stem(n,x_rectified)
xlabel(’n’)
ylabel(’x[n]’)
subplot(2,1,2)
polar(n, x_rectified);
Instead of using Cartesian coordinates, the polar(angle,r) command makes use of polar
coordinates and plots the angle in radians against the radius r). A help polar gives the fol-
lowing:
Note that negative values of RHO are reflected through the origin and are rotated by 180 degrees.3
This is the reason why a polar plot of a sine wave consists of a single circle.
The plots produced by the sample script are shown in Figure 5 where we can clearly see the peri-
odic nature of the signal in the polar plot representation.
0.8
0.6
x[n]
0.4
0.2
0
0 2 4 6 8 10 12 14 16 18 20
n
90 1
120 60
150 0.5 30
180 0
210 330
240 300
270
Repeat Question 1(a) using polar plots for the transform instead of rectangular plots. Use the fol-
lowing for the input signal:
Question 3:
Sometimes a signal is a distorted version of another signal, for example when a signal passes
through a communication channel or when a signal is compressed. In order to recover the original
signal from the distorted one, we can use a LTI system. This system receives the distorted signal
and returns the recovered one. This LTI system can be designed in frequency domain or time
domain depending on the type of distortion.
To measure the difference between two signals we use mean square error (MSE) as a metric. The
MSE for given signals x[k] and y[k] with length of L is defined as:
L
MSE = ---
1
L ∑ ( x[ k ] – y[ k ] )2
k=1
Two files: Original.wav and Distorted.wav provide the original signal with length of 2 seconds
and its distorted version, respectively. The sampling frequency for these audio signals is Fs =
22050 Hz and number of bits per sample is 16. Thus, the number of samples in each signal is L =
44100. To load audio files into your programs you can use function wavread as:
sound(x, Fs , numBitsPerSample);
To write an audio signal into a file you can use wavwrite function as:
wavwrite(x,Fs,bits,’Recovered.wav’);
(a) Load two files Original.wav and Distorted.wav to your program and plot them in time
domain.
(b) Use MSE to compare the original signal and the distorted.
(c) Design a system that recovers the original signal from the distorted signal and save it to a file
called Recovered.wav. Which domain did you choose for the design: frequency domain or time
domain? Why?
(d) Compute the MSE between the recovered signal and the original signal. Does your system
improve the MSE?
15
(e) By playing and listening to recovered signal, does your system improve the quality of the
sound?
References
Revised: May 25, 2017. Added Question 3 created by H. Kourkchi and Dr. W. Lynch.
T. Obuchowicz
May 30, 2014