period of sawtooth from measurements - matlab

I have a series of 2D measurements (time on x-axis) that plot to a non-smooth (but pretty good) sawtooth wave. In an ideal world the data points would form a perfect sawtooth wave (with partial amplitude data points at either end). Is there a way of calculating the (average) period of the wave, using OCTAVE/MATLAB? I tried using the formula for a sawtooth from Wikipedia (Sawtooth_wave):
P = mean(time.*pi./acot(tan(y./4))), -pi < y < +pi
also tried:
P = mean(abs(time.*pi./acot(tan(y./4))))
but it didn't work, or at least it gave me an answer I know is out.
An example of the plotted data:
I've also tried the following method - should work - but it's NOT giving me what I know is close to the right answer. Probably something simple and wrong with my code. What?
slopes = diff(y)./diff(x); % form vector of slopes for each two adjacent points
for n = 1:length(diff(y)) % delete slope of any two points that form the 'cliff'
if abs(diff(y(n,1))) > pi
slopes(n,:) = [];
end
end
P = median((2*pi)./slopes); % Amplitude is 2*pi

Old post, but thought I'd offer my two-cent's worth. I think there are two reasonable ways to do this:
Perform a Fourier transform and calculate the fundamental
Do a curve-fitting of the phase, period, amplitude, and offset to an ideal square-wave.
Given curve-fitting will likely be difficult because of discontinuities in saw-wave, so I'd recommend Fourier transform. Self-contained example below:
f_s = 10; # Sampling freq. in Hz
record_length = 1000; # length of recording in sec.
% Create noisy saw-tooth wave, with known period and phase
saw_period = 50;
saw_phase = 10;
t = (1/f_s):(1/f_s):record_length;
saw_function = #(t) mod((t-saw_phase)*(2*pi/saw_period), 2*pi) - pi;
noise_lvl = 2.0;
saw_wave = saw_function(t) + noise_lvl*randn(size(t));
num_tsteps = length(t);
% Plot time-series data
figure();
plot(t, saw_wave, '*r', t, saw_function(t));
xlabel('Time [s]');
ylabel('Measurement');
legend('measurements', 'ideal');
% Perform fast-Fourier transform (and plot it)
dft = fft(saw_wave);
freq = 0:(f_s/length(saw_wave)):(f_s/2);
dft = dft(1:(length(saw_wave)/2+1));
figure();
plot(freq, abs(dft));
xlabel('Freqency [Hz]');
ylabel('FFT of Measurement');
% Estimate fundamental frequency:
[~, idx] = max(abs(dft));
peak_f = abs(freq(idx));
peak_period = 1/peak_f;
disp(strcat('Estimated period [s]: ', num2str(peak_period)))
Which outputs a couple of graphs, and also the estimated period of the saw-tooth wave. You can play around with the amount of noise and see that it correctly gets a period of 50 seconds till very high levels of noise.
Estimated period [s]: 50

Related

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

identifying phase shift between signals

I have generated three identical waves with a phase shift in each. For example:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = 10; % phase shift
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = 15; % phase shift
y3 = A*sin(2*pi*f1*t + phi); % signal 3
YY = [y1',y2',y3'];
plot(t,YY)
I would now like to use a method for detecting this phase shift between the waves. The point of doing this is so that I can eventually apply the method to real data and identify phase shifts between signals.
So far I have been thinking of computing the cross spectra between each wave and the first wave (i.e. without the phase shift):
for i = 1:3;
[Pxy,Freq] = cpsd(YY(:,1),YY(:,i));
coP = real(Pxy);
quadP = imag(Pxy);
phase(:,i) = atan2(coP,quadP);
end
but I'm not sure if this makes any sense.
Has anyone else done something similar to this? The desired outcome should show a phase shift at 10 and 15 for waves 2 and 3 respectively.
Any advice would be appreciated.
There are several ways that you can measure the phase shift between signals. Between your response, the comments below your response, and the other answers, you've gotten most of the options. The specific choice of technique is usually based on issues such as:
Noisy or Clean: Is there noise in your signal?
Multi-Component or Single-Component: Are there more than one type of signal within your recording (multiple tones at multiple frequencies moving in different directions)? Or, is there just a single signal, like in your sine-wave example?
Instantaneous or Averaged: Are you looking for the average phase lag across your entire recording, or are you looking to track how the phase changes throughout the recording?
Depending on your answer to these questions, you could consider the following techniques:
Cross-Correlation: Use the a command like [c,lag]=xcorr(y1,y2); to get the cross-correlation between the two signals. This works on the original time-domain signals. You look for the index where c is maximum ([maxC,I]=max(c);) and then you get your lag value in units of samples lag = lag(I);. This approach gives you the average phase lag for the entire recording. It requires that your signal of interest in the recording be stronger than anything else in your recording...in other words, it is sensitive to noise and other interference.
Frequency Domain: Here you convert your signals into the frequency domain (using fft or cpsd or whatever). Then, you'd find the bin that corresponds to the frequency that you care about and get the angle between the two signals. So, for example, if bin #18 corresponds to your signal's frequency, you'd get the phase lag in radians via phase_rad = angle(fft_y1(18)/fft_y2(18));. If your signals have a constant frequency, this is an excellent approach because it naturally rejects all noise and interference at other frequencies. You can have really strong interference at one frequency, but you can still cleanly get your signal at another frequency. This technique is not the best for signals that change frequency during the fft analysis window.
Hilbert Transform: A third technique, often overlooked, is to convert your time-domain signal into an analytic signal via the Hilbert transform: y1_h = hilbert(y1);. Once you do this, your signal is a vector of complex numbers. A vector holding a simple sine wave in the time domain will now be a vector of complex numbers whose magnitude is constant and whose phase is changing in sync with your original sine wave. This technique allows you to get the instantaneous phase lag between two signals...it's powerful: phase_rad = angle(y1_h ./ y2_h); or phase_rad = wrap(angle(y1_h) - angle(y2_h));. The major limitation to this approach is that your signal needs to be mono-component, meaning that your signal of interest must dominate your recording. Therefore, you may have to filter out any substantial interference that might exist.
For two sinusoidal signal the phase of the complex correlation coefficient gives you what you want. I can only give you an python example (using scipy) as I don't have a matlab to test it.
x1 = sin( 0.1*arange(1024) )
x2 = sin( 0.1*arange(1024) + 0.456)
x1h = hilbert(x1)
x2h = hilbert(x2)
c = inner( x1h, conj(x2h) ) / sqrt( inner(x1h,conj(x1h)) * inner(x2h,conj(x2h)) )
phase_diff = angle(c)
There is a function corrcoeff in matlab, that should work, too (The python one discard the imaginary part). I.e. c = corrcoeff(x1h,x2h) should work in matlab.
The Matlab code to find relative phase using cross-correlation:
fr = 20; % input signal freq
timeStep = 1e-4;
t = 0:timeStep:50; % time vector
y1 = sin(2*pi*t); % reference signal
ph = 0.5; % phase difference to be detected in radians
y2 = 0.9 * sin(2*pi*t + ph); % signal, the phase of which, is to be measured relative to the reference signal
[c,lag]=xcorr(y1,y2); % calc. cross-corel-n
[maxC,I]=max(c); % find max
PH = (lag(I) * timeStep) * 2 * pi; % calculated phase in radians
>> PH
PH =
0.4995
With the correct signals:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = 10*pi/180; % phase shift in radians
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = 15*pi/180; % phase shift in radians
y3 = A*sin(2*pi*f1*t + phi); % signal 3
The following should work:
>> acos(dot(y1,y2)/(norm(y1)*norm(y2)))
>> ans*180/pi
ans = 9.9332
>> acos(dot(y1,y3)/(norm(y1)*norm(y3)))
ans = 0.25980
>> ans*180/pi
ans = 14.885
Whether or not that's good enough for your "real" signals, only you can tell.
Here is the little modification of your code: phi = 10 is actually in degree, then in sine function, phase information is mostly expressed in radian,so you need to change deg2rad(phi) as following:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = deg2rad(10); % phase shift
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = deg2rad(15); % phase shift
y3 = A*sin(2*pi*f1*t + phi); % signal 3
YY = [y1',y2',y3'];
plot(t,YY)
then using frequency domain method as mentioned chipaudette
fft_y1 = fft(y1);
fft_y2 = fft(y2);
phase_rad = angle(fft_y1(1:end/2)/fft_y2(1:end/2));
phase_deg = rad2deg(angle(fft_y1(1:end/2)/fft_y2(1:end/2)));
now this will give you a phase shift estimate with error = +-0.2145
If you know the frequency and just want to find the phase, rather than use a full FFT, you might want to consider the Goertzel algorithm, which is a more efficient way to calculate the DFT for a single frequency (an FFT will calculate it for all frequencies).
For a good implementation, see: https://www.mathworks.com/matlabcentral/fileexchange/35103-generalized-goertzel-algorithm and https://asp-eurasipjournals.springeropen.com/track/pdf/10.1186/1687-6180-2012-56.pdf
If you use an AWGN signal with delay and apply your method it works, but if you are using a single tone frequency estimation will not help you. because there is no energy in any other frequency but the tone. You better use cross-correlation in the time domain for this - it will work better for a fixed delay. If you have a wideband signal you can use subbands domain and estimate the phase from that (it is better than FFT due to low cross-frequency dependencies).

Comparing FFT of Function to Analytical FT Solution in Matlab

I am trying to compare the FFT of exp(-t^2) to the function's analytical fourier transform, exp(-(w^2)/4)/sqrt(2), over the frequency range -3 to 3.
I have written the following matlab code and have iterated on it MANY times now with no success.
fs = 100; %sampling frequency
dt = 1/fs;
t = 0:dt:10-dt; %time vector
L = length(t); %number of sample points
%N = 2^nextpow2(L); %necessary?
y = exp(-(t.^2));
Y=dt*ifftshift(abs(fft(y)));
freq = (-L/2:L/2-1)*fs/L; %freq vector
F = (exp(-(freq.^2)/4))/sqrt(2); %analytical solution
%Y_valid_pts = Y(W>=-3 & W<=3); %compare for freq = -3 to 3
%npts = length(Y_valid_pts);
% w = linspace(-3,3,npts);
% Fe = (exp(-(w.^2)/4))/sqrt(2);
error = norm(Y - F) %L2 Norm for error
hold on;
plot(freq,Y,'r');
plot(freq,F,'b');
xlabel('Frequency, w');
legend('numerical','analytic');
hold off;
You can see that right now, I am simply trying to get the two plots to look similar. Eventually, I would like to find a way to do two things:
1) find the minimum sampling rate,
2) find the minimum number of samples,
to reach an error (defined as the L2 norm of the difference between the two solutions) of 10^-4.
I feel that this is pretty simple, but I can't seem to even get the two graphs visually agree.
If someone could let me know where I'm going wrong and how I can tackle the two points above (minimum sampling frequency and minimum number of samples) I would be very appreciative.
Thanks
A first thing to note is that the Fourier transform pair for the function exp(-t^2) over the +/- infinity range, as can be derived from tables of Fourier transforms is actually:
Finally, as you are generating the function exp(-t^2), you are limiting the range of t to positive values (instead of taking the whole +/- infinity range).
For the relationship to hold, you would thus have to generate exp(-t^2) with something such as:
t = 0:dt:10-dt; %time vector
t = t - 0.5*max(t); %center around t=0
y = exp(-(t.^2));
Then, the variable w represents angular frequency in radians which is related to the normalized frequency freq through:
w = 2*pi*freq;
Thus,
F = (exp(-((2*pi*freq).^2)/4))*sqrt(pi); %analytical solution

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

Spectrogram and what it is

I am very interested to know how the top right figure in :http://en.wikipedia.org/wiki/Spectrogram
is generated (the script) and how to analyse it i.e what information does it convey?I would appreciate a simplified answer with minimum mathematical jargons. Thank you.
The plot shows time along the horizontal axis, and frequency along the vertical axis. With pixel color showing the intensity of each frequency at each time.
A spectrogram is generated by taking a signal and chopping it into small time segments, doing a Fourier series on each segment.
here is some matlab code to generate one.
Notice how plotting the signal directly, it looks like garbage, but plotting the spectrogram, we can clearly see the frequencies of the component signals.
%%%%%%%%
%% setup
%%%%%%%%
%signal length in seconds
signalLength = 60+10*randn();
%100Hz sampling rate
sampleRate = 100;
dt = 1/sampleRate;
%total number of samples, and all time tags
Nsamples = round(sampleRate*signalLength);
time = linspace(0,signalLength,Nsamples);
%%%%%%%%%%%%%%%%%%%%%
%create a test signal
%%%%%%%%%%%%%%%%%%%%%
%function for converting from time to frequency in this test signal
F1 = #(T)0+40*T/signalLength; #frequency increasing with time
M1 = #(T)1-T/signalLength; #amplitude decreasing with time
F2 = #(T)20+10*sin(2*pi()*T/signalLength); #oscilating frequenct over time
M2 = #(T)1/2; #constant low amplitude
%Signal frequency as a function of time
signal1Frequency = F1(time);
signal1Mag = M1(time);
signal2Frequency = F2(time);
signal2Mag = M2(time);
%integrate frequency to get angle
signal1Angle = 2*pi()*dt*cumsum(signal1Frequency);
signal2Angle = 2*pi()*dt*cumsum(signal2Frequency);
%sin of the angle to get the signal value
signal = signal1Mag.*sin(signal1Angle+randn()) + signal2Mag.*sin(signal2Angle+randn());
figure();
plot(time,signal)
%%%%%%%%%%%%%%%%%%%%%%%
%processing starts here
%%%%%%%%%%%%%%%%%%%%%%%
frequencyResolution = 1
%time resolution, binWidth, is inversly proportional to frequency resolution
binWidth = 1/frequencyResolution;
%number of resulting samples per bin
binSize = sampleRate*binWidth;
%number of bins
Nbins = ceil(Nsamples/binSize);
%pad the data with zeros so that it fills Nbins
signal(Nbins*binSize+1)=0;
signal(end) = [];
%reshape the data to binSize by Nbins
signal = reshape(signal,[binSize,Nbins]);
%calculate the fourier transform
fourierResult = fft(signal);
%convert the cos+j*sin, encoded in the complex numbers into magnitude.^2
mags= fourierResult.*conj(fourierResult);
binTimes = linspace(0,signalLength,Nbins);
frequencies = (0:frequencyResolution:binSize*frequencyResolution);
frequencies = frequencies(1:end-1);
%the upper frequencies are just aliasing, you can ignore them in this example.
slice = frequencies<max(frequencies)/2;
%plot the spectrogram
figure();
pcolor(binTimes,frequencies(slice),mags(slice,:));
The inverse Fourier transform of the fourierResult matrix, will return the original signal.
Just to add to Suki's answer, here is a great tutorial that walks you through, step by step, reading Matlab spectrograms, touching on only enough math and physics to explain the main concepts intuitively:
http://www.caam.rice.edu/~yad1/data/EEG_Rice/Literature/Spectrograms.pdf