As my teacher explained the process in class, I wanted to write a MATLAB code for learning purposes that downsamples a signal with a downsampling factor of L. Here's the code I wrote and the output that followed:
clear;
clc;
f0 = 5; % sin() frequency [Hz]
fc1 = 100; % sampling rate [Hz]
T1 = 1/fc1;
L = 2; % downsampling factor
fc2 = fc1/L; % lower sampling rate [Hz]
T2 = 1/fc2;
B = fc2/2; % anti-aliasing filter's band [Hz]
W = 0.1; % transition band width
N = 100; % sin() number of samples
% Let's design the anti-aliasing low-pass FIR filter with Parks McClellan
% method
delta = 10^(-7);
amp = [1,0];
freq =[B-W*B B];
deviaz =[delta, delta];
[N2,normal_freq,amp_resp,W] = firpmord(freq,amp,deviaz,fc1);
hlp=firpm(N2,normal_freq,amp_resp,W);
delay = mean(grpdelay(hlp)); % filter group delay
%fvtool(hlp,1);
% first sampling, at frequency fc1
times1 = (0:N-1)*T1;
samples1 = sin(2*pi*f0*times1);
% filtering the input signal (in this case a sin()) with the anti-aliasing
% filter and compensate for filter delay
samples1_filt = filter(hlp,1,[samples1 zeros(1,delay)]);
samples1_filt = samples1_filt(delay+1:end);
% downsampling by taking one sample each L samples (discarding L-1 samples
% each L samples)
times2 = (0:(N/L)-1)*T2;
samples2 = zeros(1,N/L);
for i = 0:N/L-1
samples2(i+1) = samples1_filt((i*L)+1);
end
subplot(3,2,1);
stem(times1,samples1,Color='#0072BD');
title(sprintf("fc1 = %d Hz",fc1));
subplot(3,2,2);
stem(times2 ,samples2, Color='#D95319');
title(sprintf("fc2 = %d Hz, L = %d",fc2,L));
subplot(3,2,[3,4]);
hold on
stem(times1 ,samples1);
stem(times2 ,samples2);
hold off
title(sprintf("fc1 (%d Hz) vs fc2 (%d Hz)",fc1,fc2));
legend("fc1","fc2");
subplot(3,2,[5,6]);
hold on
stem(times2 ,samples2, Color='#D95319');
samples2 = decimate(samples1,L,'fir'); % downsampling using built-in function
stem(times2 ,samples2, Color="#77AC30");
hold off
title("comparison with built-in function");
legend("fc2","fc2 built-in");
The program seems to be working, but upon closer inspection, there is a slight difference between the downsampled samples produced by the built-in function and the one that comes from my code. Furthermore, my samples don't start from zero.
So now, I have some questions:
- Is my code correct? If yes, why do I get these differences with the built-in function? Could it be because of the filter?
- How should an anti-aliasing filter be designed to achieve the best results? I understand that the filter needs to filter up to $\frac{fc2}{2}$, but does this mean that the band-pass should extend to $\frac{fc2}{2}$ (so the transition band will exceed this boundary), or should both the band-pass and the transition band be within $\frac{fc2}{2}$ like in my code above?
- How should I handle a non-integer group delay?
Thanks for the help
EDIT 1
With "How should I handle a non-integer group delay?" I mean, for example, choosing delta = 10^(-6);
in the filter project I get a low-pass FIR with a group delay of delay = 146.5
.
Since zeros()
only takes integer numbers I can't compensate for the delay introduced by the filter with:
samples1_filt = filter(hlp,1,[samples1 zeros(1,delay)]);
samples1_filt = samples1_filt(delay+1:end);
how should I handle this situation?
I tried to run the same program on a low-pass least-squared FIR filter generated by the following code:
hlp = firls(400,[0 0.45 0.5 1],[1 1 0 0]);
even playing with the filter order and adding some weights I get approximately the same error compared with the built-in function. Why the biggest difference between the built-in function and mine is only on the first sample?