I want to understand the discrete fourier transform by implementing it by myself.
While the result returned by my DFT is not correct the in matlab included version returns the correct frequencies of the original signal.
So the question is, where went I wrong. Is it a math or a implementation problem?
%% Initialisation
samples=2000;
nfft = 1024;
K = nfft / 2 + 1;
c = 264;
e = 330;
t = -1:1/samples:1-1/samples;
[~, N] = size(t);
f = (sin(2*c*pi*t)+cos(2*e*pi*t)).*exp(-pi*(2*t-1).^2);
X = zeros(nfft, 1);
%% Discrete Fourier Transform
if true
for k=1:nfft
for n=1:nfft
X(k) = X(k) + f(n)*exp(-j*2*pi*(k-1)*(n-1)/N);
end
end
else
X=fft(f, nfft);
end
R = abs(X(1:K));
[V,I] = sort(R,'descend');
F1 = samples*(I(1)-1)/nfft;
F2 = samples*(I(2)-1)/nfft;
disp(F1)
disp(F2)
plot(1:K, R, 1:K, real(X(1:K)), 1:K, imag(X(1:K)))
The issue lies in the number of samples for which the transform is done.
Xall = fft(f);
plot(abs(Xall(1:500)),'b');
hold on
plot(abs(X(1:500)),'r');
What you compute matches the result from the FFT done on all samples (i.e. with 4000 real samples in and 4000 complex values out).
Now, if you read the documentation of FFT with doc fft you will see that the signal is truncated if the output size is smaller than the input size. If you try:
Y = zeros(nfft, 1);
for k=1:nfft
for n=1:nfft
Y(k) = Y(k) + f(n)*exp(-1j*2*pi*(k-1)*(n-1)/nfft);
end
end
Y2 = fft(f(:),nfft); %make it a column
abs(sum(Y-Y2)) %6.0380e-12 , result within precision of the double float format
Related
The formula for the discrete double Fourier series that I'm attempting to code in MATLAB is:
The coefficient in front of the trigonometric sum (Fourier amplitude) is what I'm trying to extract from the fitting of the data through the double Fourier series seen above. Using my current code, the original function is not reconstructed, therefore my coefficients cannot be correct. I'm not certain if this is of any significance or insight, but the second term for the A coefficients (Akn(1))) is 13 orders of magnitude larger than any other coefficient.
Any suggestions, modifications, or comments about my program would be greatly appreciated.
%data = csvread('digitized_plot_data.csv',1);
%xdata = data(:,1);
%ydata = data(:,2);
%x0 = xdata(1);
lambda = 20; %km
tau = 20; %s
vs = 7.6; %k/s (velocity of CHAMP satellite)
L = 4; %S
% Number of terms to use:
N = 100;
% set up matrices:
M = zeros(length(xdata),1+2*N);
M(:,1) = 1;
for k=1:N
for n=1:N %error using *, inner matrix dimensions must agree...
M(:,2*n) = cos(2*pi/lambda*k*vs*xdata).*cos(2*pi/tau*n*xdata);
M(:,2*n+1) = sin(2*pi/lambda*k*vs*xdata).*sin(2*pi/tau*n*xdata);
end
end
C = M\ydata;
%least squares coefficients:
A0 = C(1);
Akn = C(2:2:end);
Bkn = C(3:2:end);
% reconstruct original function values (verification check):
y = A0;
for k=1:length(Akn)
y = y + Akn(k)*cos(2*pi/lambda*k*vs*xdata).*cos(2*pi/tau*n*xdata) + Bkn(k)*sin(2*pi/lambda*k*vs*xdata).*sin(2*pi/tau*n*xdata);
end
% plotting
hold on
plot(xdata,ydata,'ko')
plot(xdata,yk,'b--')
legend('Data','Least Squares','location','northeast')
xlabel('Centered Time Event [s]'); ylabel('J[\muA/m^2]'); title('Single FAC Event (50 Hz)')
I have already had a phase screen (a 2-D NxN matrix and LxL in size scale, ex: N = 256, L = 2 meters).
I would like to find phase structure function - D(r) defined by D(delta(r)) = <[x(r)-x(r+delta(r))]^2> (<.> is ensemble averaging, r is position in phase screen in meter, x is phase value at a point in phase screen, delta(r) is variable and not fix) in Matlab program. Do you have any suggestion for my purpose?
P/S: I tried to calculate D(r) via the autocorrelation (is defined as B(r)), but this calculation still remaining some approximations. Therefore, I want to calculate precisely the result of D(r). May you please see this image to better understand the definition of D(r) and B(r). Below is my function code to calculate B(r).
% Code copied from "Numerical Simulation of Optical Wave Propagation with Examples in Matlab",
% by Jason D. Schmidt, SPIE Press, SPIE Vol. No.: PM199
% listing 3.7, page 48.
% (Schmidt defines the ft2 and ift2 functions used in this code elswhere.)
function D = str_fcn2_ft(ph, mask, delta)
% function D = str_fcn2_ft(ph, mask, delta)
N = size(ph, 1);
ph = ph .* mask;
P = ft2(ph, delta);
S = ft2(ph.^2, delta);
W = ft2(mask, delta);
delta_f = 1/(N*delta);
w2 = ift2(W.*conj(W), delta_f);
D = 2 * ft2(real(S.*conj(W)) - abs(P).^2, delta) ./ w2 .*mask;`
%fire run
N = 256; %number of samples
L = 16; %grid size [m]
delta = L/N; %sample spacing [m]
F = 1/L; %frequency-domain grid spacing[1/m]
x = [-N/2 : N/2-1]*delta;
[x y] = meshgrid(x);
w = 2; %width of rectangle
%A = rect(x/2).*rect(y/w);
A = lambdaWrapped;
%A = phz;
mask = ones(N);
%perform digital structure function
C = str_fcn2_ft(A, mask, delta);
C = real(C);
One way of directly computing this function D(r) is through random sampling: you pick two random points on your screen, determine their distance and phase difference squared, and update an accumulator:
phi = rand(256,256)*(2*pi); % the data, phase
N = size(phi,1); % number of samples
L = 16; % grid size [m]
delta = L/N; % sample spacing [m]
D = zeros(1,sqrt(2)*N); % output function
count = D; % for computing mean
for n = 1:1e6 % find a good amount of points here, the more points the better the estimate
coords = randi(N,2,2);
r = round(norm(coords(1,:) - coords(2,:)));
if r<1
continue % skip if the two coordinates are the same
end
d = phi(coords(1,1),coords(1,2)) - phi(coords(2,1),coords(2,2));
d = mod(abs(d),pi); % you might not need this, depending on how A is constructed
D(r) = D(r) + d.^2;
count(r) = count(r) + 1;
end
I = count > 0;
D(I) = D(I) ./ count(I); % do not divide by 0, some bins might not have any samples
I = count < 100;
D(I) = 0; % ignore poor estimates
r = (1:length(D)) * delta;
plot(r,D)
If you need even more precision, consider interpolating. Compute random coordinates as floating-point values, and interpolate the phase to get the values in between samples. D then needs to be longer, indexed as round(r*10) or something like that. You will need many more random samples to fill up that much larger accumulator.
How can I get the correct frequency vector to plot using the FFT of MATLAB?
My problem:
N = 64;
n = 0:N-1;
phi1 = 2*(rand-0.5)*pi;
omega1 = pi/6;
phi2 = 2*(rand-0.5)*pi;
omega2 = 5*pi/6;
w = randn(1,N); % noise
x = 2*exp(1i*(n*omega1+phi1))+4*sin(n*omega2+phi2);
h = rectwin(N).';
x = x.*h;
X = abs(fft(x));
Normally I'd do this :
f = f = Fs/Nsamples*(0:Nsamples/2-1); % Prepare freq data for plot
The problem is this time I do not have a Fs (sample frequency).
How can I do it correctly in this case?
If you don't have a Fs, simply set it to 1 (as in one sample per sample). This is the typical solution I've always used and seen everybody else use. Your frequencies will run from 0 to 1 (or -0.5 to 0.5), without units. This will be recognized by everyone as meaning "periods per sample".
Edit
From your comment I conclude that you are interested in radial frequencies. In that case you want to set your plot x-axis to
omega = 2*pi*f;
I have a 6ms-long signal with three frequency components sampled at 60kHz:
fs = 60000;
T = 0.006;
t = 0:1/fs:T;
x = 0.3*sin(2*pi*2000*t) + sin(2*pi*5000*t) + 0.4*sin(2*pi*8000*t);
I have a bandpass filter with impulse response being the difference between two sinc functions:
M = 151;
N = 303;
n = 0:(N-1);
h = (sin(0.5760*pi*(n-M))-sin(0.3665*pi*(n-M)))./pi./(n-M);
h(n==M) = 0.2094;
I designed a function that convolves the input with filter:
function y = fir_filter(h,x)
y = zeros(1,length(x)+length(h)-1);
for i = 1:length(x)
for j = 1:length(h)
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j);
end
end
And then applied the filter:
y = fir_filter(h,x);
This produced strange results:
figure(21)
ax1 = subplot(311);
plot(x);
title('Input Signal');
ax2 = subplot(312);
plot(h);
title('FIR');
ax3 = subplot(313);
plot(y);
title('Output Signal');
linkaxes([ax1,ax2,ax3],'x')
ax2.XLim = [0,length(y)];
Since the filter is bandpass, only one frequency component was expected to survive.
I tried to use yy = filter(h,1,[x,zeros(1,length(h)-1)]); and yyy = conv(h,x); and got the same results.
Please, can anybody explain me what I am doing wrong? Thank you!
Your passband does not cover any of the three signal frequency components. This can be seen directly on the graph (the second figure, containing the impulse response, has too fast variations compared with the signal). Or it can be seen from the numbers 0.5760 and 0.3655 in
h = (sin(0.5760*pi*(n-M))-sin(0.3665*pi*(n-M)))./pi./(n-M);
Why did you choose those numbers? The normalized frecuencies of the signal are [2 5 8]/60, that is, 0.0333 0.0833 0.1333. They are all below the passband [.3665 .5760]. As a result, the filter greatly attenuates the three components of the input signal.
Say you want to isolate the center frequency component (f = 5000 Hz, or f/fs = 0.08333 normalized frequency). You need a bandpass filter than lets that frequency through, and rejects the others. So you would use for example a normalized passband [.06 .1]:
h = (sin(0.06*pi*(n-M))-sin(0.1*pi*(n-M)))./pi./(n-M);
h(n==M) = (h(n==M+1)+h(n==M-1))/2; %// auto adjustment to avoid the 0/0 sample
A second problem with your code is that it gives two errors because you index h with 0. To solve this, change
n = 0:(N-1);
to
n = 1:N;
and
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j);
to
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j+1);
So, the modified code to isolate the central frequency component is:
fs = 60000;
T = 0.006;
t = 0:1/fs:T;
x = 0.3*sin(2*pi*2000*t) + sin(2*pi*5000*t) + 0.4*sin(2*pi*8000*t);
M = 151;
N = 303;
n = 1:N; %// modified
h = (sin(0.06*pi*(n-M))-sin(0.1*pi*(n-M)))./pi./(n-M); %// modified
h(n==M) = (h(n==M+1)+h(n==M-1))/2; %// modified
y = zeros(1,length(x)+length(h)-1);
for i = 1:length(x)
for j = 1:length(h)
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j+1); %// modified
end
end
figure(21)
ax1 = subplot(311);
plot(x);
title('Input Signal');
ax2 = subplot(312);
plot(h);
title('FIR');
ax3 = subplot(313);
plot(y);
title('Output Signal');
linkaxes([ax1,ax2,ax3],'x')
ax2.XLim = [0,length(y)];
The result is as follows.
As can be seen, only the central frequency component is present in the output signal.
It's also observed that the envelope of the output signal is not constant. That's because the duration of the input signal is comparable to the filter lenght. That is, you are seeing the transient response of the filter. Note that the envelop rise times is approximately the length of the filter's impulse response h.
It's interesting to note an interesting trade-off here (signal processing is full of trade-offs). To make the transient shorter you could use a wider passband (filter length is inversely proportional to passband). But then the filter would be less selective, that is, it would have less attenuation at the unwanted frequencies. For example, see the result with passband [.04 .12]:
As expected, the transient is now shorter, but the desired frequency is less pure: you can see some "modulation" caused by remains of the other frequencies.
My MATLAB code for fft and ifft below has a problem with the inverse Fourier signal y not matching the in put signal x. Is there any solution to resolve this?
N = 1000;
t0 = 1e-13;
tau = 2*1e-14;
n = [0:t0/40:2*1e-13-t0/40];
f0 = 3*1e8/(150*1e-9);
x = cos(2*pi*f0*n);
x = x.*exp((-(n-t0).^2)./(tau^2));
X = abs(fft(x,N));
F = [-N/2 : N/2 - 1]/N;
X = fftshift(X);
y=ifft(X,80);
figure(3)
plot(n,y)
I see a number of issues here:
N = 1000;
t0 = 1e-13;
tau = 2*1e-14;
n = [0:t0/40:2*1e-13-t0/40];
f0 = 3*1e8/(150*1e-9);
x = cos(2*pi*f0*n);
x = x.*exp((-(n-t0).^2)./(tau^2));
% X = abs(fft(x,N)); <-- Not seen this technique before, and why N=1000?
% try something more like:
X = fft(x);
F = [-N/2 : N/2 - 1]/N;
% this is fine to shift and plot the function
Xshifted = fftshift(X);
plot( abs( Xshifted ) )
% now you're taking the inverse of the shifted function, not what you want
% y=ifft(X,80); also not sure about the 80
y = ifft(X);
figure(3)
plot(n,y)
figure(4)
plot( n, x ); hold on; plot( n, y, 'o' )
That's all I see at first. HTH!
If you take the absolute value of the fft, you destroy the phase information needed to reconstruct the original signal, i.e. the moment you compute
X = abs(fft(x,N));
You cannot go back via ifft, because now you only have the magnitude.
Also, the inverse transformation only works if you use the same number of FFT bins with NFFT>=length(x).
y=ifft(fft(x));
should be exactly the same as x.