In Octave, I am playing with signal processing primitives, attempting to reproduce the convolution theorem in multiple ways: that convolution in the time domain is equivalent to point-wise multiplication in the frequency domain.
I consider three routes to reconstruct the original signal:
fft
and ifft
functionsconv
functionI am attaching my working code, and the output, glad for inputs on where the bugs may be located.
N = 512; % number of points
t = 0:N-1; % [0,1,2,...,N-1]
h = exp(-t); % filter impulse reponse
H = fft(h); % filter frequency response
x = (1+t) .* sin(sqrt(1+t)); % (input signal of our choice)
y1 = conv(x,h,"same"); % Direct convolution
y2 = ifft(fft(x) .* H); % FFT convolution
T = transpose(t) * t;
W = exp(j * 2*pi/N * T); % DFT matrix
y3 = (x * W .* H) * W/N; % "Manual" convolution
lw = 2
plot(t,
x, ";orig;", "linewidth", lw+1,
y1, ";conv;", "linestyle", "--", "linewidth", lw,
real(y2), ";fft;", "linestyle", ":", "linewidth", lw,
real(y3), ";manual;", "linestyle", "-.", "linewidth", lw)
set(gca, "fontsize", 20, "linewidth", lw)
fft
but flipped horizontally.Issues:
Your definition of the kernel is not right. conv
expects the origin of the kernel to be in the middle of the array (at floor(N/2) + 1), so your t
, for the purposes of building the kernel, should be t-floor(N/2)
(this puts the 0 at the right location). Also, the kernel should be normalized to avoid changing the signal strength with the convolution. Just divide h
by its sum.
But now, H = fft(h)
will be wrong, because fft
(and the DFT) expects the origin to be on the first element of the array. Use ifftshift
to circularly shift the kernel array to put its origin on the first element: H = fft(ifftshift(h))
.
For the manual DFT, you use the same matrix for the forward and the inverse transforms. You need to conjugate transpose the matrix to compute the inverse transform: y3 = (x * W .* H) * W'/N
.
This is my corrected code (also some changes in the plotting to make it compatible with MATLAB):
N = 512; % number of points
t = 0:N-1; % [0,1,2,...,N-1]
h = exp(-abs(t-floor(N/2))); % filter impulse response (this definition is symmetric around the origin, for a causal filter set the left half of h to zero)
h = h / sum(h); % normalize
H = fft(ifftshift(h)); % filter frequency response
x = (1+t) .* sin(sqrt(1+t)); % (input signal of our choice)
y1 = conv(x,h,"same"); % Direct convolution
y2 = ifft(fft(x) .* H); % FFT convolution
T = transpose(t) * t;
W = exp(2j*pi / N * T); % DFT matrix
y3 = (x * W .* H) * W'/N; % "Manual" convolution
lw = 2;
clf
hold on
plot(t,x, "displayname", "orig", "linewidth", lw+1)
plot(t,y1, "displayname", "conv", "linestyle", "--", "linewidth", lw)
plot(t,real(y2), "displayname", "fft", "linestyle", ":", "linewidth", lw)
plot(t,real(y3), "displayname", "manual", "linestyle", "-.", "linewidth", lw)
legend