Matlab: How to find frequency, amplitude and phase of a sinusoidal function with linerary increasing frequency and phase? - matlab

I am trying to calculate amplitude and frequency modulation, as well as phase, of a sinusoidal function.
The function is decribed as follows:
fs = 128;
x1 = 1/fs:1/fs:1;
A1 = 50*x1;
B1 = 10*x1;
C1 = 1;
D1 = 1;
y1 = D1 + A1.*sin(C1 + B1.*x1);
As a result I have obtained a sinusoidal function. It's amplitude and frequency increase over time, and are dependant on time. I need to use only 128 samples, so sampling frequency was changed to 128Hz.
Now, assuming I do NOT know A1, B1, C1 or D1 parameters, and only know the sampling frequency and result of "y1", is it possible to calculate all of those parameters?
What I would like, is to be able to determine frequency, amplitude and shift of the function at any given point in time.
I know that it's possible to calculate all of those for a function with stable parameters in few ways, I personally have tried this:
zastep2 = 1 + 40.*sin(1 + 10.*x1);
x = x1';
y = zastep2';
calc = #(d) [ones(size(x)),sin(d*x),cos(d*x)]\y;
calc2 = #(d) sum((y-[ones(size(x)),sin(d*x),cos(d*x)]*calc(d)).^2);
Bw = fminbnd(calc2,1,50)
abb = calc(Bw);
Dw = abb(1)
Aw = norm(abb([2 3]))
Cw = acos(abb(2)/Aw)
"zastep2" is used to simulate a function with unchanging parameters. As a result, I get the values of Dw = 1, Cw = 1, Aw = 40 and Bw = 10, so everything is okay.
Problem is, my function's amplitude and frequency linerary increase in every step, so using this kind of solution is impossible.
Is there any way to calculate those if both frequency and amplitude increase in every step? I am of course not asking for an instant solution or complete code, but I am really stuck on this, and after searching in the Internet for quite a while, decided to ask my own question.

Instead of calculating the actual parameters A1, B1, C1 or D1 you could estimate them if you have y1 and the sampling frequency.
You could use the Cramer Rao lower bound or Maximum likelihood to find a pretty good estimate for these parameters since you already know the sampling frequency and the output y1 the estimation turns out pretty close to the original values.
https://en.wikipedia.org/wiki/Estimation_theory

Related

Determining time-dependent frequency using a sliding-window FFT

I have an instrument which produces roughly sinusoidal data, but with frequency varying slightly in time. I am using MATLAB to prototype some code to characterize the time dependence, but I'm running into some issues.
I am generating an idealized approximation of my data, I(t) = sin(2 pi f(t) t), with f(t) variable but currently tested as linear or quadratic. I then implement a sliding Hamming window (of width w) to generate a set of Fourier transforms F[I(t), t'] corresponding to the data points in I(t), and each F[I(t), t'] is fit with a Gaussian to more precisely determine the peak location.
My current MATLAB code is:
fs = 1000; %Sample frequency (Hz)
tlim = [0,1];
t = (tlim(1)/fs:1/fs:tlim(2)-1/fs)'; %Sample domain (t)
N = numel(t);
f = #(t) 100-30*(t-0.5).^2; %Frequency function (Hz)
I = sin(2*pi*f(t).*t); %Sample function
w = 201; %window width
ww=floor(w/2); %window half-width
for i=0:2:N-w
%Take the FFT of a portion of I, convolved with a Hamming window
II = 1/(fs*N)*abs(fft(I((1:w)+i).*hamming(w))).^2;
II = II(1:floor(numel(II)/2));
p = (0:fs/w:(fs/2-fs/w))';
%Find approximate FFT maximum
[~,maxIx] = max(II);
maxLoc = p(maxIx);
%Fit the resulting FFT with a Gaussian function
gauss = #(c,x) c(1)*exp(-(x-c(2)).^2/(2*c(3)^2));
op = optimset('Display','off');
mdl = lsqcurvefit(gauss,[max(II),maxLoc,10],p,II,[],[],op);
%Generate diagnostic plots
subplot(3,1,1);plot(p,II,p,gauss(mdl,p))
line(f(t(i+ww))*[1,1],ylim,'color','r');
subplot(3,1,2);plot(t,I);
line(t(1+i)*[1,1],ylim,'color','r');line(t(w+i)*[1,1],ylim,'color','r')
subplot(3,1,3);plot(t(i+ww),f(t(i+ww)),'b.',t(i+ww),mdl(2),'r.');
hold on
xlim([0,max(t)])
drawnow
end
hold off
My thought process is that the peak location in each F[I(t), t'] should be a close approximation of the frequency at the center of the window which was used to produce it. However, this does not seem to be the case, experimentally.
I have had some success using discrete Fourier analysis for engineering problems in the past, but I've only done coursework on continuous Fourier transforms--so there may be something obvious that I'm missing. Also, this is my first question on StackExchange, so constructive criticism is welcome.
So it turns out that my problem was a poor understanding of the mathematics of the sine function. I had assumed that the frequency of the wave was equal to whatever was multiplied by the time variable (e.g. the f in sin(ft)). However, it turns out that the frequency is actually defined by the derivative of the entire argument of the sine function--the rate of change of the phase.
For constant f the two definitions are equal, since d(ft)/dt = f. But for, say, f(t) = sin(t):
d(f(t)t)/dt = d(sin(t) t)/dt = t cos(t) + sin(t)
The frequency varies as a function very different from f(t). Changing the function definition to the following fixed my problem:
f = #(t) 100-30*(t-0.5).^2; %Frequency function (Hz)
G = cumsum(f(t))/fs; %Phase function (Hz)
I = sin(2*pi*G); %Sampling function

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

Matlab Average Two Fourier transforms with different frequency vectors

Suppose I have two power spectrum vectors PS1 and PS2 which were created using fft and then taking only the positive frequency values and squaring the fft values (complex conjugate really).
Suppose also that the corresponding frequency values for PS1 and PS2 are different. E.g. PS1(10) might correspond to 10 Hz and PS2(10) might correspond to 10.5 Hz.
I want to have an average of these two (and more) power spectra. How would I best create such an average? It is fine if the PS_ave is a longer vector than any of the original power spectra, so long as there is a corresponding frequency vector. So, it might be that PS_ave(11) corresponds to 10.25 Hz, and this value should probably be the average of PS1(10) and PS2(10). All ideas are welcome!
Thanks!
You might try using interp1, which can interpolate within one signal to match frequencies for another spectrum. The following example illustrates this:
v1 =1;
t1 = [0:0.1:10];
sig1 = sin(2*pi*t1*v1).*exp(-0.5*t1)/length(t1);
v2 = 0.5;
t2 = [0:0.2:10];
sig2 = cos(2*pi*t2*v2).*exp(-0.5*t2)/length(t2);
s1= fft(sig1);
s1 = s1(1:end/2);
f1 = [0:length(s1)-1]*(1/max(t1));
s2= fft(sig2);
s2 = s2(1:end/2);
f2 = [0:length(s2)-1]*(1/max(t2));
p1 = abs(s1);
p2 = abs(s2);
% Now average using interpolation to find points in the longer vector matching the shorter
p1_interp = interp1(f1,p1,f2);
power_avg = mean([p2; p1_interp],1)
hold on, plot(f2,power_avg,'r')
Here's the result (red = avg power):

Fourier Transforms in MatLab

So I have had a few posts the last few days about using MatLab to perform a convolution (see here). But I am having issues and just want to try and use the convolution property of Fourier Transforms. I have the code below:
width = 83.66;
x = linspace(-400,400,1000);
a2 = 1.205e+004 ;
al = 1.778e+005 ;
b1 = 94.88 ;
c1 = 224.3 ;
d = 4.077 ;
measured = al*exp(-((abs((x-b1)./c1).^d)))+a2;
%slit
rect = #(x) 0.5*(sign(x+0.5) - sign(x-0.5));
rt = rect(x/width);
subplot(5,1,1);plot(x,measured);title('imported data-super gaussian')
subplot(5,1,2);plot(x,(real(fftshift(fft(rt)))));title('transformed slit')
subplot(5,1,3);plot(x,rt);title('slit')
u = (fftshift(fft(measured)));
l = u./(real(fftshift(fft(rt))));
response = (fftshift(ifft(l)));
subplot(5,1,4);plot(x,real(response));title('response')
%Data Check
check = conv(rt,response,'full');
z = linspace(min(x),max(x),length(check));
subplot(5,1,5);plot(z,real(check));title('check')
My goal is to take my case, which is $measured = rt \ast signal$ and find signal. Once I find my signal, I convolve it with the rectangle and should get back measured, but I do not get that.
I have very little matlab experience, and pretty much 0 signal processing experience (working with DFTs). So any advice on how to do this would be greatly appreciated!
After considering the problem statement and woodchips' advice, I think we can get closer to a solution.
Input: u(t)
Output: y(t)
If we assume the system is causal and linear we would need to shift the rect function to occur before the response, like so:
rt = rect(((x+270+(83.66/2))/83.66));
figure; plot( x, measured, x, max(measured)*rt )
Next, consider the response to the input. It looks to me to be first order. If we assume as such, we will have a system transfer function in the frequency domain of the form:
H(s) = (b1*s + b0)/(s + a0)
You had been trying to use convolution to and FFT's to find the impulse response, "transfer function" in the time domain. However, the FFT of the rect, being a sinc has a zero crossing periodically. These zero points make using the FFT to identify the system extremely difficult. Due to:
Y(s)/U(s) = H(s)
So we have U(s) = A*sinc(a*s), with zeros, which makes the division go to infinity, which doesn't make sense for a real system.
Instead, let's attempt to fit coefficients to the frequency domain linear transfer function that we postulate is of order 1 since there are no overshoots, etc, 1st order is a reasonable place to start.
EDIT
I realized my first answer here had a unstable system description, sorry! The solution to the ODE is very stiff due to the rect function, so we need to crank down the maximum time step and use a stiff solver. However, this is still a tough problem to solve this way, a more analytical approach may be more tractable.
We use fminsearch to find the continuous time transfer function coefficients like:
function x = findTf(c0,u,y,t)
% minimize the error for the estimated
% parameters of the transfer function
% use a scaled version without an offset for the response, the
% scalars can be added back later without breaking the solution.
yo = (y - min(y))/max(y);
x = fminsearch(#(c) simSystem(c,u,y,t),c0);
end
% calculate the derivatives of the transfer function
% inputs and outputs using the estimated coefficient
% vector c
function out = simSystem(c,u,y,t)
% estimate the derivative of the input
du = diff([0; u])./diff([0; t]);
% estimate the second derivative of the input
d2u = diff([0; du])./diff([0; t]);
% find the output of the system, corresponds to measured
opt = odeset('MaxStep',mean(diff(t))/100);
[~,yp] = ode15s(#(tt,yy) odeFun(tt,yy,c,du,d2u,t),t,[y(1) u(1) 0],opt);
% find the error between the actual measured output and the output
% from the system with the estimated coefficients
out = sum((yp(:,1) - y).^2);
end
function dy = odeFun(t,y,c,du,d2u,tx)
dy = [c(1)*y(3)+c(2)*y(2)-c(3)*y(1);
interp1(tx,du,t);
interp1(tx,d2u,t)];
end
Something like that anyway should get you going.
x = findTf([1 1 1]',rt',measured',x');

inverse fast fourier transform for frequency range

My problem is to obtain original signal from amplitude spectrum (fft) based on inverse fft but only for some frequency range ex. 8-12 Hz. Could anyone help me? I try to used:
xdft=fft(x);
ixdft=ifft(xdft(a:b)), %where xdft(a:b) is |Y(f)| for freq 8-12 Hz.
But it doesn't want to work.
You can set all the values of xdft to zero except those you want, i.e.,
xdft = fft(x);
xdft = xdft(1:ceil(length(xdft) / 2));
xdft(1:a) = 0;
xdft(b+1:end) = 0;
ixdft = ifft(xdft, 'symmetric');
The reason I have taken only half of the original FFT'd data is that your result will be symmetric about Fs / 2 (where Fs is the sample rate), and if you don't do the same thing to the frequencies either side of the centre, you will get a complex signal out. Instead of doing the same thing to both sides manually, I've just taken one side, modified it, and told ifft that it has to reconstruct the data for the full frequency range by appending a mirror image of what you pass it; this by done by calling it with the 'symmetric' option.
If you need to figure out what a and b should be for some frequency, you can first create a vector of the frequencies at which you've performed the FFT, then find those frequencies that are within your range, like so:
xdft = fft(x);
xdft = xdft(1:ceil(length(xdft) / 2));
f = linspace(0, Fs / 2, length(xdft));
keepInd = f >= 8 & f <= 12; % Keep frequencies between 8 and 12 Hz
xdft(~keepInd) = 0;
Note that I've actually omitted the use of the two variables a and b altogether in this example and opted for logical indexing, and that Fs is the sample rate.