How to find the period of a periodic function using FFT? - matlab

Assume I have a smooth function (represented as a vector):
x=0:0.1:1000;
y=sin(2*x);
and I want to find its periodicity - pi (or even its frequency -2 ) .
I have tried the following:
nfft=1024;
Y=fft(y,nfft);
Y=abs(Y(1:nfft/2));
plot(Y);
but obviously it doesn't work (the plot does not give me a peak at "2" ).
Will you please help me find a way to find the value "2"?
Thanks in advance

You have several issues here:
You are computing the fft of x when your actual signal is y
x should be in radians
You need to define a sampling rate and use that to determine the frequency values along the x axis
So once we correct all of these things, we get:
samplingRate = 1000; % Samples per period
nPeriods = 10;
nSamples = samplingRate * nPeriods;
x = linspace(0, 2*pi*nPeriods, nSamples);
y = sin(2*x);
F = fft(y);
amplitude = abs(F / nSamples);
f = samplingRate / nSamples*[0:(nSamples/2-1),-nSamples/2:-1];
plot(f, amplitude)

In general, you can't use an FFT alone to find the period of a periodic signal. That's because an FFT does sinusoidal basis decomposition (or basis transform), and lots of non-sinusoidal waveforms (signals that look absolutely nothing like a sinewave or single sinusoidal basis vector) can be repeated to form a periodic function, waveform, or signal. Thus, it's quite possible for the frequency of a periodic function or waveform to not show up at all in an FFT result (it's called the missing fundamental problem).
Only in the case of a close or near sinusoidal signal will an FFT reliably report the reciprocal of the period of that periodic function.
There are lots of pitch detection/estimation algorithms. You can use an FFT as a sub-component of some composite methods, including cepstrums or cepstral analysis, and Harmonic Product Spectrum pitch detection methods.

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).

How can i use fft to find the maximum frequency of a periodic signal?

I'm trying to find the maximum frequency of a periodic signal in Matlab and as i know when you convert a periodic signal to the frequency spectrum you get only delta functions however i get a few curves between the produced delta functions. Here is the code :
t=[-0.02:10^-3:0.02];
s=5.*(1+cos(2*pi*10*t)).*cos(2*pi*100*t);
figure, subplot(211), plot(t,s);
y=fft(s);
subplot(212), plot(t,y);
Here is a code-snippet to help you understand how to get the frequency-spectrum using fft in matlab.
Things to remember are:
You need to decide on a sampling frequency, which should be high enough, as per the Nyquist Criterion (You need the number of samples, at least more than twice the highest frequency or else we will have aliasing). That means, fs in this example cannot be below 2 * 110. Better to have it even higher to see a have a better appearance of the signal.
For a real signal, what you want is the power-spectrum obtained as the square of the absolute of the output of the fft() function. The imaginary part, which contains the phase should contain nothing but noise. (I didn't plot the phase here, but you can do this to check for yourself.)
Finally, we need to use fftshift to shift the signal such that we get the mirrored spectrum around the zero-frequency.
The peaks would be at the correct frequencies. Now considering only the positive frequencies, as you can see, we have the largest peak at 100Hz and two further lobs around 100Hz +- 10Hz i.e. 90Hz and 110Hz.
Apparently, 110Hz is the highest frequency, in your example.
The code:
fs = 500; % sampling frequency - Should be high enough! Remember Nyquist!
t=[-.2:1/fs:.2];
s= 5.*(1+cos(2*pi*10*t)).*cos(2*pi*100*t);
figure, subplot(311), plot(t,s);
n = length(s);
y=fft(s);
f = (0:n-1)*(fs/n); % frequency range
power = abs(y).^2/n;
subplot(312), plot(f, power);
Y = fftshift(y);
fshift = (-n/2:n/2-1)*(fs/n); % zero-centered frequency range
powershift = abs(Y).^2/n;
subplot(313), plot(fshift, powershift);
The output plots:
The first plot is the signal in the time domain
The signal in the frequency domain
The shifted fft signal

Changing the inverse fast Fourier transform (ifft) to use an arbitrary waveform instead of sine waves to create a new signal

I know that an inverse fast Fourier transform (ifft) sums multiple sine waves together from data obtain from doing an fft on a signal. Is there a way to create a signal using a new type of inverse fast Fourier transform (ifft) using an arbitrary waveform instead of just using sine waves?
I'm not trying to re-create the original signal. I'm trying to create a new signal using a new type of inverse fast Fourier transform (ifft) using a given arbitrary waveform based on the (frequency, amplitude, phase) data calculated from the fft from the source signal.
The arbitrary waveform is a sampled signal that will replace one period of the sine wave used in the fft. That is, the signal is to be scaled, repeated, and shifted according to the values given by the fft.
See simple example below: the signals I will be applying FFT to are human audio signals about 60 seconds long at 44100 samples (large arrays) so I'm trying to see if I can use / alter the ifft command in some way to create a new signal using / based on an arbitrary waveform.
PS: I'm using Octave 4.0 which is similar to Matlab and the arbitrary waveform signal used to create a new signal will be changed to create different signals.
clear all,clf reset, clc,tic
fs=44100 % Sampling frequency
len_of_sig=2; %length of signal in seconds
t=linspace(0,2*pi*len_of_sig,fs*len_of_sig);
afp=[.5,2.43,pi/9;.3,3,pi/2;.3,4.3,pi/3]; %represents Amplitude,frequency,phase data array
%1 create source signal
ya=0;
for zz=1:size(afp,1)
ya = ya+afp(zz,1)*sin(afp(zz,2)*t+afp(zz,3));
end
%2 create source frequency domain data
ya_fft = fft(ya);
%3 rebuild original source signal
mag = abs(ya_fft);
phase = unwrap(angle(ya_fft));
ya_newifft=ifft(mag.*exp(i*phase));
ifft_sig_combined_L1=ifft(mag.*exp(i*phase),length(ya_newifft));
%4 %%%-----begin create arbitrary waveform to use ----
gauss = #(t, t0, g) exp(-((t-t0)/g).^2); % a simple gaussian
t_arbitrary=0:1:44100; % sampling
t_arbitrary_1 = 10000; % pulses peak positions (s)
t_arbitrary_2 = 30000; % pulses peak positions (s)
g = 2000; % pulses width (at 1/e^2) (s)
lilly = gauss(t_arbitrary, t_arbitrary_1, g) - (.57*gauss(t_arbitrary, t_arbitrary_2, g)); %different amplitude peaks
%%%%-----End arbitrary waveform to use----
%5 plot
t_sec=t./(2*pi); %converts time in radians to seconds
t_arbitrary_sec=t_arbitrary./length(lilly); %converts time in radians to seconds
subplot(4,1,1);
plot(t_sec,ya,'r')
title('1) source signal')
subplot(4,1,2);
plot(t_sec,ifft_sig_combined_L1)
title('2) rebuilt source signal using ifft')
subplot(4,1,3);
plot(t_arbitrary_sec,lilly,'r')
title('3) arbitrary waveform used to create new signal')
Added a work-flow chart below with simple signals to see if that explains it better:
Section 1) The audio signal is read into an array
Section 2) FFT is done on the signal
Section 3 Red) Normally Inverse FFT uses sin waves to rebuild the signal see signal in red
Section 3 Blue) I want to use an arbitrary signal wave instead to rebuild the signal using the FFT data calculated in (Section 2)
Section 4) New signals created using a new type of Inverse FFT (Section 3).
Please note the new type of Inverse FFT final signal (in blue ) must use the FFT data taken from the original signal.
The signal Sample rate tested should be 44100 and the length of the signal in seconds should be 57.3 seconds long. I use these numbers to test that the array can handle large amounts and that the code can handle non even numbers in seconds.
Let's start with a function lilly that takes a frequency, an amplitude and a phase (all scalars), as well as a signal length N, and computes a sine wave as expected for the inverse DFT (see note 2 below):
function out = lilly(N,periods,amp,phase)
persistent t
persistent oneperiod
if numel(t)~=N
disp('recomputung "oneperiod"');
t = 0:N-1;
oneperiod = cos(t * 2 * pi / N);
end
p = round(t * periods + phase/(2*pi)*N);
p = mod(p,N) + 1;
out = amp * oneperiod(p);
I have written this function such that it uses a sampled signal representing a single period of the since wave.
The following function uses the lilly function to compute an inverse DFT (see note 1 below):
function out = pseudoifft(ft)
N = length(ft);
half = ceil((N+1)/2);
out = abs(ft(1)) + abs(ft(half)) * ones(1,N);
for k=2:half-1
out = out + lilly(N,k-1,2*abs(ft(k)),angle(ft(k)));
end
out = out/N;
Now I test to verify that it actually computes the inverse DFT:
>> a=randn(1,256);
>> b=fft(a);
>> c=pseudoifft(b);
recomputung "oneperiod"
>> max(abs(a-c))
ans = 0.059656
>> subplot(2,1,1);plot(a)
>> subplot(2,1,2);plot(c)
The error is relatively large, due to the round function: we're subsampling the signal instead of interpolating. If you need more precision (not likely I think) you should use interp1 instead of indexing using round(p).
Next, we replace the sine in the lilly function with your example signal:
function out = lilly(N,periods,amp,phase)
persistent t
persistent oneperiod
if numel(t)~=N
disp('recomputung "oneperiod"');
t = 0:N-1;
%oneperiod = cos(t * 2 * pi / N);
gauss = #(t,t0,g) exp(-((t-t0)/g).^2); % a simple gaussian
t1 = N/4; % pulses peak positions (s)
t2 = 3*N/4; % pulses peak positions (s)
g = N/20; % pulses width (at 1/e^2) (s)
oneperiod = gauss(t,t1,g) - (.57*gauss(t,t2,g)); %different amplitude peaks
oneperiod = circshift(oneperiod,[1,-round(N/4)]); % this will make it look more like cos
end
p = round(t * periods + phase/(2*pi)*N);
p = mod(p,N) + 1;
out = amp * oneperiod(p);
The function pseudoifft now creates a function composed of your basis:
>> c=pseudoifft(b);
recomputung "oneperiod"
>> subplot(2,1,2);plot(c)
Let's look at a simpler input:
>> z=zeros(size(a));
>> z(10)=1;
>> subplot(2,1,1);plot(pseudoifft(z))
>> z(19)=0.2;
>> subplot(2,1,2);plot(pseudoifft(z))
Note 1: In your question you specifically ask to use the FFT. The FFT is simply a every efficient way of computing the forward and inverse DFT. The code above computes the inverse DFT in O(n^2), the FFT would compute the same result in O(n log n). Unfortunately, the FFT is an algorithm built on the properties of the complex exponential used in the DFT, and the same algorithm would not be possible if one were to replace that complex exponential with any other function.
Note 2: I use a cosine function in the inverse DFT. It should of course be a complex exponential. But I'm just taking a shortcut assuming that the data being inverse-transformed is conjugate symmetric. This is always the case if the input to the forward transform is real (the output of the inverse transform must be real too, the complex components of two frequencies cancel out because of the conjugate symmetry).
An IFFT is just a way to implement a IDFT. An IDFT is just a weighted sum of sinusoidal waveforms that are integer periodic in aperture.
If you want, you can take almost any DFT or IDFT algorithm or source code, and replace the sin() function with whatever other function you want for waveform synthesis. You can even use different waveforms for different frequencies, or change the synthesis frequencies to non-integer-periodic-in-aperture, if you wish.
The (inverse) Fast Fourier Transform relies on special properties of sinusoidal functions which allow one to move between the time-domain and frequency domain with a much lower computational cost (O(n.log(n))) than for arbitrary waveforms (O(n^2)). If you change the basis waveforms from sinusoids to something else, in general, you can no longer get the computational advantages of the FFT.
In your case, is sounds like you may want to generate a signal that has the same frequency spectrum as your original signal, but not necessarily the same envelope in the time-domain. As I think you've already implemented in your code, the easiest way to do that is simply to change the phase of each frequency-bin in the output of your FFT, and take the inverse FFT. That will generate a time-domain signal that has quite different appearance from your input signal, but will have the same amount of power in each frequency bin.
One subtlety you may need bear in mind is how to change the phases so that the output signal remains real-valued, rather than involving complex numbers. You can think of the Fourier transform as producing a set of positive and negative frequencies. For a real-valued signal, the corresponding positive and negative frequencies must have amplitudes that are complex conjugates of each other. Assuming that your input signal is real-valued, your FFT will already have this property, but you'll need to arrange that the random phases you apply still preserve this relationship between positive and negative frequencies. In practice, this will mean that you only have about half as many random phases to choose - none for zero-frequency bin, and one each for all the positive frequencies (the first n/2 entries) of your FFT, with the other phases being such that the phase at entry k is -1 times that of the entry at (n-k).

Detection of peaks in the frequency spectrum without using FFTs MATLAB

Assuming I have a signal x(t), would it be possible for me to detect the peak in the frequency spectrum (that is, the frequency with the highest energy content) without using FFTs ?
*PS - I saw something in Wavelet Decomposition called scale2freq(). I looked over that in the MATLAB help page and it ended up confusing me. Does the function have anything to do with frequency representations? If yes, how do I find peak frequencies using it?
What you probably want is called a pitch detection algorithm and there is a variety of them in time-domain or frequency-domain (or both of them). Please search google for "pitch detection algorithms" for further reference or check selected links for a quick overview:
http://en.wikipedia.org/wiki/Pitch_detection_algorithm
http://sound.eti.pg.gda.pl/student/eim/synteza/leszczyna/index_ang.htm
In time-domain some simple approach is to locate peaks of autocorrelation function. Indeed autocorrelation is maximal at t=0 and then next peaks gives an estimation of the main period:
ncount = 10000;
Ts = 0.0001; % Sampling period
t = (1:ncount) * Ts; % Sampling times
f = sin(2*pi*60*t) + 0.1*sin(2*pi*200*t) + 0.01 * randn(1, length(t)); % Signal
R = xcorr(f);
[~, locs] = findpeaks(R);
meanLag = Ts * mean(diff(locs));
pitch = 1 / meanLag; ===> Will be around 60 Hz
This approach is ok for very basic signals, you'll probably have to refine it upon you situation (noise level, periodicity, multi-tone, etc...). See above references for more refined algorithms.

What's the fastest way to approximate the period of data using Octave?

I have a set of data that is periodic (but not sinusoidal). I have a set of time values in one vector and a set of amplitudes in a second vector. I'd like to quickly approximate the period of the function. Any suggestions?
Specifically, here's my current code. I'd like to approximate the period of the vector x(:,2) against the vector t. Ultimately, I'd like to do this for lots of initial conditions and calculate the period of each and plot the result.
function xdot = f (x,t)
xdot(1) =x(2);
xdot(2) =-sin(x(1));
endfunction
x0=[1;1.75]; #eventually, I'd like to try lots of values for x0(2)
t = linspace (0, 50, 200);
x = lsode ("f", x0, t)
plot(x(:,1),x(:,2));
Thank you!
John
Take a look at the auto correlation function.
From Wikipedia
Autocorrelation is the
cross-correlation of a signal with
itself. Informally, it is the
similarity between observations as a
function of the time separation
between them. It is a mathematical
tool for finding repeating patterns,
such as the presence of a periodic
signal which has been buried under
noise, or identifying the missing
fundamental frequency in a signal
implied by its harmonic frequencies.
It is often used in signal processing
for analyzing functions or series of
values, such as time domain signals.
Paul Bourke has a description of how to calculate the autocorrelation function effectively based on the fast fourier transform (link).
The Discrete Fourier Transform can give you the periodicity. A longer time window gives you more frequency resolution so I changed your t definition to t = linspace(0, 500, 2000).
time domain http://img402.imageshack.us/img402/8775/timedomain.png (here's a link to the plot, it looks better on the hosting site).
You could do:
h = hann(length(x), 'periodic'); %# use a Hann window to reduce leakage
y = fft(x .* [h h]); %# window each time signal and calculate FFT
df = 1/t(end); %# if t is in seconds, df is in Hz
ym = abs(y(1:(length(y)/2), :)); %# we just want amplitude of 0..pi frequency components
semilogy(((1:length(ym))-1)*df, ym);
frequency domain http://img406.imageshack.us/img406/2696/freqdomain.png Plot link.
Looking at the graph, the first peak is at around 0.06 Hz, corresponding to the 16 second period seen in plot(t,x).
This isn't computationally that fast though. The FFT is N*log(N) operations.