2025-02-22 05:16:38 +01:00
|
|
|
%% Compute and plot Zernike Polynomials
|
2025-02-22 00:07:28 +01:00
|
|
|
|
2025-02-22 05:16:38 +01:00
|
|
|
NumberOfGridPoints = 100; % Resolution
|
2025-02-22 00:07:28 +01:00
|
|
|
|
2025-02-22 05:16:38 +01:00
|
|
|
plotZernike(2, 0, NumberOfGridPoints) % Defocus (Z2^0)
|
2025-02-22 00:07:28 +01:00
|
|
|
%%
|
2025-02-22 05:16:38 +01:00
|
|
|
plotZernike(2, 2, NumberOfGridPoints) % Astigmatism (Z2^2)
|
2025-02-22 00:07:28 +01:00
|
|
|
%%
|
2025-02-22 05:16:38 +01:00
|
|
|
plotZernike(3, 1, NumberOfGridPoints) % Coma (Z3^1, x-direction)
|
2025-02-22 00:07:28 +01:00
|
|
|
%%
|
2025-02-22 05:16:38 +01:00
|
|
|
plotZernike(3, -1, NumberOfGridPoints) % Coma (Z3^-1, y-direction)
|
2025-02-22 00:07:28 +01:00
|
|
|
%%
|
2025-02-22 05:16:38 +01:00
|
|
|
plotZernike(4, 0, NumberOfGridPoints) % Spherical Aberration (Z4^0)
|
2025-02-22 00:07:28 +01:00
|
|
|
|
2025-02-22 05:16:38 +01:00
|
|
|
%% Compute and plot Aberrated PSF, Image
|
2025-02-23 01:24:32 +01:00
|
|
|
NumberOfGridPoints = 256; % Number of grid points per side
|
|
|
|
PupilRadius = 10E-3; % Radius of pupil [m]
|
|
|
|
Length = 32E-6; % Total size of the grid [m]
|
2025-02-22 05:16:38 +01:00
|
|
|
GridSpacing = Length / NumberOfGridPoints; % Grid spacing [m]
|
2025-02-23 01:24:32 +01:00
|
|
|
Wavelength = 421E-9; % Optical wavelength [m]
|
|
|
|
FocalLength = 3*PupilRadius; % Focal Length
|
2025-02-22 00:07:28 +01:00
|
|
|
|
2025-02-22 05:16:38 +01:00
|
|
|
% Generate PSF
|
2025-02-23 01:24:32 +01:00
|
|
|
C = [1.5, 0.0, 0.1, -0.1, 0.5]; % Zernike coefficients
|
|
|
|
PSF = generateAberratedPSF(C, Wavelength, PupilRadius, NumberOfGridPoints, GridSpacing, FocalLength);
|
|
|
|
|
|
|
|
xvals = (-NumberOfGridPoints/2 : NumberOfGridPoints/2-1) * GridSpacing * 1E6;
|
|
|
|
yvals = (-NumberOfGridPoints/2 : NumberOfGridPoints/2-1) * GridSpacing * 1E6;
|
|
|
|
plotImage(xvals, yvals, PSF, 'PSF');
|
2025-02-22 05:16:38 +01:00
|
|
|
|
|
|
|
% Generate object
|
2025-02-23 01:24:32 +01:00
|
|
|
% Object = generateObject(Wavelength, NumberOfGridPoints, GridSpacing, FocalLength);
|
2025-02-22 05:16:38 +01:00
|
|
|
|
|
|
|
% Convolve the object with the PSF to simulate imaging
|
2025-02-23 01:24:32 +01:00
|
|
|
% Image = convolveObjectWithPSF(abs(Object).^2, PSF);
|
|
|
|
% plotImage(xvals, yvals, Image, 'Image of Object formed by convolving with the PSF');
|
2025-02-22 05:16:38 +01:00
|
|
|
%% Functions
|
2025-02-22 00:07:28 +01:00
|
|
|
|
|
|
|
function Z = computeZernikePolynomials(n, m, r, theta)
|
|
|
|
% Zernike polynomial function for radial and angular coordinates (r, theta)
|
|
|
|
% Input:
|
|
|
|
% n - radial order
|
|
|
|
% m - azimuthal frequency
|
|
|
|
% r - radial coordinate (normalized to unit circle, 0 <= r <= 1)
|
|
|
|
% theta - angular coordinate (angle in radians)
|
|
|
|
%
|
|
|
|
% Output:
|
|
|
|
% Z - Zernike polynomial value at (r, theta)
|
|
|
|
|
|
|
|
if n == 2 && m == 0
|
|
|
|
% Defocus (Z2^0)
|
2025-02-23 01:24:32 +01:00
|
|
|
Z = sqrt(3) * (2 * r.^2 - 1);
|
2025-02-22 00:07:28 +01:00
|
|
|
elseif n == 2 && m == 2
|
2025-02-23 01:24:32 +01:00
|
|
|
% Vertical Astigmatism (Z2^2)
|
|
|
|
Z = sqrt(6) * r.^2 .* cos(2 * theta);
|
2025-02-22 00:07:28 +01:00
|
|
|
elseif n == 3 && m == 1
|
2025-02-23 01:24:32 +01:00
|
|
|
% Horizontal Coma (Z3^1)
|
|
|
|
Z = sqrt(8) * (3 * r.^3 - 2 * r) .* cos(theta);
|
2025-02-22 00:07:28 +01:00
|
|
|
elseif n == 3 && m == -1
|
2025-02-23 01:24:32 +01:00
|
|
|
% Vertical Coma (Z3^-1)
|
|
|
|
Z = sqrt(8) * (3 * r.^3 - 2 * r) .* sin(theta);
|
2025-02-22 00:07:28 +01:00
|
|
|
elseif n == 4 && m == 0
|
|
|
|
% Spherical Aberration (Z4^0)
|
2025-02-23 01:24:32 +01:00
|
|
|
Z = sqrt(5) * (6 * r.^4 - 6 * r.^2 + 1);
|
2025-02-22 00:07:28 +01:00
|
|
|
else
|
|
|
|
% Default to zero if no known Zernike polynomial matches
|
|
|
|
Z = 0;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2025-02-22 05:16:38 +01:00
|
|
|
function plotZernike(n, m, NumberOfGridPoints)
|
2025-02-22 00:07:28 +01:00
|
|
|
% n: radial order
|
|
|
|
% m: azimuthal frequency
|
2025-02-22 05:16:38 +01:00
|
|
|
% NumberOfGridPoints: number of points for plotting
|
2025-02-22 00:07:28 +01:00
|
|
|
|
|
|
|
% Create a grid of (r, theta) coordinates
|
2025-02-22 05:16:38 +01:00
|
|
|
[theta, r] = meshgrid(linspace(0, 2*pi, NumberOfGridPoints), linspace(0, 1, NumberOfGridPoints));
|
2025-02-22 00:07:28 +01:00
|
|
|
|
|
|
|
% Calculate the Zernike polynomial for the given (n, m)
|
|
|
|
Z = computeZernikePolynomials(n, m, r, theta);
|
|
|
|
|
|
|
|
% Convert polar to Cartesian coordinates for plotting
|
|
|
|
[X, Y] = pol2cart(theta, r);
|
|
|
|
|
|
|
|
% Plot the Zernike polynomial using a surface plot
|
|
|
|
figure(1)
|
|
|
|
clf
|
|
|
|
set(gcf,'Position',[50 50 950 750])
|
|
|
|
surf(X, Y, Z, 'EdgeColor', 'none');
|
|
|
|
colormap jet;
|
|
|
|
colorbar;
|
|
|
|
title(sprintf('Zernike Polynomial Z_{%d}^{%d}', n, m), 'Interpreter', 'tex', 'FontSize', 16);
|
|
|
|
xlabel('X', 'FontSize', 16);
|
|
|
|
ylabel('Y', 'FontSize', 16);
|
|
|
|
zlabel('Zernike Value', 'FontSize', 16);
|
|
|
|
axis equal;
|
|
|
|
shading interp;
|
|
|
|
end
|
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
function PSF = generateAberratedPSF(C, Wavelength, PupilRadius, NumberOfGridPoints, GridSpacing, FocalLength)
|
2025-02-22 00:07:28 +01:00
|
|
|
% C is the vector of Zernike coefficients [C_defocus, C_astigmatism, C_coma, C_spherical, ...]
|
2025-02-22 05:16:38 +01:00
|
|
|
% NumberOfGridPoints is the number of points for the grid (NxN grid)
|
|
|
|
% PupilRadius is the radius of the pupil aperture
|
2025-02-22 00:07:28 +01:00
|
|
|
|
2025-02-22 05:16:38 +01:00
|
|
|
% Create a grid of (x, y) of pupil-plane coordinates in Cartesian space
|
|
|
|
[X, Y] = meshgrid((-NumberOfGridPoints/2 : NumberOfGridPoints/2-1) * GridSpacing);
|
2025-02-22 00:07:28 +01:00
|
|
|
|
2025-02-22 05:16:38 +01:00
|
|
|
% Convert (x, y) to polar coordinates (r, theta)
|
2025-02-23 01:24:32 +01:00
|
|
|
[theta, ~] = cart2pol(X, Y);
|
2025-02-22 05:16:38 +01:00
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
% Pupil function
|
|
|
|
P = generatePupil(Wavelength, PupilRadius, NumberOfGridPoints, GridSpacing, FocalLength);
|
2025-02-22 00:07:28 +01:00
|
|
|
|
|
|
|
% Wavefront error from Zernike polynomials
|
2025-02-23 01:24:32 +01:00
|
|
|
W = C(1) * computeZernikePolynomials(2, 0, P, theta) + ... % Defocus (Z2^0)
|
|
|
|
C(2) * computeZernikePolynomials(2, 2, P, theta) + ... % Astigmatism (Z2^2)
|
|
|
|
C(3) * computeZernikePolynomials(3, 1, P, theta) + ... % Coma (Z3^1, x-direction)
|
|
|
|
C(4) * computeZernikePolynomials(3, -1, P, theta) + ... % Coma (Z3^-1, y-direction)
|
|
|
|
C(5) * computeZernikePolynomials(4, 0, P, theta); % Spherical Aberration (Z4^0)
|
2025-02-22 00:07:28 +01:00
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
W(P>1) = 0;
|
|
|
|
E = exp(1i*2*pi*W); % Complex amplitude
|
|
|
|
E(P>1) = 0; % Impose aperture size
|
|
|
|
|
2025-02-22 00:07:28 +01:00
|
|
|
% Fourier transform of the pupil function with aberrations
|
2025-02-23 01:24:32 +01:00
|
|
|
PSF = abs(calculateFFT2(E)).^2;
|
|
|
|
PSF = PSF/sum(PSF(:));
|
2025-02-22 00:07:28 +01:00
|
|
|
end
|
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
function plotImage(xvals, yvals, image, titlestring)
|
|
|
|
figure
|
2025-02-22 00:07:28 +01:00
|
|
|
clf
|
|
|
|
set(gcf,'Position',[50 50 950 750])
|
2025-02-23 01:24:32 +01:00
|
|
|
imagesc(xvals, yvals, image);
|
2025-02-22 00:07:28 +01:00
|
|
|
colorbar;
|
|
|
|
colormap jet;
|
2025-02-23 01:24:32 +01:00
|
|
|
title(titlestring, 'FontSize', 16);
|
2025-02-22 00:07:28 +01:00
|
|
|
xlabel('X', 'FontSize', 16);
|
|
|
|
ylabel('Y', 'FontSize', 16);
|
|
|
|
end
|
2025-02-22 05:16:38 +01:00
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
function P_Norm = generatePupil(Wavelength, PupilRadius, NumberOfGridPoints, GridSpacing, FocalLength)
|
|
|
|
[X,Y] = meshgrid((-NumberOfGridPoints/2 : NumberOfGridPoints/2-1) * ((Wavelength * FocalLength) / (NumberOfGridPoints * GridSpacing)));
|
|
|
|
P = sqrt(X.^2 + Y.^2);
|
|
|
|
P_Norm = P/PupilRadius; % Pupil normalized by aperture radius
|
|
|
|
assert(max(P_Norm(:))>=sqrt(2),'Sampling is not sufficient to reconstruct the entire wavefront.');
|
2025-02-22 05:16:38 +01:00
|
|
|
end
|
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
function G = calculateFFT2(g)
|
|
|
|
G = fftshift(fft2(ifftshift(g)));
|
2025-02-22 05:16:38 +01:00
|
|
|
end
|
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
%{
|
2025-02-22 05:16:38 +01:00
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
function g = calculateInverseFFT2(G)
|
|
|
|
g = ifftshift(ifft2(fftshift(G)));
|
2025-02-22 05:16:38 +01:00
|
|
|
end
|
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
function obj = generateObject(Wavelength, NumberOfGridPoints, GridSpacing, FocalLength)
|
|
|
|
% Image-plane coordinates
|
|
|
|
[U, V] = meshgrid((-NumberOfGridPoints/2 : NumberOfGridPoints/2-1) * ((Wavelength * FocalLength) / (NumberOfGridPoints * GridSpacing)));
|
|
|
|
obj = ;
|
2025-02-22 05:16:38 +01:00
|
|
|
end
|
|
|
|
|
2025-02-23 01:24:32 +01:00
|
|
|
function C = convolveObjectWithPSF(A, B)
|
|
|
|
C = calculateInverseFFT2(calculateFFT2(A) .* calculateFFT2(B));
|
2025-02-22 05:16:38 +01:00
|
|
|
end
|
2025-02-23 01:24:32 +01:00
|
|
|
|
|
|
|
%}
|
|
|
|
|