How to plot bode diagram of a signal using fft in MATLAB? - matlab

I want to plot bode diagram of the following system both using bode and fft:
%// System info
num=[0 1]; %// Numerator of z-transform of impulse response of system
den=[1 -0.8]; %// Denominator of z-transform of impulse response of system
I used dbode to plot bode method:
figure(6); dbode(num,den,1) %// 1 is sampling time Ts
As I want to do it from fft method, it gets wrong:
Ts=1;
Fs=1/Ts;
L=length(ym);
NFFT = 2^nextpow2(L); %// Next power of 2 from length of ym
H2=fft(ym,NFFT)./fft(u,NFFT);
f=Fs/2*linspace(0,1,NFFT/2+1);
ww=f*2*pi;
figure(7)
semilogx(20*log10(abs(H2(1:NFFT/2+1))))
figure(10)
semilogx((180/pi)*angle(H2(1:NFFT/2+1)))
Bode diagram using bode:
Any idea
Here is my data (u and ym)

I looked at your data and compared it with the theoretical transfer function in the time-domain and it isn't a bad fit if you ignore some of the data:
t = 1:length(u);
num=[0 1]; %// Numerator of z-transform of impulse response of system
den=[1 -0.8]; %// Denominator of z-transform of impulse response of system
H = tf(num,den,1)
[yy,tt,xx] = step(H,max(t));
plot(t-10,ym-2.2,tt,yy)
You'll notice that I have discarded the time values before 10 and shifted the response values down by about 2.2. This gives the following plot (in Octave):
I suggest you do the same thing when taking the FFT:
L = length(ym(t>=10));
NFFT = 2^nextpow2(L);
H2 = fft(ym(t>=10)-2.2,NFFT)./fft(u(t>=10),NFFT);
f=Fs/2*linspace(0,1,NFFT/2+1);ww=f*2*pi;
[mag,ph,w ] = bode(H);
semilogx(ww,20*log10(abs(H2(1:NFFT/2+1))),w,20*log10(abs(mag)))
The DC level of the transfer function is correct, but the poor FFT technique yields too much noise at (relatively) higher frequencies. tfestimate would be a better choice to estimate the transfer based on the measurement data (again remember to pre-process the data the same way as I have just done here). It is part of the Signal Processing Toolbox.

Related

Integration/differentiation through the FFT

I'm trying to understand how to perform an integration or differentiation of an FFT using MATLAB. However, I think I'm doing something wrong somewhere and would like to know what I'm missing...
Here's an example of an FFT integration that, to the best of my knowledge, should work but doesn't.
clc; clear all; close all;
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sampling period
L = 1500; % Length of signal
t = (0:L-1)*T; % Time vector
f = Fs*(0:(L/2))/L;
omega = 2*pi.*f;
S is the time signal we are going to operate the FFT on, and dS is its derivative. We're going to apply an FFT to dS, and try to integrate that transform to get the same result as S.
S = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
dS = 70*pi*cos(2*pi*50*t) + 140*pi*cos(2*pi*120*t);
P2 = fft(S);
Y = P2(1:L/2+1);
c = fft(dS);
dm = c(1:L/2+1);
From what I found online, to integrate an FFT, you need to multiply each FFT value by the corresponding omega*1i. I'm assuming each point on the FFT result correspond to the values of my frequency vector f.
for z = 1:length(f)
dm(z) = dm(z)./(1i*omega(z));
end
figure
semilogy(f,abs(Y),'b'); hold on
semilogy(f,abs(dm),'r');
We can see on the plot that both curves don't match: the FFT of the initial time signal S is different from the integral of the FFT of the differentiated time signal dS.
The main difference between your two plots is in the noise. Because you use a logarithmic y axis, the noise gets blown up and looks important. Pay attention to the magnitudes when comparing. Anything about 1015 times smaller than the peak value should be ignored. This is the precision of the floating-point numbers used.
The relevant part of these frequency spectra is the two peaks. And the difference there between the sine and cosine is the phase. But you are plotting the magnitude, so the function and its derivative will look the same. Plot the phase also! (but only where the magnitude is above the noise level).

Matlab: computing signal to noise ratio (SNR) of two highly correlated time domain signals

I'm working in the space of biosignal acquisition. I made a experiment as detailed below, and am now trying to obtain some results from the data.
I have a text file of a signal in Matlab. I loaded the signal onto a waveform generator, then I recorded the generator output on an oscilloscope.
I imported the recorded signal from the oscilloscope back into Matlab.
The Pearson's correlation coefficient between the original signal and the oscilloscope signal is 0.9958 (obtained using corrcoeff function).
I want to compute the SNR of the oscilloscope signal (what I'm calling my signal plus whatever noise is introduced through the digital-to-analog conversion and visa-versa). I have attached a snippet of the 2 signals for reference.
So my original signal is X and oscilloscope signal is X + N.
I used the snr function to compute SNR as follows.
snr(original, (oscilloscope - original))
The result I got was 20.44 dB.
This seems off to me as I would have thought with such a high correlation, that the SNR should be much higher?
Or is it not appropriate to try and compute SNR in this sort of situation?
All help is appreciated.
Thanks
Edit: Graph of a couple of results vs Sleutheye's simulated relationship
You might be surprised at just how even such moderate SNR can still result in fairly high correlations.
I ran an experiment to illustrate the approximate relation between correlation and signal-to-noise-ratio estimate. Since I did not have your specific EEG signal, I just used a reference constant signal and some white Gaussian noise. Keep in mind that the relationship could be affected by the nature of the signal and noise, but it should give you an idea of what to expect. This simulation can be executed with the following code:
SNR = [10:1:40];
M = 10000;
C = zeros(size(SNR));
for i=1:length(SNR)
x = ones(1,M);
K = sqrt(sum(x.*x)/M)*power(10, -SNR(i)/20);
z = x + K*randn(size(x));
C(i) = xcorr(x,z,0)./sqrt(sum(x.*x)*sum(z.*z));
end
figure(1);
hold off; plot(SNR, C);
corr0 = 0.9958;
hold on; plot([SNR(1) SNR(end)], [corr0 corr0], 'k:');
snr0 = 20.44;
hold on; plot([snr0 snr0], [min(C) max(C)], 'r:');
xlabel('SNR (dB)');
ylabel('Correlation');
The dotted black horizontal line highlights your 0.9958 correlation measurement, and the dotted red vertical line highlights your 20.44 dB SNR result.
I'd say that's a pretty good match!
In fact, for this specific case in my simulation (x = 1; z = x + N(0,σ)) if we denote C(x,z) to be the correlation between x and z, and σ as the noise standard deviation, we can actually show that:
Given a correlation value of 0.9958, this would yield an SNR of 20.79dB, which is consistent with your results.

Plotting the magnitude and phase spectra of a wav file in the range of -fs/2 to fs/2

I'm having problems plotting the FFT of a wav file. I managed to plot the magnitude and phase spectrums of the signal, however I need to repeat this in range -fs/2:fs/2.
%read sound files
%'y' is the vector holding the original samples & 'fs' refers to the sampling frequency
[y,fs] = wavread('handel.wav');
ydft = fft(y); %fft to transform the original signal into frequency domain
n = length (y); %length of the original signal
% y has even length
ydft = ydft(1:length(y)/2+1);
% create a frequency vector
freq = 0:fs/length(y):fs/2;
shiftfreq = fftshift(freq);
%plot original signal in time domain;
figure;
plot ((1:n)/fs, y);
title('handel.wav in time domain');
xlabel ('second');
grid on;
% plot magnitude in frequency domain
figure;
plot(freq,abs(ydft));
title('handel.wav in frequency domain');
xlabel ('Hz');
ylabel('Magnitude');
grid on;
% plot phase in frequency domain
figure;
plot(freq,unwrap(angle(ydft)));
title ('handel.wav in frequency domain');
xlabel ('Hz');
ylabel ('Phase');
grid on;
What you are currently doing now is plotting the half spectrum, so from 0 <= f < fs/2 where fs is the sampling frequency of your signal, and so fs/2 is the Nyquist frequency. Take note that considering the half spectrum is only valid if the signal is real. This means that the negative spectra is symmetric to the positive spectra and so you don't really need to consider the negative spectra here.
However, you would like to plot the full spectrum of the magnitude and phase. Take note that when calculating the fft using MATLAB, it uses the Cooley-Tukey algorithm so when computing the N point FFT, half of result is for the frequencies from 0 Hz inclusive up to fs/2 Hz exclusive and the other half is for the frequencies from -fs/2 Hz inclusive up to 0 Hz exclusive.
As such, to plot the full spectrum, simply perform a fftshift on the full signal so that the right half and left half of the spectrum is swapped so that the 0 Hz frequency is located in the centre of the signal. Also, you must generate frequencies between -fs/2 to fs/2 to cover the full spectrum. Specifically, you need to generate N points linearly spaced between -fs/2 to fs/2. However, take note that the Nyquist frequency at fs/2 Hz is being excluded at the end, so you need to generate N+1 points between -fs/2 to fs/2 and remove the last point in order for the right step size between each frequency bin to be correct. The easiest way to generate this linear array of points is by using the linspace command where the start frequency is -fs/2, the ending frequency is fs/2 and you want N+1 points between this range and remove the last point:
freq = linspace(-fs/2, fs/2, n+1);
freq(end) = [];
As such, borrowing some parts of your code, this is what the modified code looks like to plot the full spectrum of the magnitude and phase:
%// Read in sound file
[y,fs] = wavread('handel.wav');
%// Take N-point FFT where N is the length of the signal
ydft = fft(y);
n = numel(y); %// Get N - length of signal
%// Create frequency vector - make sure you remove last point
freq = linspace(-fs/2, fs/2, n+1);
freq(end) = [];
%// Shift the spectrum
shiftSpectrum = fftshift(ydft);
%//plot original signal in time domain;
figure;
plot ((0:n-1)/fs, y); %// Note you should start from time = 0, not time = 1/fs
title('handel.wav in time domain');
xlabel ('second');
grid on;
%// plot magnitude in frequency domain
figure;
plot(freq,abs(shiftSpectrum));
title('handel.wav in frequency domain');
xlabel ('Hz');
ylabel('Magnitude');
grid on;
%// plot phase in frequency domain
figure;
plot(freq,unwrap(angle(shiftSpectrum)));
title('handel.wav in frequency domain');
xlabel('Hz');
ylabel('Phase');
grid on;
I don't have access to your handel.wav file, but I'll be using the one provided with MATLAB. You can load this in with load handel;. The sampling frequency is stored in a variable called Fs, so I had to do fs = Fs; before the code I wrote above could work. The sampling frequency for this particular file is 8192 Hz, and this is approximately a 9 second long file (numel(y) / fs = 8.9249 seconds). With that file, this is the magnitude and phase that I get:
For the discrete Fourier transform (DFT) as well as its fast implementations (FFTs), the frequencies are normalized with the sampling frequency fs, i.e., the original range -fs/2:fs/2 is changed to -pi:pi.
Besides, the DFT/FFT always starts with 0, and you can use fftshift() to shift the 0 frequency to the center. Therefore, after fftshift(), the range is -pi:pi, then, you can scale to -fs/2:fs/2.
look at the following Matlab function, it can calculate phase spectrum as well as amplitude spectrum with a perfect accuracy:
https://www.mathworks.com/matlabcentral/fileexchange/63965-amplitude-and-phase-spectra-of-a-signal--fourier-transform-
This program calculates amplitude and phase spectra of an input signal with acceptable accuracy especially in the calculation of phase spectrum.The code does three main jobs for calculation amplitude and phase spectra. First of all, it extends the input signal to infinity; because for calculation Fourier transform(FT) (fft function in Matlab), we consider our signal is periodic with an infinite wavelength, the code creates a super_signal by putting original signal next to itself until the length of super_signal is around 1000000 samples, why did I choose 1000000 samples? Actually, it is just based on try and error!! For most signals that I have tried, a supper signal with 1000000 samples has the best output.
Second, for calculating fft in Matlab you can choose different resolutions, the Mathwork document and help use NFFT=2^nextpow2(length(signal)), it definitely isn't enough for one that wants high accuracy output. Here, I choose the resolution of NFFT=100000 that works for most signals.
Third, the code filters result of FT by thresholding, it is very important step! For calculating phase spectrum, its result is very noisy because of floating rounding off error, it causes during calculation "arctan" even small rounding off error produces significant noise in the result of phase spectrum, for suppressing this kind of noise you can define a threshold value. It means if amplitude of specific frequency is less than predefined threshold value (you must define it) it put zero instead of it.
These three steps help to improve the result of amplitude and phase spectra significantly.
IF YOU USE THIS PROGRAM IN YOUR RESEARCH, PLEASE CITE THE FOLLOWING PAPER:
Afshin Aghayan, Priyank Jaiswal, and Hamid Reza Siahkoohi (2016). "Seismic denoising using the redundant lifting scheme." GEOPHYSICS, 81(3), V249-V260. https://doi.org/10.1190/geo2015-0601.1

Mean amplitude value in EMG signal

I have an EMG-signal and would like to get the mean value of the amplitude in a defined frequency space. I tried making FFT, but i don't get a vector with amplitudes and frequencies. I am new to Matlab - please help.
Here is how you would get power (i.e., amplitude) and frequency from the fft function in Matlab:
fs = 1024; % sampling rate of signal
time = 1; % time in sec of simulated signal
xn = randn (fs*time,1); % creating random signal
nfft = time*fs; % size of window on which to perform FFT
Y = fft (xn, nfft);
% Convert value to obtain the power of the signal at
% each frequency
Pyy = Y .* conj(Y) / nfft;
% Create a frequency axis for ploting
fy = fs/nfft * (0:(nfft/2) -1);
plot (fy, Pyy(1:nfft/2))
ylabel ('power (AU)^2') % arbitrary units
xlabel ('frequency (Hz)')
xlim ([0 512])
However, keep in mind that EMG is not a stationary signal. When doing spectral analysis on EMG signals we are trying to approximate the true spectrum. This is why EMG is usually seperated into windows of data. FFT is performed on each of these windows and the averaged.
Rather than using the fft function, you might want to consider using the pwelch function in the Matlab Signal Processing Toolbox. It allows you to set the window size, amount of overlap of the windows, etc.
Alternatively you could use the very popular Neurospec Matlab toolbox writen by David Halliday. It has a PDF that describes all the functions and examples you can use. It has additional functionality (e.g., coherence), but you can ignore it if you like and simply extract the spectral data.

Frequency response using FFT in MATLAB

Here is the scenario: using a spectrum analyzer i have the input values and the output values. the number of samples is 32000 and the sampling rate is 2000 samples/sec, and the input is a sine wave of 50 hz, the input is current and the output is pressure in psi.
How do i calculate the frequency response from this data using MATLAB,
using the FFT function in MATLAB.
i was able to generate a sine wave, that gives out the the magnitude and phase angles, here is the code that i used:
%FFT Analysis to calculate the frequency response for the raw data
%The FFT allows you to efficiently estimate component frequencies in data from a discrete set of values sampled at a fixed rate
% Sampling frequency(Hz)
Fs = 2000;
% Time vector of 16 second
t = 0:1/Fs:16-1;
% Create a sine wave of 50 Hz.
x = sin(2*pi*t*50);
% Use next highest power of 2 greater than or equal to length(x) to calculate FFT.
nfft = pow2(nextpow2(length(x)))
% Take fft, padding with zeros so that length(fftx) is equal to nfft
fftx = fft(x,nfft);
% Calculate the number of unique points
NumUniquePts = ceil((nfft+1)/2);
% FFT is symmetric, throw away second half
fftx = fftx(1:NumUniquePts);
% Take the magnitude of fft of x and scale the fft so that it is not a function of the length of x
mx = abs(fftx)/length(x);
% Take the square of the magnitude of fft of x.
mx = mx.^2;
% Since we dropped half the FFT, we multiply mx by 2 to keep the same energy.
% The DC component and Nyquist component, if it exists, are unique and should not be multiplied by 2.
if rem(nfft, 2) % odd nfft excludes Nyquist point
mx(2:end) = mx(2:end)*2;
else
mx(2:end -1) = mx(2:end -1)*2;
end
% This is an evenly spaced frequency vector with NumUniquePts points.
f = (0:NumUniquePts-1)*Fs/nfft;
% Generate the plot, title and labels.
subplot(211),plot(f,mx);
title('Power Spectrum of a 50Hz Sine Wave');
xlabel('Frequency (Hz)');
ylabel('Power');
% returns the phase angles, in radians, for each element of complex array fftx
phase = unwrap(angle(fftx));
PHA = phase*180/pi;
subplot(212),plot(f,PHA),title('frequency response');
xlabel('Frequency (Hz)')
ylabel('Phase (Degrees)')
grid on
i took the frequency response from the phase plot at 90 degree phase angle, is this the right way to calculate the frequency response?
how do i compare this response to the values that is obtained from the analyzer? this is a cross check to see if the analyzer logic makes sense or not.
Looks OK at first glance, but a couple of things you're missing:
you should apply a window function to the time domain data before the FFT, see e.g. http://en.wikipedia.org/wiki/Window_function for windowing in general and http://en.wikipedia.org/wiki/Hann_window for the most commonly used window function (Hann aka Hanning).
you probably want to plot log magnitude in dB rather than just raw magnitude
You should consider looking at the cpsd() function for calculating the Frequency response. The scaling and normalisation for various window functions is handled for you.
the Frequency reponse would then be
G = cpsd (output,input) / cpsd (input,input)
then take the angle() to obtain the phase difference between the input and the output.
Your code snippet does not mention what the input and output data sets are.