def test_thinlens_hopkins_agree(): # F/10 beam x, y = coordinates.make_xy_grid(128, diameter=10) dx = x[0, 1] - x[0, 0] r = np.hypot(x, y) amp = geometry.circle(5, r) phs = polynomials.hopkins(0, 2, 0, r / 5, 0, 1) * ( 1.975347661 * HeNe * 1000) # 1000, nm to um wf = propagation.Wavefront.from_amp_and_phase(amp, phs, HeNe, dx) # easy case is to choose thin lens efl = 10,000 # which will result in an overall focal length of 99.0 mm # solve defocus delta z relation, then 1000 = 8 * .6328 * 100 * x # x = 1000 / 8 / .6328 / 100 # = 1.975347661 psf = wf.focus(efl=100, Q=2).intensity no_phs_wf = propagation.Wavefront.from_amp_and_phase(amp, None, HeNe, dx) # bea tl = propagation.Wavefront.thin_lens(10_000, HeNe, x, y) wf = no_phs_wf * tl psf2 = wf.focus(efl=100, Q=2).intensity # lo and behold all ye who read this test, the lies of physical optics modeling # did the beam propagate 100, or 99 millimeters? # is the PSF we're looking at in the z=100 plane, or the z=99 plane? # the answer is simply a matter of interpretation, # if the phase screen for the thin lens is in your mind as a way of going # to z=99, then we are in the z=99 plane. # if the lens is really there, we are in the z=100 plane. assert np.allclose(psf.data, psf2.data, rtol=1e-5)
def test_circle_correct_area(): x, y = coordinates.make_xy_grid(256, diameter=2) r, _ = coordinates.cart_to_polar(x, y) mask = geometry.circle(1, r) expected_area_of_circle = x.size * 3.14 # sum is integer quantized, binary mask, allow one half quanta of error assert pytest.approx(mask.sum(), expected_area_of_circle, abs=0.5)
def test_diffprop_matches_airydisk(efl, epd, wvl): fno = efl / epd x, y = make_xy_grid(128, diameter=epd) r, t = cart_to_polar(x, y) amp = circle(epd/2, r) wf = Wavefront.from_amp_and_phase(amp/amp.sum(), None, wvl, x[0, 1] - x[0, 0]) psf = wf.focus(efl, Q=3) s = psf.intensity.slices() u_, sx = s.x u_, sy = s.y analytic = airydisk(u_, fno, wvl) assert np.allclose(sx, analytic, atol=PRECISION) assert np.allclose(sy, analytic, atol=PRECISION)
def test_diffprop_matches_analyticmtf(efl, epd, wvl): fno = efl / epd x, y = make_xy_grid(128, diameter=epd) r, t = cart_to_polar(x, y) amp = circle(epd/2, r) wf = Wavefront.from_amp_and_phase(amp, None, wvl, x[0, 1] - x[0, 0]) psf = wf.focus(efl, Q=3).intensity mtf = mtf_from_psf(psf.data, psf.dx) s = mtf.slices() u_, sx = s.x u_, sy = s.y analytic_1 = diffraction_limited_mtf(fno, wvl, frequencies=u_) analytic_2 = diffraction_limited_mtf(fno, wvl, frequencies=u_) assert np.allclose(analytic_1, sx, atol=PRECISION) assert np.allclose(analytic_2, sy, atol=PRECISION)
def test_array_orientation_consistency_tilt(): """The pupil array should be shaped as arr[y,x], as should the psf and MTF. A linear phase error in the pupil along y should cause a motion of the PSF in y. Specifically, for a positive-signed phase, that should cause a shift in the +y direction. """ N = 128 wvl = .5 Q = 3 x, y = make_xy_grid(N, diameter=2.1) r, t = cart_to_polar(x, y) amp = circle(1, r) wf = Wavefront.from_amp_and_phase(amp, None, wvl, x[0, 1] - x[0, 0]) psf = wf.focus(1, Q=Q).intensity idx_y, idx_x = np.unravel_index(psf.data.argmax(), psf.data.shape) # row-major y, x assert idx_x == (N*Q) // 2 assert idx_y > N // 2
psize = pixel_pitch / oversampling # PSF_domain_res will be output_res*oversampling # Pupil domain samples will be (PSF_domain_res)/Q_forward samples = ceil(output_res * oversampling / Q_forward) # Find pupil dx from wanted psize pup_dx = psf_sample_to_pupil_sample(psize, samples, wlen, f) # Construct pupil grid, convert to polar, construct normalized r for phase xi, eta = make_xy_grid(samples, dx=pup_dx) r, theta = cart_to_polar(xi, eta) norm_r = r / lens_R # Construct amplitude function of pupil function amp = circle(lens_R, r) amp = amp / amp.sum() # Construct phase mode aber = zernike_nm(4, 0, norm_r, theta) # spherical aberration # Scale phase mode to desired opd phase = aber * wlen / 16 * 1e3 # Construct pupil function from amp and phase functions, propagate to PSF plane, take square modulus. P = Wavefront.from_amp_and_phase(amp, phase, wlen, pup_dx) coherent_PSF = P.focus(f, Q=Q_forward) PSF = coherent_PSF.intensity # Plot PSF PSF_radius = 1.22 * wlen * fno PSF.plot2d(xlim=5 * PSF_radius, cmap='gray', clim=(0, .1))
def sample_i_mutate(): i = Interferogram.from_zygo_dat(sample_files('dat')) return i.mask(circle( 40, i.r)).crop().remove_piston().remove_tiptilt().remove_power().fill()
def test_crop_mask_works(): z = np.random.rand(32, 32) i = Interferogram(z, dx=1) i.mask(circle(10, i.r)) i.crop() assert i