<< Chapter < Page | Chapter >> Page > |
Another approach would be to find the center of mass of the data. This decision rule is more comprehensive because it takes into account all data from the Fourier transform, not just the maximum value. In order to find the average weight of all amplitudes, we change the inner part of code to the following (starting with "%find the maximum one"):
...
%find the frequency corresponding to the "average" amplitudeavg_freq = sum(freq.*amps)/(sum(amps)*df);%decided which way the car should move based on the max frequency
if avg_freq<freq_criterion;
...
One can imagine myriad other ways to approach this problem. Many strategies have been developed, but the question is open-ended. A natural next step is for the reader to think of new ways to interpret spectrogram data. The most effective characterizations probably have yet to be discovered!
In this module we developed the tools to decompose an arbitrary signal, such as an EEG, into is component frequencies. We began with sine waves, established the trapezoid scheme, and finally introduced Fourier analysis. This same flavor of analysis is used in many other settings, too–see the related documents.
mytrapz.m
function curve_area = mytrapz(x, y, fast)
% function curve_area = mytrapz(x, y, fast)%
% mytrapz.m performs the trapezoid rule on the vector given by x and y.%
% Input:% x - a vector containing the domain of the function
% y - a vector containing values of the function corresponding to the% values in 'x'
if nargin<3
curve_area = 0;%loop through and add up trapezoids for as many points as we are givenfor n = 2 : numel(x)
height = (y(n) + y(n-1))/2; %average height of function across intervalbase = x(n) - x(n-1); %length of interval
trap_area = base * height; %area of trapezoidcurve_area = curve_area + trap_area; %add to continuing sum
endelseif fast%alternate (fast) implementation
xvals = x(3:end) - x(1:end-2);yvals = y(2:end-1);
curve_area = yvals(:)'*xvals(:);curve_area = curve_area + y(1)*(x(2) - x(1)) + y(end)*(x(end) - x(end-1));
curve_area = curve_area/2;end
myfreq.m
% myfreq.m
%% find the frequencies and amplitudes at which a wave is "vibrating"
%% Contrast simple (but laborious) trapezoid computations to the fast
% and flexible built-in fft command (fft stands for fast Fourier% transform).
% To make full sense of this we will need to think about complex% numbers and the complex exponential function.
%T = 5;% duration of signal
dt = 0.001; % time between signal samplest = 0:dt:T;
N = length(t);y = 2.5*sin(3*2*pi*t) - 4.2*sin(4*2*pi*t); % a 2-piece wave
plot(t,y)xlabel('time (seconds)')
ylabel('signal')for f = 1:5, % compute the amplitudes as ratios of areas
a(f) = trapz(t,y.*sin(f*2*pi*t))/trapz(t,sin(f*2*pi*t).^2);end
figureplot(1:5,a,'ko') % plot the amplitudes vs frequency
hold onplot(1:5, [0 0 2.5 -4.2 0], 'b*')figure(34)
f = (0:N-1)/T;% fft frequenciessc = N*trapz(t,sin(2*pi*t).^2)/T; % fft scale factor
A = fft(y);newa = -imag(A)/sc;
plot(f,newa,'r+')y = y + 3*cos(6*2*pi*t); % add a cosine piece
figure(1)hold on
plot(t,y,'g') % plot ithold off
legend('2 sines','2 sines and 1 cosine')figure(2)
A = fft(y); % take the fft of the new signalnewa = -imag(A)/sc;
plot(f,newa,'gx')b = real(A)/sc;
plot(f,b,'gx')xlim([0 7]) % focus in on the low frequencieshold off
xlabel('frequency (Hz)')ylabel('amplitude')
legend('by hand','by fft','with cosine')
myfourier.m
% function [mag freq] = myfourier(y, dt, use_fft)%
% myfourier.m decomposes the signal 'y', taken with sample interval dt,% into its component frequencies.
%% Input:
%% y -- signal vection
% dt -- sample interval (s/sample) of y% use_fft -- if designated, use matlab's fft instead of trapezoid method
%% Output:
%% freq -- frequency domain
% mag -- magnitude of frequency components of y corresponding to 'freq'function [freq mag] = myfourier(y, dt, use_fft)y = y(:);
N = numel(y); %number of samplesT = N*dt; %total time
t = linspace(0,T,N)'; %reconstruct time vectorhalf_N = floor(N/2); %ensures that N/2 is an integer
if mod(N,2) %treat differently if f odd or evenfreq = (-half_N:half_N)'/T; %fft frequencies
elsefreq = (-half_N:half_N-1)'/T; %fft frequencies
endif nargin<3 %perform explicit Fourier transform
sinmag = zeros(size(freq)); %vector for component magnitudescosmag = zeros(size(freq)); %vector for component magnitudes%loop through each frequency we will test
for n = 1 : numel(freq)%obtain coefficient for freqency 'freq(n)'
sinmag(n) = mytrapz(t, y.*sin(freq(n)*2*pi*t), 1);cosmag(n) = mytrapz(t, y.*cos(freq(n)*2*pi*t), 1);
end%scale to account for sample lengthscale_factor = mytrapz(t, sin(2*pi*t).^2);
sinmag = sinmag / scale_factor;cosmag = cosmag / scale_factor;
mag = [sinmag(:) cosmag(:)];elseif use_fft %use built-in MATLAB fft() for speed
fft_scale_factor = mytrapz(t, sin(2*pi*t).^2) * N / T;A = fft(y);
mag(:,1) = -imag(A)/fft_scale_factor;mag(:,2) = real(A)/fft_scale_factor;
mag = circshift(mag, half_N);end
mysgram.m
%
% function [stft_plot freq tm]= my_stft(y, dt, Nwindow)
%% my_stft splits the signa 'y' into time windows, the breaks each
% segment into its component frequencies. See "Short-time Fourier Transform"%
%% Input:
% y -- signal% dt -- sample interval
% Nwindow -- number of time intervals to analyze%
% Output:% stft_plot -- values plotted in the spectrogram
% freq -- frequency domain% tm -- time domain
function [stft_plot freq tm hh]= mysgram(y, dt, Nwindow)
%count the number of windowsN = numel(y);
win_len = floor(N/Nwindow);sm = zeros(win_len, Nwindow);
cm = zeros(win_len, Nwindow);tm = linspace(0, numel(y) * dt, Nwindow);
%for each windowfor n = 1:Nwindow
%isolate the part of the signal we want to deal withsig_win = y((n-1)*win_len + 1 : n*win_len);
%perform the fourier transform[freq mg] = myfourier(sig_win, dt, 1);sm(:,n) = mg(1:win_len,1);
cm(:,n) = mg(1:win_len,2);end
stft_plot = abs(sm + cm);stft_plot = stft_plot(end/2:end, :);
%plot the fourier transform over timehh = imagesc(tm, freq(round(end/2):end), stft_plot);
title('Spectrogram', 'FontSize', 20)xlabel('time', 'FontSize', 16)
ylabel('frequency', 'FontSize', 16)set(gca, 'ydir', 'normal')
%just look at lower frequenciesylim([0-win_len/2 50+win_len/2])
Notification Switch
Would you like to follow the 'The art of the pfug' conversation and receive update notifications?