Matlab, FFT frequency range differences or are they the same? - matlab

I'm trying to understand how the FFT in matlab works, particularly, how to define the frequency range to plot it. It happens that I have read from matlab help links and from other discussions here and I think (guess) that I'm confused about it.
In the matlab link:
http://es.mathworks.com/help/matlab/math/fast-fourier-transform-fft.html
they define such a frequency range as:
f = (0:n-1)*(fs/n)
with n and fs as:
n = 2^nextpow2(L); % Next power of 2 from length of signal x
fs = N/T; % N number of samples in x and T the total time of the recorded signal
But, on the other hand, in the previous post Understanding Matlab FFT example
(based on previous version of matlab), the resulting frequency range is defined as:
f = fs/2*linspace(0,1,NFFT/2+1);
with NFFT as the aforementioned n (Next power of 2 from length of signal x).
So, based on that, how these different vectors (equation 1 and final equation) could be the same?
If you can see, the vectors are different since the former has n points and the later has NFFT/2 points! In fact, the factor (fs/n) is different from fs/2.

So, based on that, how these different vectors (equation 1 and final equation) could be the same?
The example in the documentation from Mathworks plots the entire n-point output of the FFT. This covers the frequencies from 0 to nearly fs (exactly (n-1)/n * fs). They then make the following observation (valid for real inputs to the FFT):
The first half of the frequency range (from 0 to the Nyquist frequency fs/2) is sufficient to identify the component frequencies in the data, since the second half is just a reflection of the first half.
The other post you refer to just chooses to not show that redundant second half. It then uses half the number of points which also cover half the frequency range.
In fact, the factor (fs/n) is different from fs/2.
Perhaps the easiest way to make sense of it is to compare the output of the two expressions for some small value of n, says n=8 and setting fs=1 (since fs multiplies both expressions). On the one hand the output of the first expression [0:n-1]*(fs/n) would be:
0.000 0.125 0.250 0.500 0.625 0.750 0.875
whereas the output of fs/2*linspace(0,1,n/2+1) would be:
0.000 0.125 0.250 0.500
As you can see the set of frequencies are exactly the same up to Nyquist frequency fs/2.

The confusion is perhaps arising from the fact that the two examples which you have referenced are plotting results of the fft differently. Please refer to the code below for the references made in this explanation.
In the first example, the plot is of the power spectrum (periodogram) over the frequency range. Note, in the first plot, that the periodogram is not centered at 0, meaning that the frequency range appears to be twice that of the Nyquist sampling frequency. As mentioned in the mathworks link, it is common practice to center the periodogram at 0 to avoid this confusion (figure 2).
For the second example, taking the same parameters, the original plot is of amplitude of the fourier spectrum with a different normalization than in the first example (figure 3). Using the syntax of Matlab's full frequency ordering (as commented in the code), it is trivial to convert this seemingly different fft result to that of example 1; the identical result of the 0-centered periodogram is replicated in figure 4.
Thus, to answer your question specifically, the frequency ranges in both cases are the same, with the maximum frequency equal to the Nyquist sampling frequency as in:
f = fs/2*linspace(0,1,NFFT/2+1);
The key to understanding how the dfft works (also in Matlab) is to understand that you are simply performing a projection of your discrete data set into fourier space where what is returned by the fft() function in matlab are the coefficients of the expansion for each frequency component and the order of the coefficients is given (in Matlab as in example 2) by:
f = [f(1:end-1) -fliplr(f(1,2:end))];
See the Wikipedia page on the DFT for additional details:
https://en.wikipedia.org/wiki/Discrete_Fourier_transform
It might also be helpful for you to take the fft omitting the length as a power of 2 parameter as
y = fft(x).
In this case, you would see only a few non-zero components in y corresponding to the exact coefficients of your input signal. The mathworks page claims the following as a motivation for using or not using this length:
"Using a power of two for the transform length optimizes the FFT algorithm, though in practice there is usually little difference in execution time from using n = m."
%% First example:
% http://www.mathworks.com/help/matlab/math/fast-fourier-transform-fft.html
fs = 10; % Sample frequency (Hz)
t = 0:1/fs:10-1/fs; % 10 sec sample
x = (1.3)*sin(2*pi*15*t) ... % 15 Hz component
+ (1.7)*sin(2*pi*40*(t-2)); % 40 Hz component
% Removed the noise
m = length(x); % Window length
n = pow2(nextpow2(m)); % Transform length
y = fft(x,n); % DFT
f = (0:n-1)*(fs/n); % Frequency range
power = y.*conj(y)/n; % Power of the DFT
subplot(2,2,1)
plot(f,power,'-o')
xlabel('Frequency (Hz)')
ylabel('Power')
title('{\bf Periodogram}')
y0 = fftshift(y); % Rearrange y values
f0 = (-n/2:n/2-1)*(fs/n); % 0-centered frequency range
power0 = y0.*conj(y0)/n; % 0-centered power
subplot(2,2,2)
plot(f0,power0,'-o')
% plot(f0,sqrt_power0,'-o')
xlabel('Frequency (Hz)')
ylabel('Power')
title('{\bf 0-Centered Periodogram} Ex. 1')
%% Second example:
% http://stackoverflow.com/questions/10758315/understanding-matlab-fft-example
% Let's redefine the parameters for consistency between the two examples
Fs = fs; % Sampling frequency
% T = 1/Fs; % Sample time (not required)
L = m; % Length of signal
% t = (0:L-1)*T; % Time vector (as above)
% % Sum of a 3 Hz sinusoid and a 2 Hz sinusoid
% x = 0.7*sin(2*pi*3*t) + sin(2*pi*2*t); %(as above)
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
% NFFT == n (from above)
Y = fft(x,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
subplot(2,2,3)
plot(f,2*abs(Y(1:NFFT/2+1)),'-o')
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')
% Get the 0-Centered Periodogram using the parameters of the second example
f = [f(1:end-1) -fliplr(f(1,2:end))]; % This is the frequency ordering used
% by the full fft in Matlab
power = (Y*L).*conj(Y*L)/NFFT;
% Rearrange for nicer plot
ToPlot = [f; power]; [~,ind] = sort(f);
ToPlot = ToPlot(:,ind);
subplot(2,2,4)
plot(ToPlot(1,:),ToPlot(2,:),'-o')
xlabel('Frequency (Hz)')
ylabel('Power')
title('{\bf 0-Centered Periodogram} Ex. 2')

Related

Matlab FFT - Scaling y-axis

As a data basis, I have measured data the volts were recorded.
Matlab will now be used to perform an FFT.
I have the following questions:
- What unit do I have on the ordinate axis after the FFT? Also volts?
- How is scaled correctly? By hiding the negative frequencies (Nyquist), I would actually have to double the amplitude, right?
- Do I have to multiply all values of the FFT again with 20 * log10 (FFT) to represent the ordinate in db?
Thank you so much for your support!
Frank
Matlab example:
load('TimeDomain.mat')%loading of the time domain signal
L=2500; %length of the signal
Fs=500000;%sampling frequency
N=2^nextpow2(L);%scale factor
t=(0:L-1)*10^-3;%time domain array
f=linspace(0,Fs/2,length(t));%frequency domain array
FFT=abs(fft(Timedomain,N));
figure(1)
plot(f,FFT(1:2500))
Yes, after the FFT, the unit of the ordinate axis will still be volts.
You can scale it by dividing by the number of samples of your signal, then you can indeed multiplying by two (except the first and last elements that represents respectively the frequency 0 and Fs/2) if you want to plot all the spectrum in the positive side.
Then if you want to plot in dB, you can use the function mag2db which applies the formula that you said.
I found some weirdnesses in your code, so I suggest some fixes. My Timedomain signal is a 100 kHz sine at 1 V.
Fs = 500000; % sampling frequency
L = 2500; % length of the signal
t = (0:L-1)/Fs; % time domain array
f = linspace(0, Fs/2, L/2+1); % frequency domain array
Timedomain = cos(2*pi*100000*t); % Input signal
FFT = abs(fft(Timedomain)/L);
FFT(2:L/2) = 2*FFT(2:L/2);
%% Plots
subplot(2,1,1);
plot(f, FFT(1:L/2+1)); xlabel('Frequency (Hz)'); ylabel('Tension (V)');
subplot(2,1,2);
plot(f, mag2db(FFT(1:L/2+1))); xlabel('Frequency (Hz)'); ylabel('Tension (dBV)');
This returns:

How do I create band-limited (100-640 Hz) white Gaussian noise?

I would like to create 500 ms of band-limited (100-640 Hz) white Gaussian noise with a (relatively) flat frequency spectrum. The noise should be normally distributed with mean = ~0 and 99.7% of values between ± 2 (i.e. standard deviation = 2/3). My sample rate is 1280 Hz; thus, a new amplitude is generated for each frame.
duration = 500e-3;
rate = 1280;
amplitude = 2;
npoints = duration * rate;
noise = (amplitude/3)* randn( 1, npoints );
% Gaus distributed white noise; mean = ~0; 99.7% of amplitudes between ± 2.
time = (0:npoints-1) / rate
Could somebody please show me how to filter the signal for the desired result (i.e. 100-640 Hz)? In addition, I was hoping somebody could also show me how to generate a graph to illustrate that the frequency spectrum is indeed flat.
I intend on importing the waveform to Signal (CED) to output as a form of transcranial electrical stimulation.
The following is Matlab implementation of the method alluded to by "Some Guy" in a comment to your question.
% In frequency domain, white noise has constant amplitude but uniformly
% distributed random phase. We generate this here. Only half of the
% samples are generated here, the rest are computed later using the complex
% conjugate symmetry property of the FFT (of real signals).
X = [1; exp(i*2*pi*rand(npoints/2-1,1)); 1]; % X(1) and X(NFFT/2) must be real
% Identify the locations of frequency bins. These will be used to zero out
% the elements of X that are not in the desired band
freqbins = (0:npoints/2)'/npoints*rate;
% Zero out the frequency components outside the desired band
X(find((freqbins < 100) | (freqbins > 640))) = 0;
% Use the complex conjugate symmetry property of the FFT (for real signals) to
% generate the other half of the frequency-domain signal
X = [X; conj(flipud(X(2:end-1)))];
% IFFT to convert to time-domain
noise = real(ifft(X));
% Normalize such that 99.7% of the times signal lies between ±2
noise = 2*noise/prctile(noise, 99.7);
Statistical analysis of around a million samples generated using this method results in the following spectrum and distribution:
Firstly, the spectrum (using Welch method) is, as expected, flat in the band of interest:
Also, the distribution, estimated using histogram of the signal, matches the Gaussian PDF quite well.

How to find the frequency of a periodic sound signal?

I'm working on sound signals of a walking pattern, which has obvious regular patterns:
Then I thought I can get the frequency of walking (approximately 1.7Hz from the image) using FFT function:
x = walk_5; % Walking sound with a size of 711680x2 double
Fs = 48000; % sound frquency
L=length(x);
t=(1:L)/Fs; %time base
plot(t,x);
figure;
NFFT=2^nextpow2(L);
X=fft(x,NFFT);
Px=X.*conj(X)/(NFFT*L); %Power of each freq components
fVals=Fs*(0:NFFT/2-1)/NFFT;
plot(fVals,Px(1:NFFT/2),'b','LineSmoothing','on','LineWidth',1);
title('One Sided Power Spectral Density');
xlabel('Frequency (Hz)')
ylabel('PSD');
But then it doesn't give me what I expected:
FFT result:
zoom image has lots of noises:
and there is no information near 1.7Hz
Here is the graph from log domain using
semilogy(fVals,Px(1:NFFT));
It's pretty symmetric though:
I couldn't find anything wrong with my code. Do you have any solutions to easily extract the 1.7Hz from the walking pattern?
here is the link for the audio file in mat
https://www.dropbox.com/s/craof8qkz9n5dr1/walk_sound.mat?dl=0
Thank you very much!
Kai
I suggest you to forget about DFT approach since your signal is not appropriate for this type of analysis due to many reasons. Even by looking on the spectrum in range of frequencies that you are interested in, there is no easy way to estimate the peak:
Of course you could try with PSD/STFT and other funky methods, but this is an overkill. I can think of two, rather simple methods, for this task.
First one is based simply on the Auto Correlation Function.
Calculate the ACF
Define the minimum distance between them. Since you know that expected frequency is around 1.7Hz, then it corresponds to 0.58s. Let's make it 0.5s as the minimum distance.
Calculate the average distance between peaks found.
This gave me an approximate frequency of 1.72 Hz .
Second approach is based on the observation to your signal already has some peaks which are periodic. Therefore we can simply search for them using findpeaks function.
Define the minimum peak distance in a same way as before.
Define the minimum peak height. For example 10% of maximum peak.
Get the average difference.
This gave me an average frequency of 1.7 Hz.
Easy and fast method. There are obviously some things that can be improved, such as:
Refining thresholds
Finding both positive and negative peaks
Taking care of some missing peaks, i.e. due to low amplitude
Anyway that should get you started, instead of being stuck with crappy FFT and lazy semilogx.
Code snippet:
load walk_sound
fs = 48000;
dt = 1/fs;
x = walk_5(:,1);
x = x - mean(x);
N = length(x);
t = 0:dt:(N-1)*dt;
% FFT based
win = hamming(N);
X = abs(fft(x.*win));
X = 2*X(1:N/2+1)/sum(win);
X = 20*log10(X/max(abs(X)));
f = 0:fs/N:fs/2;
subplot(2,1,1)
plot(t, x)
grid on
xlabel('t [s]')
ylabel('A')
title('Time domain signal')
subplot(2,1,2)
plot(f, X)
grid on
xlabel('f [Hz]')
ylabel('A [dB]')
title('Signal Spectrum')
% Autocorrelation
[ac, lag] = xcorr(x);
min_dist = ceil(0.5*fs);
[pks, loc] = findpeaks(ac, 'MinPeakDistance', min_dist);
% Average distance/frequency
avg_dt = mean(gradient(loc))*dt;
avg_f = 1/avg_dt;
figure
plot(lag*dt, ac);
hold on
grid on
plot(lag(loc)*dt, pks, 'xr')
title(sprintf('ACF - Average frequency: %.2f Hz', avg_f))
% Simple peak finding in time domain
[pkst, loct] = findpeaks(x, 'MinPeakDistance', min_dist, ...
'MinPeakHeight', 0.1*max(x));
avg_dt2 = mean(gradient(loct))*dt;
avg_f2 = 1/avg_dt2;
figure
plot(t, x)
grid on
hold on
plot(loct*dt, pkst, 'xr')
xlabel('t [s]')
ylabel('A')
title(sprintf('Peak search in time domain - Average frequency: %.2f Hz', avg_f2))
Here's a nifty solution:
Take the absolute value of your raw data before taking the FFT. The data has a ton of high frequency noise that is drowning out whatever low frequency periodicity is present in the signal. The amplitude of the high frequency noise gets bigger every 1.7 seconds, and the increase in amplitude is visible to the eye, and periodic, but when you multiply the signal by a low frequency sine wave and sum everything you still end up with something close to zero. Taking the absolute value changes this, making those amplitude modulations periodic at low frequencies.
Try the following code comparing the FFT of the regular data with the FFT of abs(data). Note that I took a few liberties with your code, such as combining what I assume were the two stereo channels into a single mono channel.
x = (walk_5(:,1)+walk_5(:,2))/2; % Convert from sterio to mono
Fs = 48000; % sampling frquency
L=length(x); % length of sample
fVals=(0:L-1)*(Fs/L); % frequency range for FFT
walk5abs=abs(x); % Take the absolute value of the raw data
Xold=abs(fft(x)); % FFT of the data (abs in Matlab takes complex magnitude)
Xnew=abs(fft(walk5abs-mean(walk5abs))); % FFT of the absolute value of the data, with average value subtracted
figure;
plot(fVals,Xold/max(Xold),'r',fVals,Xnew/max(Xnew),'b')
axis([0 10 0 1])
legend('old method','new method')
[~,maxInd]=max(Xnew); % Index of maximum value of FFT
walkingFrequency=fVals(maxInd) % print max value
And plotting the FFT for both the old method and the new, from 0 to 10 Hz gives:
As you can see it detects a peak at about 1.686 Hz, and for this data, that's the highest peak in the FFT spectrum.

Finding peak frequency in a complex signal using Matlab

I'm trying to find the peak frequency for two signals 'CA1' and 'PFC', within a specified range (25-140Hz).
In Matlab, so far I have plotted an FFT for each of these signals (see pictures below). These FFTs suggest that the peak frequency between 25-140Hz is different for each signal, but I would like to quantify this (e.g. CA1 peaks at 80Hz, whereas PFC peaks at 55Hz). However, I think the FFT is not smooth enough, so when I try and extract the peak frequencies it doesn't make sense as my code pulls out loads of values. I was only expecting a few values - one each time the FFT peaks (around 2Hz, 5Hz and ~60Hz).
I want to know, between 25-140Hz, what is the peak frequency in 'CA1' compared with 'PFC'. 'CA1' and 'PFC' are both 152401 x 7 matrices of EEG data, recorded
from 7 separate individuals. I want the MEAN peak frequency for each data set (i.e. averaged across the 7 test subjects for CA1 and PFC).
My code so far (based on Matlab help files and code I've scrabbled together online):
Fs = 508;
%notch filter
[b50,a50] = iirnotch(50/(Fs/2), (50/(Fs/2))/70);
CA1 = filtfilt(b50,a50,CA1);
PFC = filtfilt(b50,a50,PFC);
%FFT
L = length(CA1);
NFFT = 2^nextpow2(L);
%FFT for each of the 7 subjects
for i = 1:size(CA1,2);
CA1_FFT(:,i) = fft(CA1(:,i),NFFT)/L;
PFC_FFT(:,i) = fft(PFC(:,i),NFFT)/L;
end
%Average FFT across all 7 subjects - CA1
Mean_CA1_FFT = mean(CA1_FFT,2);
% Mean_CA1_FFT_abs = 2*abs(Mean_CA1_FFT(1:NFFT/2+1));
%Average FFT across all 7 subjects - PFC
Mean_PFC_FFT = mean(PFC_FFT,2);
% Mean_PFC_FFT_abs = 2*abs(Mean_PFC_FFT(1:NFFT/2+1));
f = Fs/2*linspace(0,1,NFFT/2+1);
%LEFT HAND SIDE FIGURE
plot(f,2*abs(Mean_CA1_FFT(1:NFFT/2+1)),'r');
set(gca,'ylim', [0 2]);
set(gca,'xlim', [0 200]);
[C,cInd] = sort(2*abs(Mean_CA1_FFT(1:NFFT/2+1)));
CFloor = 0.1; %CFloor is the minimum amplitude value (ignore small values)
Amplitudes_CA1 = C(C>=CFloor); %find all amplitudes above the CFloor
Frequencies_CA1 = f(cInd(1+end-numel(Amplitudes_CA1):end)); %frequency of the peaks
%RIGHT HAND SIDE FIGURE
figure;plot(f,2*abs(Mean_PFC_FFT(1:NFFT/2+1)),'r');
set(gca,'ylim', [0 2]);
set(gca,'xlim', [0 200]);
[P,pInd] = sort(2*abs(Mean_PFC_FFT(1:NFFT/2+1)));
PFloor = 0.1; %PFloor is the minimum amplitude value (ignore small values)
Amplitudes_PFC = P(P>=PFloor); %find all amplitudes above the PFloor
Frequencies_PFC = f(pInd(1+end-numel(Amplitudes_PFC):end)); %frequency of the peaks
Please help!! How do I calculate the 'major' peak frequencies from an FFT, and ignore all the 'minor' peaks (because the FFT is not smoothed).
FFTs assume that the signal has no trend (this is called a stationary signal), if it does then this will give a dominant frequency component at 0Hz as you have here. Try using the MATLAB function detrend, you may find this solves your problem.
Something along the lines of:
x = x - mean(x)
y = detrend(x, 'constant')

Ways to Compute Spectrum Matlab

I have a question while computing the spectrum of a time series in Matlab. I have read the documentations concerning 'fft' function. However I have seen two ways of implementation and both wgive me different results. I would appreciate to have some answer about this difference:
1st Method:
nPoints=length(timeSeries);
Time specifications:
Fs = 1; % samples per second
Fs = 50;
freq = 0:nPoints-1; %Numerators of frequency series
freq = freq.*Fs./nPoints;
% Fourier Transform:
X = fft(timeSeries)/nPoints; % normalize the data
% find find nuquist frequency
cutOff = ceil(nPoints./2);
% take only the first half of the spectrum
X = abs(X(1:cutOff));
% Frequency specifications:
freq = freq(1:cutOff);
%Plot spectrum
semilogy(handles.plotLoadSeries,freq,X);
2nd Method:
NFFT = 2^nextpow2(nPoints); % Next power of 2 from length of y
Y = fft(timeSeries,NFFT)/nPoints;
f = 1/2*linspace(0,1,NFFT/2+1);
% % Plot single-sided amplitude spectrum.
% plot(handles.plotLoadSeries, f,2*abs(Y(1:NFFT/2+1)))
semilogy(handles.plotLoadSeries,f,2*abs(Y(1:NFFT/2+1)));
I thought that it is not necessary to use 'nextpow' function in 'fft' function in Matlab. Finally, which is the good one?
THanks
The short answer: you need windowing for spectrum analysis.
Now for the long answer... In the second approach, you are using an optimised FFT algorithm useful when the length of the input vector is a power of two. Let's assume that your original signal has 401 samples (as in my example below) from an infinitely long signal; nextpow2() will give you NFFT=512 samples. When you feed the shorter, 401-sample signal into the fft() function, it is implicitly zero-padded to match the requested length of 512 (NFFT). But (here comes the tricky part): zero-padding your signal is equivalent to multiplying an infinitely long signal by a rectangular function, an operation that in the frequency domain translates to a convolution with a sinc function. This would be the reason behind the increased noise floor at the bottom of your semilogarithmic plot.
A way to avoid this noise increase is to create manually the 512-sample signal you want to feed into fft(), using a smoother window function instead of the default rectangular one. Windowing means just multiplying your signal by a tapered, symmetric one. There are tons of literature on choosing a good windowing function, but a typically accurate one with low sidelobes (low noise increase) is the Hamming function, implemented in MATLAB as hamming().
Here is a figure illustrating the issue (in the frequency domain and time domain):
...and the code to generate this figure:
clear
% Create signal
fs = 40; % sampling freq.
Ts = 1/fs; % sampling period
t = 0:Ts:10; % time vector
s = sin(2*pi*3*t); % original signal
N = length(s);
% FFT (length not power of 2)
S = abs(fft(s)/N);
freq = fs*(0:N-1)/N;
% FFT (length power of 2)
N2 = 2^nextpow2(N);
S2 = abs(fft(s, N2)/N2);
freq2 = fs*(0:N2-1)/N2;
t2 = (0:N2-1)*Ts; % longer time vector
s2 = [s,zeros(1,N2-N)]; % signal that was implicitly created for this FFT
% FFT (windowing before FFT)
s3 = [s.*hamming(N).',zeros(1,N2-N)];
S3 = abs(fft(s3, N2)/N2);
% Frequency-domain plot
figure(1)
subplot(211)
cla
semilogy(freq,S);
hold on
semilogy(freq2,S2,'r');
semilogy(freq2,S3,'g');
xlabel('Frequency [Hz]')
ylabel('FFT')
grid on
legend( 'FFT[401]', 'FFT[512]', 'FFT[512] with windowing' )
% Time-domain plot
subplot(212)
cla
plot(s)
hold on
plot(s3,'g')
xlabel('Index')
ylabel('Amplitude')
grid on
legend( 'Original samples', 'Windowed samples' )