def test_doesnt_recalculate_when_psf_caches_mtf(): from prysm import Pupil, PSF pu = Pupil() ps = PSF.from_pupil(pu, 2) mt = otf.MTF.from_psf(ps) ps._mtf = mt mt2 = otf.MTF.from_psf(ps) assert id(mt) == id(mt2)
def test_diffprop_matches_airydisk(efl, epd, wvl): fno = efl / epd p = Pupil(wavelength=wvl, epd=epd) psf = PSF.from_pupil(p, efl) u, sx = psf.slice_x u, sy = psf.slice_y analytic = airydisk(u, fno, wvl) assert np.allclose(sx, analytic, rtol=PRECISION, atol=PRECISION) assert np.allclose(sy, analytic, rtol=PRECISION, atol=PRECISION)
def test_diffprop_matches_airydisk(efl, epd, wvl): fno = efl / epd p = Pupil(wavelength=wvl, epd=epd) psf = PSF.from_pupil(p, efl, Q=3) # use Q=3 not Q=4 for improved accuracy u, sx = psf.slice_x u, sy = psf.slice_y analytic = _airydisk(u, fno, wvl) assert np.allclose(sx, analytic, atol=PRECISION) assert np.allclose(sy, analytic, atol=PRECISION)
def test_diffprop_matches_airydisk(efl, epd, wvl): fno = efl / epd p = Pupil(dia=epd, xy_unit=u.mm, z_unit=u.nm, wavelength=mkwvl(wvl, u.um)) psf = PSF.from_pupil(p, efl, Q=3) # use Q=3 not Q=4 for improved accuracy s = psf.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 p = Pupil(wavelength=wvl, epd=epd) psf = PSF.from_pupil(p, efl) mtf = MTF.from_psf(psf) u, t = mtf.tan uu, s = mtf.sag analytic_1 = diffraction_limited_mtf(fno, wvl, frequencies=u) analytic_2 = diffraction_limited_mtf(fno, wvl, frequencies=uu) assert np.allclose(analytic_1, t, rtol=PRECISION, atol=PRECISION) assert np.allclose(analytic_2, s, rtol=PRECISION, atol=PRECISION)
def test_diffprop_matches_analyticmtf(efl, epd, wvl): fno = efl / epd p = Pupil(dia=epd, xy_unit=u.mm, z_unit=u.nm, wavelength=mkwvl(wvl, u.um)) psf = PSF.from_pupil(p, efl) mtf = MTF.from_psf(psf) s = mtf.slices() u_, x = s.x u__, y = 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, x, atol=PRECISION) assert np.allclose(analytic_2, y, atol=PRECISION)
def test_array_orientation_consistency_tilt(): ''' The pupil array should be shaped as arr[x,y], 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. ''' samples = 128 p = Seidel(W111=1, samples=samples) ps = PSF.from_pupil(p, 1) idx_y, idx_x = np.unravel_index(ps.data.argmax(), ps.data.shape) # row-major y, x assert idx_x == ps.center_x assert idx_y > ps.center_y
exit() plt.style.use('bmh') if 0: dia = 10 fnos = [1, 4] opds = [.4, .1] mtfs = [] for fno, opd in zip(fnos, opds): z = NollZernike(Z11=opd, norm=True, dia=dia, opd_unit='um', wavelength=0.1, samples=1024) ps = PSF.from_pupil(z, efl=dia * fno) mt = MTF.from_psf(ps) mtfs.append(mt.tan) fig, ax = plt.subplots() for curve, fno, opd in zip(mtfs, fnos, opds): # each element in mtfs is a tuple of (frequency, MTF). # * unpacks this and provides the x, y positional arguments to ax.plot ax.plot(*curve, label=f'F/{fno}, opd={opd}') ax.legend() ax.set(xlim=(0, 120), xlabel='Spatial Frequency [cy/mm]', ylim=(0, 1), ylabel='MTF [Rel. 1.0]') plt.show()
def sample_psf_bigger(): p = Pupil() return PSF.from_pupil(p, 20)
def sample_psf(): p = Pupil() return PSF.from_pupil(p, 10)