FFT of ECG signal in MATLAB - matlab

This is the input signal :
plot(abs(fft(ecg)))
I have also tried
fvtool(x_vals)
which gave me :
However I want the x axis in Hz. So essentially I want to see the frequency spectrum of this signal in Hz.
Thanks!

function [f amp] = getspectrum( Mdata, Mf )
% Mdata data
% Mf sampling rate / frequency (Hz)
NFFT = 2 ^ nextpow2(length(Mdata));
Y = fft(double(Mdata), NFFT) / length(Mdata);
f = (double(Mf) / 2 * linspace(0, 1, NFFT / 2))'; % Vector containing frequencies in Hz
amp = 2 * abs(Y(1:(NFFT / 2))); % Vector containing corresponding amplitudes
I hope this might be of help.

Related

Scaling amplitudes by two for the FFT in MATLAB

I just read the example of Mablab tutorial, trying to studying the FFT function.
Can anyone tell me that for the final step, why P1(2:end-1) = 2*P1(2:end-1). In my opinion, it is not necessary to multiply by 2.
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 1000; % Length of signal
t = (0:L-1)*T; % Time vector
%--------
S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
%---------
X = S + 2*randn(size(t));
%---------
plot(1000*t(1:50),X(1:50))
title('Signal Corrupted with Zero-Mean Random Noise')
xlabel('t (milliseconds)')
ylabel('X(t)')
Y = fft(X);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
f = Fs*(0:(L/2))/L;
plot(f,P1)
title('Single-Sided Amplitude Spectrum of X(t)')
xlabel('f (Hz)')
ylabel('|P1(f)|')
Y = fft(S);
P2 = abs(Y/L);
P1 = P2(1:L/2+1);
P1(2:end-1) = 2*P1(2:end-1);
plot(f,P1)
title('Single-Sided Amplitude Spectrum of S(t)')
xlabel('f (Hz)')
ylabel('|P1(f)|')
Matlab sample
The reason for the multiplication by 2 is that the spectrum returned by fft is symmetric about the DC component. Since they are showing the single-sided amplitude spectrum, the amplitude of each point is going to be doubled to account for the contributions of data on the other side of the spectrum. For example, the single-sided amplitude of pi/4 is the amplitude at pi/4 plus the amplitude at -pi/4.
The first sample is skipped since it is the DC point and therefore shared between the two sides of the spectrum.
So for example, if we look at the fft of their example signal with a 50Hz sinusoid of amplitude 0.7 and a 120Hz sinusoid of amplitude 1.
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 1000; % Length of signal
t = (0:L-1)*T; % Time vector
S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
% Compute the FFT
Y = fft(S);
% Compute the amplitudes
amplitude = abs(Y / L);
% Figure out the corresponding frequencies
f = Fs/L*[0:(L/2-1),-L/2:-1]
% Plot the result
plot(f, amplitude)
When we plot this, you'll see that it's symmetric and the original input amplitude is only realized by combining the amplitudes from both sides of the spectrum.
A slightly more explicit version of what they have done would be to be the following which sums the two halves of the spectrum
P1(2:end-1) = P1(2:end-1) + P2((L/2+2):end);
But since by definition the spectrum is symmetric, the opt to simply multiply by 2.

Finding the phase of each harmonics using fft

I use Matlab.
I have a sinusoidal signal :
X (amp:220/ Freq:50)
to which I add 3 harmonics :
x1 => (h2) amp:30 / Freq:100 / phase:30°
x2 => (h4) amp:10 / Freq:200 / phase:50°
x3 => (h6) amp:05 / Freq:300 / phase:90°
I sum all the signals together (like X containing 3 harmonics), the resulting signal is called : Xt
Here is the code :
%% Original signal
X = 220.*sin(2 .* pi .* 50 .* t);
%% Harmonics
x1 = 30.*sin(2 .* pi .* 100 .* t + 30);
x2 = 10.*sin(2 .* pi .* 200 .* t + 50);
x3 = 05.*sin(2 .* pi .* 300 .* t + 90);
%% adding the harmonics
Xt = X + x1 + x2 + x3;
What I want to do is : find the 3 harmonics signal (their amplitude, frequency and phase) starting for the summed signal Xt and knowing the fundamental signal X (amplitude and frequency) !
So far, I was able using fft, to retrieve the frequencies and the amplitudes of the harmonics, the problem now is finding the phases of the harmonics (in our case : 30°, 50° and 90°).
The FFT returns you an array consisting of complex numbers. To define phases of the frequency components you need to use angle() function for complex numbers. Don't forget: the phase of your harmonics has to be given in radians.
Here is the code:
Fs = 1000; % Sampling frequency
t=0 : 1/Fs : 1-1/Fs; %time
X = 220*sin(2 * pi * 50 * t);
x1 = 30*sin(2*pi*100*t + 30*(pi/180));
x2 = 10*sin(2*pi*200*t + 50*(pi/180));
x3 = 05*sin(2*pi*300*t + 90*(pi/180));
%% adding the harmonics
Xt = X + x1 + x2 + x3;
%Transformation
Y=fft(Xt); %FFT
df=Fs/length(Y); %frequency resolution
f=(0:1:length(Y)/2)*df; %frequency axis
subplot(2,1,1);
M=abs(Y)/length(Xt)*2; %amplitude spectrum
stem(f, M(1:length(f)), 'LineWidth', 0.5);
xlim([0 350]);
grid on;
xlabel('Frequency (Hz)')
ylabel('Magnitude');
subplot(2,1,2);
P=angle(Y)*180/pi; %phase spectrum (in deg.)
stem(f, P(1:length(f)), 'LineWidth', 0.5);
xlim([0 350]);
grid on;
xlabel('Frequency (Hz)');
ylabel('Phase (degree)');
It will result in such a mess (but you can see your amplitudes very well):
You can see a lot of phase components on the second plot. But if you eliminate all the frequencies which correspond to zero amplitudes, you will see your phases.
Here we are:
Y=fft(Xt); %FFT
df=Fs/length(Y); %frequency resolution
f=(0:1:length(Y)/2)*df; %frequency axis
subplot(2,1,1);
M=abs(Y)/length(Xt)*2; %amplitude spectrum
M_rounded = int16(M(1:size(f, 2))); %Limit the frequency range
ind = find(M_rounded ~= 0);
stem(f(ind), M(ind), 'LineWidth', 0.5);
xlim([0 350]);
grid on;
xlabel('Frequency (Hz)')
ylabel('Magnitude');
subplot(2,1,2);
P=angle(Y)*180/pi; %phase spectrum (in deg.)
stem(f(ind), P(ind), 'LineWidth', 0.5);
xlim([0 350]);
ylim([-100 100]);
grid on;
xlabel('Frequency (Hz)');
ylabel('Phase (degree)');
Now you can see the phases, but all of them are shifted to 90 degrees. Why? Because the FFT works with cos() instead of sin(), so:
X = 220*sin(2*pi*50*t + 0*(pi/180)) = 220*cos(2*pi*50*t - 90*(pi/180));
UPDATE
What if the parameters of some signal components are not integer numbers?
Let's add a new component x4:
x4 = 62.75*cos(2*pi*77.77*t + 57.62*(pi/180));
Using the provided code you will get the following plot:
This is not really what we expected to get, isn't it? The problem is in the resolution of the frequency samples. The code approximates the signal with harmonics, which frequencies are sampled with 1 Hz. It is obviously not enough to work with frequencies like 77.77 Hz.
The frequency resolution is equal to the inversed value of the signal's time. In our previous example the signal's length was 1 second, that's why the frequency sampling was 1/1s=1Hz. So in order to increase the resolution, you need to expand the time window of the processed signal. To do so just correct the definition of the vaiable t:
frq_res = 0.01; %desired frequency resolution
t=0 : 1/Fs : 1/frq_res-1/Fs; %time
It will result in the following spectra:
UPDATE 2
It does not matter, which frequency range has to be analyzed. The signal components can be from a very high range, what is shown in the next example. Suppose the signal looks like this:
f=20e4; % 200 KHz
Xt = sin(2*pi*(f-55)*t + pi/7) + sin(2*pi*(f-200)*t-pi/7);
Here is the resulting plot:
The phases are shifted to -90 degrees, what was explained earlier.
Here is the code:
Fs = 300e4; % Sampling frequency
frq_res = 0.1; %desired frequency resolution
t=0 : 1/Fs : 1/frq_res-1/Fs; %time
f=20e4;
Xt = sin(2*pi*(f-55)*t + pi/7) + sin(2*pi*(f-200)*t-pi/7);
Y=fft(Xt); %FFT
df=Fs/length(Y); %frequency resolution
f=(0:1:length(Y)/2)*df; %frequency axis
subplot(2,1,1);
M=abs(Y)/length(Xt)*2; %amplitude spectrum
M_rounded = int16(M(1:size(f, 2))); %Limit the frequency range
ind = find(M_rounded ~= 0);
stem(f(ind), M(ind), 'LineWidth', 0.5);
xlim([20e4-300 20e4]);
grid on;
xlabel('Frequency (Hz)')
ylabel('Magnitude');
subplot(2,1,2);
P=angle(Y)*180/pi; %phase spectrum (in deg.)
stem(f(ind), P(ind), 'LineWidth', 0.5);
xlim([20e4-300 20e4]);
ylim([-180 180]);
grid on;
xlabel('Frequency (Hz)');
ylabel('Phase (degree)');
To start off we should note (as you correctly found out in comments) that Matlab uses radians for angles, so the harmonics should be:
%% Harmonics
x1 = 30.*sin(2 .* pi .* 100 .* t + 30*pi/180);
x2 = 10.*sin(2 .* pi .* 200 .* t + 50*pi/180);
x3 = 05.*sin(2 .* pi .* 300 .* t + 90*pi/180);
The simple case
The process of estimating the amplitude, frequency and phase of the frequency components will usually start off with taking the Fast Fourier Transform (FFT), and selecting the strongest frequency components:
% Compute the frequency spectrum
N = length(Xt);
Xf = fft(Xt);
Nmax = N/2 + 1;
Xf = Xf(1:Nmax);
% Locate the peaks
largest_peak = max(20*log10(abs(Xf)));
peak_floor = largest_peak - 100; % to reject peaks from spectral leakage and noise
[pks,idx] = findpeaks((max(peak_floor, 20*log10(abs(Xf))) - peak_floor)')
Now if the fundamental frequency and the frequency of the harmonics happens to be exact multiples of fs/N where fs is the sampling rate and N is the number of samples (in this case length(Xt)) then the tones will fall exactly on a bin, and the frequencies, amplitudes and phases of each component can be estimated fairly easily with:
Amp = 2*abs(Xf(idx))/N;
freq = (idx-1)*fs/N;
phase = angle(Xf(idx));
phase = phase - phase(1); % set phase reference to that of the fundamental
The usual and more complicated reality
If on the other hand the frequency components are not exact multiples of fs/N, (or at least are not known to be exact multiples of fs/N, you are after all trying to estimate the frequency of those components) then things get more complicated. Note that this can have a particularly significant effect on the phase estimate.
We start off by recalling that a pure complex tone (exp(2*pi*j*n*f/fs)) of finite length N has a Discrete Fourier Transform (DFT) given by:
One estimation approach could be to start by estimating the frequency. The amplitude and phase can be factored out by looking at the ratio of the magnitudes of two successive bins of Xf around the peak, mainly at indices idx(i) and idx(i)+1. Under the assumption that those two bins suffer little interference, then the ratio can be expressed as:
ratio = abs(Xf(idx(i)+1)/Xf(idx))
= abs(sin(pi*frac/N)/sin(pi*(frac-1)/N))
Where the frequency to be estimated is f = (idx(i)-1 + frac)*fs/N. The parameter frac can then be obtained with the Newton-Raphson method:
% Solve for "f" for which ratio = sin(pi*frac/N)/sin(pi*(frac-1)/N)
function f = fractional_frequency(ratio, N)
niter = 20;
K = (pi/N) * sin(pi/N);
f = 0;
for i=1:niter
a = sin(pi*f/N);
b = sin(pi*(f-1)/N);
y = ratio - a/b;
yp = K / (b^2);
f = max(-0.5, min(f - y/yp, 0.5));
end
end
Which we use to estimate the frequency with:
freq = zeros(1,length(idx));
for i=1:length(idx)
ratio = abs(Xf(idx(i)+1))/abs(Xf(idx(i)));
if (abs(Xf(idx(i)+1)) > abs(Xf(idx(i)-1)))
ratio = -ratio;
end
frac = fractional_frequency(ratio, N)
freq(i) = (idx(i)-1+frac)*fs/N;
end
Now that we have the tone frequency, we can obtain the amplitude and phase by fitting the DFT equation given above (where we also add a factor of 2 for the amplitude by since we are dealing with a real tone):
Amp(i) = 2 * abs(Xf(idx(i))) * abs(sin(pi*frac/N)/sin(pi*frac));
phase(i) = angle( Xf(idx(i)) .* (1-exp(2*pi*frac*j/N)) ./ (1-exp(2*pi*frac*j)) );
And putting it all together:
Amp = zeros(1,length(idx));
freq = zeros(1,length(idx));
phase = zeros(1,length(idx));
for i=1:length(idx)
ratio = abs(Xf(idx(i)+1))/abs(Xf(idx(i)));
if (abs(Xf(idx(i)+1)) > abs(Xf(idx(i)-1)))
ratio = -ratio;
end
frac = fractional_frequency(ratio, N)
freq(i) = (idx(i)-1+frac)*fs/N;
Amp(i) = 2 * abs(Xf(idx(i))) * abs(sin(pi*frac/N)/sin(pi*frac));
phase(i) = angle( Xf(idx(i)) .* (1-exp(2*pi*frac*j/N)) ./ (1-exp(2*pi*frac*j)) );
end
phase = phase - phase(1); % set phase reference to that of the fundamental

how to get fourier transform of a signal

In the below code I am trying to get the fourier transform of a stationary signal (x3). But at run time the plot i get is absolutely something wrong and does not show any frequencies of the signals x3.
kindly please guide me and help me to get the fourier transform correctly.
Code:
%% Time specifications:
Fs = 8000; % samples per second
dt = 1/Fs; % seconds per sample
StopTime = 1; % seconds
t = (0:dt:StopTime-dt); % seconds
x1 = (10)*cos(2*pi*3*(t));
x2 = x1 + (10)*cos(2*pi*5*(t));
x3 = x2 + (10)*cos(2*pi*10*(t));
%% here i try to Plot fourier transform of the signal x3:
NFFT = 2^nextpow2(StopTime); % Next power of 2 from length of y
Y = fft(y,NFFT)/StopTime;
f = Fs/2*linspace(0,1,NFFT/2+1);
figure;
plot(f,2*abs(Y(1:NFFT/2+1)));
%% Plot the signal versus time:
figure;
hold on;
plot(t,x1,'r');
plot(t,x2,'g');
plot(t,x3,'b');
Update_1
You can not see what you expected because the value of NFFT is 1 means when you write NFFT/2+1 as an index of Y it will not be an integer value so MATLAB warns you. You can calculate NFFT like this:
NFFT = 2^nextpow2(length(t))
instead of writing
NFFT = 2^nextpow2(StopTime)
Well, try this:
Fs = 8000; % samples per second
dt = 1/Fs; % seconds per sample
StopTime = 1; % seconds
t = (0 : dt : StopTime-dt); % seconds
x1 = 10 * cos(2*pi*3*t);
x2 = x1 + 10 * cos(2*pi*5*t);
x3 = x2 + 10 * cos(2*pi*10*t);
%% here i try to Plot fourier transform of the signal x3:
NFFT = 2 ^ nextpow2(length(t)); % Next power of 2 from length of y
Y = fft(x3, NFFT) / StopTime;
f = Fs / 2 * linspace(0, 1, NFFT/2 + 1);
figure;
plot(f, 2 * abs( Y( 1:length(f) ) ) ); % // Also try this: plot(f(f <= 200), 2 * abs( Y( 1:length(f(f <= 200)) ) ) )
%% Plot the signal versus time:
figure;
hold on;
plot(t, x1, 'r');
plot(t, x2, 'g');
plot(t, x3, 'b');
Plots:
EDIT:
1- Actually you don' t have to use nextpow() function. If you use it, fft() function works faster. Because, due to time efficiency, fft() works like that recursively divide the signal by 2 for each time. Then calculates discrete fourier transform for each part and gather them. This means that the FFT is most efficient when the signal vector length is a power of 2.
2- Dividing fft result by StopTime part doesn' t make any sense to me either. Dividing fft result by NFFT may be more convenient theoretically.

Low pass filtering not working as expected

The following Matlab script is for filtering a signal consisting of a 50 Hz and 120 Hz sine. I am calculating the frequnecy in rad/s as Fp= (2*PI * 30)/1000=0.184.
I have kept fp=0.184 and fst=0.185, as I want to filter out both 50 hz and 120 Hz.
But when I am plotting the FFT of the output of the filter, what I am getting is a sine at 50 Hz. Why this 50Hz sine is coming even after filtering?
Ideally there should not be any peak in the plot.
Before filtering
after filtering
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sample time
L = 1000; % Length of signal
t =(0:L-1)*T; % Time vector
x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t); % Sum of a 50 Hz sinusoid and a 120 Hz sinusoid %
y = x + 2*randn(size(t)); % Sinusoids plus noise
y = x ;
plot(Fs*t(1:50),y(1:50));title('Signal');xlabel('time (milliseconds)')
%pause;
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L; f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
plot(f,2*abs(Y(1:NFFT/2+1)))
title('Single-Sided Amplitude Spectrum of y(t)');xlabel('Frequency(Hz)');ylabel('|Y(f)|')
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Now let us see Low Pass Filtering of this signal
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Fp= (2*pi * 30)/1000; %=0.184 %only frequncies less than 30Hz will be passed
d=fdesign.lowpass('Fp,Fst,Ap,Ast',0.184,0.185,2,60);
designmethods(d);
Hd = design(d,'equiripple'); fvtool(Hd);
Filterd_Output = filter(Hd,y);
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Filtered_Freq = fft(Filterd_Output,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
plot(f,2*abs(Filtered_Freq(1:NFFT/2+1)))
title('Single-Sided Amplitude Spectrum of Low Pass Filtered_Output')
xlabel('Frequency (Hz)');ylabel('|Filtered_Freq_Amplitude|')
Update
As suggested I compared the original spectrum with the filterd one. This explains me a lot. But is there any way so that I can reduce this spike at 50 Hz further?
You incorrectly specify the normalized frequencies for the filter. Matlab assumes the frequency to be in [0, 1], not in [0, pi].
Replace
d=fdesign.lowpass('Fp,Fst,Ap,Ast',0.184,0.185,2,60);
with
d=fdesign.lowpass('Fp,Fst,Ap,Ast', 2*30/Fs, 2*35/Fs,2,60);
or alternatively
d=fdesign.lowpass('Fp,Fst,Ap,Ast', 30, 35,2,60, Fs);
and it should work as expected.

Need help re: FFT output scaling

I'm currently studying the following book: "Fourier Transform Spectroscopy Instrumentation Engineering", by Vidi Saptari. My question is related to the code below, based on the code from the book, Appendix C. The code below computes the interferogram of 3 waves with wavenumbers [cm-1] 5000, 10000 and 15000, respectively, and than performs an FFT to retrieve the information. The unscaled output has a magnitude of 1600, instead of 1.
clear;
% Sampling clock signal generation
samp_period_nm = 632.8 / 4; % sampling period in nm. 632.8 is the HeNe laser's wavelength
samp_period = 1 * samp_period_nm * 10^-7; % sampling period in cm.
scan_dist = 0.1; % mirror scan distance in cm.
no_elements = floor(scan_dist/samp_period);
x_samp = 0:samp_period:samp_period*no_elements; %Vector of clock signals in cm
xn_samp = x_samp .* (1 + rand(1, length(x_samp)));
v1 = 5000;
v2 = 10000;
v3 = 15000;
arg = 4 * pi * x_samp;
y = cos(arg*v1) + cos(arg*v2) + cos(arg*v3) ;
total_data = 2^18;
no_zero_fills=[total_data - length(y)];
zero_fills=zeros(1, no_zero_fills);
%triangular apodization
n_y = length(y);
points = 1:1:n_y;
tri = 1 - 1/(n_y) * points(1:n_y);
y = y.*tri; %dot product of interferogram with triangular apodization function
y = [y zero_fills]; %zero filling
% FFT operation
fft_y = fft(y);
% fft_y = fft_y / n_y;
% fft_y = fft_y * samp_period;
fft_y(1) = [];
n_fft=length(fft_y);
spec_y = abs(fft_y(1:n_fft/2)); %spectrum generation
nyquist = 1 / (samp_period * 4);
freq = (1:n_fft/2)/(n_fft/2)*nyquist; %frequency scale generation
figure();
plot(freq, spec_y); % plot of spectrum vs wave number
xlabel('Wavenumber [cm-1]');
ylabel('Intesity [V]');
By multiplying the result of the fft (fft_y) with dt = samp_period, as suggested here, the peak is as 0.025.
Following the same link's second solution, by dividing fft_y by n_y (the length of y), the magnitude is 0.25.
Clearly, I'm doing something wrong. Any help is appreciated.
Thanks,
The only thing you're doing wrong here is expecting the peaks in the spectrum to be 1. According to Parseval's theorem of DFT the energy of the time domain signal is equal to the energy of the frequency domain signal divided by the lenght of the sequence N. You can check this in your example:
td_energy = sum( abs(y).^2 )
fd_energy = sum( abs(fft_y).^2 )
td_energy - fd_energy / length(y) % won't be exactly zero because you deleted the zero frequency bin.
So the peaks in your spectrum don't represent the amplitudes of cosine waves in the time domain but their energy. Also note at this point that the energy is lower than you might expect as you padded a lot of zeros.
In practice, the average power of a certain frequency is often of greater interest. Consider the following code example
t = linspace(-4*pi, 4*pi, 2^16);
N = length(t); % DFT length
y = cos(t); % single cosine wave
y_pow = sum( abs(y).^2 ) / N; % is 0.5
fft_y = fft(y);
fft_y_pow = (sum( abs(fft_y).^2 ) / N) /N; % is 0.5
figure; plot(abs(fft_y)./N);
The power is obtained by averaging the energy by the length of the sequence N. If you divide the spectrum by N you obtain the average power per frequency. In the above example you recognize a single peak with height 0.5 that represents the single cos wave of amplitude 1 (and hence power 0.5).
Personally, I prefer scaling MATLAB's FFT output by 1/sqrt(N) and its IFFT output by sqrt(N). In this way, the energy of the time and frequency domain sequence are always equal.
if you want the same energy in the IFFT input and ouput you have to multiply
the IFFT output (time signal) by sqrt(N) where N is the size of the transform.
here's the code :
same thing for the FFT (divide output by sqrt(N));
hope it helps
Felix
N = 4096;
Freq = randn(N,1)+1j*randn(N,1);
Time = sqrt(N)*ifft(Freq,N);
FreqEn = sum(real(Freq).^2 + imag(Freq).^2);
TimeEn = sum(real(Time).^2 + imag(Time).^2);
TimeEn/Freq