How can i obtain different frequencies from a waveform using FFT?

I have a csv file which i used to plot data. I'm trying to use FFT in matlab to obtain the different frequencies this signal is made up of. My fft doesn't look correct. How can i fix this? First i try to use Savitzky Golay filter to smoothen the signal then take fft. below is my code and CSV has been attached.
clear all; close all; clc;
data = csvread('TEK00001.csv'); x=data(:,1); y=data(:,2);
Fs=100; polymonial_order=0; framelength=201; % plot(x,y) % hold on %%%%%%%%%%%%%%%%%% Savitzky Golay filter %%%%%%%%%%%%%%%%%%%%% new_data=sgolayfilt(data, polymonial_order,framelength); x_col1=new_data(:,1); y_col2=new_data(:,2);
new_data(:,2)=new_data(:,2)-min(new_data(:,2));
subplot(4,1,1) plot(x,y,'r') title('Original CSV waveform') grid on grid minor xlabel('Time [s]'); ylabel('Amplitude [mV] ');
subplot(4,1,2) plot(x_col1,y_col2,'b') title('Filtered waveform using Savitzky Golay Filter') grid on grid minor xlabel('Time [s]'); ylabel('Amplitude [mV]'); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% nfft=length(data); nfft2=2^nextpow2(nfft); ff=fft(new_data,nfft2); %plot(abs(ff)); %plots the magnitude of signal xfft=Fs*(0:nfft2/2-1)/nfft2;
subplot(4,1,3) fff=ff(1:nfft2/2); plot(xfft,abs(fff))%plots the one symetrical side of fft signal title('FFT Magnitude') grid minor xlabel('Frequency [Hz]'); ylabel('Amplitude [mV]');

 Accepted Answer

It doesn’t look correct to me either.
I would not use the Savitzky-Golay filter here, at least not with you original data. (I’m not certain what you want to do with it, anyway.)
If you want to get the frequency content of your signal, I would do something like this:
data = csvread('TEK00001.csv');
x=data(:,1);
y=data(:,2);
L = numel(x); % Signal Length (Samples)
Fs = mean(diff(x)); % Sampling Frequency (Hz)
Fn = Fs/2; % Nyquist Frequency (Hz)
nfft2=2^nextpow2(L); % Desired Fourier Transform Length
ymc = y-mean(y); % Subtract Mean (Eliminates d-c- Offset)
FTy = fft(ymc, nfft2)/L; % Fourier Transform (Scaled By Length Of Data Vector)
Fv = linspace(0, 1, fix(L/2)+1)*Fn; % Frequency Vector (One-Sided Fourier Transform)
Iv = 1:numel(Fv); % Index Vector
[pks,locidx] = findpeaks(abs(FTy(Iv))*2, 'MinPeakDistance',20, 'MinPeakHeight',2E-3); % Peak Amplitudes & Indices
figure(2)
plot(Fv, abs(FTy(Iv))*2)
grid
xlim([0 1.5E-6])
title('FFT Magnitude')
grid minor
xlabel('Frequency [Hz]');
ylabel('Amplitude [mV]');
text(Fv(locidx), pks, sprintfc('%11.2E Hz\n%11.2E mV', [Fv(locidx)' pks]))
There are three peaks, at 1.1E-7 Hz, 3.2E-7 Hz, and 5.3E-7 Hz. (I see nothing of significance in the higher frequencies.) I used the findpeaks (link) function to isolate the most significant peak amplitudes and their frequencies, and text (link) function to label them. (The sprintfc function is undocumented. It produces a cell array of strings, very efficient for this sort of application. It otherwise works the same as sprintf.)
If you want to filter your signal, there are several options, the easiest being to use the bandpass (link) and related functions, introduced in R2018a. An appropriate high-frequency cutoff would be 1E-6 Hz.

6 Comments

the results look great. Thanks. About the Savitzky-Golay filter , i wanted to gain general waveform without noise.But it was not necessary.
For learning purposes, could you maybe show me how the INVERSE FFT would be coded for this same very file?
As always, my pleasure.
You can eliminate much of the noise with a lowpass or bandpass filter. Your signal (with 3 principal frequencies) only exists below about 1E-6 Hz, so everything above that is noise. The bandpass or lowpass functions I liked to in my original Answer design computationally-efficient elliptic filters. You can also emulate it by using the ellipord, ellip, and returning the [z,p,k] outputs from it, the zp2sos function to design and implement the filter, and the filtfilt function to do the actual filtering. This is relatively straightforward, although it will likely require a bit of experimentation to get the result you want.
The inverse Fourier transform of the full fft of your signal (that being FTy, not the one-sided Fourier transform I plotted) would return the original time-domain signal (perhaps with some negligible imaginary components, resulting from floating-point approximation error). Your original signal is purely real, so the inverse Fourier transform of it would be essentially the same as the Fourier transform, except for the sign of the imaginary component.
great. i have a question about findpeaks(abs(FTy(Iv))*2 and plot(Fv, abs(FTy(Iv))*2 in your code. why are you multiplying by 2??
Also about inverse fft, i'm struggling to make sense on how to write the code(correct me if i'm wrong)
%%%%%%%%%%% tryin to remake signal %%%%%%%%%%%%%%% long2 = length(Fv); inverse = ifft(FTy,nfft2)/long2; % xx =inverse(:,1); % yy = inverse(:,2); subplot(3,1,3) plot(long2,inverse)
‘why are you multiplying by 2??’
That has to do with the fft calculation. The Fourier transform is symmetrical, over negative and positive frequencies, over which it distributes all the energy of the signal. The result is that the amplitudes on each side have half the amplitude of the original signal. I am calculating and plotting the one-sided Fourier transform, so in order to get the amplitudes correct, it is necessary to multiply them by 2. I do the same in my plot call.

Sign in to comment.

More Answers (0)

Tags

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!