Esempio n. 1
0
def test_pupil_orientations_before_and_after_focus(plot=False,
                                                   npix_pupil=128,
                                                   npix_fov=128):
    """ Verify pupil orientations before and after focus, and signs of thin lens defocus

    1. A negative weak lens produces images (before focus) that have consistent orientation with the exit pupil
    2. A positive weak lens produces images (after focus) that have the opposite orientation as the exit pupil
    3. Images with the same magnitude but opposite signs of defocus should be 180 degree rotations of one another.

    """

    wave0 = poppy.Wavefront(diam=3 * u.m, npix=npix_pupil)
    wave0 *= poppy.LetterFAperture()
    wave1 = wave0.copy()
    wave2 = wave0.copy()

    wave1 *= poppy.ThinLens(nwaves=-5)
    wave1.propagate_to(
        poppy.Detector(fov_pixels=npix_fov,
                       pixelscale=0.03 * u.arcsec / u.pixel))

    wave2 *= poppy.ThinLens(nwaves=+5)
    wave2.propagate_to(
        poppy.Detector(fov_pixels=npix_fov,
                       pixelscale=0.03 * u.arcsec / u.pixel))

    if plot:
        fig, axes = plt.subplots(figsize=(15, 6), ncols=3)
        plt.suptitle("Before and After Focus sign test (Fresnel propagation)",
                     fontweight='bold')

        wave0.display(ax=axes[0])
        wave1.display(imagecrop=fov,
                      title='Intensity at plane before focus',
                      scale='log',
                      ax=axes[1])
        wave2.display(imagecrop=fov,
                      title='Intensity at plane after focus',
                      scale='log',
                      ax=axes[2])

    # check entrance pupil orientation
    assert brighter_top_half(wave0.intensity) and brighter_left_half(
        wave0.intensity), "Letter F should be brighter at top and left"
    # check orientation of what should be an image before focus
    assert brighter_top_half(wave1.intensity) and brighter_left_half(
        wave1.intensity
    ), "Image with negative lens (before focus) should have same orientation as the pupil "
    # check orientation of what should be an image after focus
    assert (not brighter_top_half(wave2.intensity)) and (
        not brighter_left_half(wave2.intensity)
    ), "Image with positive lens (after focus) should have opposite orientation as the pupil "
    # check consistency of defocus diffraction pattern on either side of focus, just with opposite signs (for this no-WFE case)
    assert np.allclose(
        wave1.intensity, np.rot90(wave2.intensity, 2)
    ), "Positive and negative weak lenses should be 180 degree rotation of one another"
Esempio n. 2
0
def test_CompoundOpticalSystem_fresnel(npix=128, display=False):
    """ Test that the CompoundOpticalSystem container works for Fresnel systems

    Parameters
    ----------
    npix : int
        Number of pixels for the pupil sampling. Kept small by default to
        reduce test run time.
    """

    import poppy

    opt1 = poppy.SquareAperture()
    opt2 = poppy.CircularAperture(radius=0.55)

    # a single optical system
    osys = poppy.FresnelOpticalSystem(beam_ratio=0.25, npix=npix)
    osys.add_optic(opt1)
    osys.add_optic(opt2, distance=10 * u.cm)
    osys.add_optic(poppy.QuadraticLens(1.0 * u.m))
    osys.add_optic(poppy.Detector(pixelscale=0.25 * u.micron / u.pixel,
                                  fov_pixels=512),
                   distance=1 * u.m)

    psf = osys.calc_psf(display_intermediates=display)

    if display:
        plt.figure()

    # a Compound Fresnel optical system
    osys1 = poppy.FresnelOpticalSystem(beam_ratio=0.25, npix=npix)
    osys1.add_optic(opt1)
    osys2 = poppy.FresnelOpticalSystem(beam_ratio=0.25)
    osys2.add_optic(opt2, distance=10 * u.cm)
    osys2.add_optic(poppy.QuadraticLens(1.0 * u.m))
    osys2.add_optic(poppy.Detector(pixelscale=0.25 * u.micron / u.pixel,
                                   fov_pixels=512),
                    distance=1 * u.m)

    cosys = poppy.CompoundOpticalSystem([osys1, osys2])

    psf2 = cosys.calc_psf(display_intermediates=display)

    assert np.allclose(
        psf[0].data, psf2[0].data
    ), "Results from simple and compound Fresnel systems differ unexpectedly."

    return psf, psf2
Esempio n. 3
0
def test_TipTiltStage(display=False, verbose=False):
    """ Test tip tilt stage moves the PSF by the requested amount
    """
    ap = poppy.HexagonAperture(flattoflat=0.75 * u.m)
    det = poppy.Detector(pixelscale=0.1 * u.arcsec / u.pix, fov_pixels=128)

    tt = poppy.active_optics.TipTiltStage(ap, include_factor_of_two=False)

    wave = poppy.Wavefront(npix=128, diam=1 * u.m)

    trans = ap.get_transmission(wave)
    assert np.allclose(tt.get_transmission(wave),
                       trans), "Transmission does not match expectations"
    assert np.allclose(tt.get_opd(wave),
                       0), "OPD without tilt does not match expectation"

    for tx, ty in ((0 * u.arcsec, 1 * u.arcsec), (1 * u.arcsec, 0 * u.arcsec),
                   (-0.23 * u.arcsec, 0.65 * u.arcsec)):
        for include_factor_of_two in [True, False]:

            if verbose:
                print(
                    f"Testing {tx}, {ty}, with include_factor_of_two={include_factor_of_two}"
                )

            tt.include_factor_of_two = include_factor_of_two
            tt.set_tip_tilt(tx, ty)

            wave = poppy.Wavefront(npix=64, diam=1 * u.m)
            wave *= ap
            wave *= tt

            if display:
                plt.figure()
                wave.display(what='both')
                plt.suptitle(f"Wavefront with {tx}, {ty}")

            wave.propagate_to(det)

            if display:
                plt.figure()
                wave.display()
                plt.title(f"PSF with {tx}, {ty}")

            cen = poppy.measure_centroid(wave.as_fits(),
                                         boxsize=5,
                                         relativeto='center',
                                         units='arcsec')

            factor = 2 if include_factor_of_two else 1
            assert np.isclose(cen[1] * u.arcsec, tx * factor,
                              atol=1e-4), "X pos not as expected"
            assert np.isclose(
                cen[0] * u.arcsec, ty * factor, atol=1e-4
            ), f"Y pos not as expected: {cen[0]*u.arcsec}, {ty*factor}"
Esempio n. 4
0
def test_wavefront_tilt_sign_and_direction(plot=False, npix=128):
    """ Test that tilt with increasing WFE towards the +X direction moves the PSF in the -X direction
    Fraunhofer propagation version

    See also test_core.test_source_offsets_in_OpticalSystem
    """
    # Create a wavefront and apply a tilt
    wave = poppy.Wavefront(diam=1 * u.m, npix=npix)
    wave *= poppy.CircularAperture(radius=0.5 * u.m)

    tilt_angle = -0.2  # must be a negative number (for -X direction shift), and within the FOV

    wave.tilt(
        Xangle=tilt_angle
    )  # for this function, the input is the desired direction for the image to tilt.
    # A shift to -X is implemented by creating an OPD that increases toward +X
    n = wave.shape[0]
    assert wave.wfe[n // 2, n // 2 -
                    5] < wave.wfe[n // 2, n // 2 +
                                  5], "Wavefront error should increase to +X"

    if plot:
        plt.suptitle("Wavefront tilt sign test (Fraunhofer propagation)",
                     fontweight='bold')
        wave.display(what='both')

    wave.propagate_to(poppy.Detector(pixelscale=0.05, fov_pixels=128))

    if plot:
        plt.figure()
        wave.display(what='both', crosshairs=True, imagecrop=2)

    n = wave.shape[0]
    cen = poppy.measure_centroid(wave.as_fits())
    assert np.allclose(cen[0], (n - 1) /
                       2), "Tilt in X should not displace the PSF in Y"
    assert cen[1] < (
        n - 1) / 2, "WFE tilt increasing to +X should displace the PSF to -X"
    assert np.allclose(((cen[1] - (n - 1) / 2) * u.pixel *
                        wave.pixelscale).to_value(u.arcsec),
                       tilt_angle), "PSF offset did not match expected amount"
Esempio n. 5
0
def test_CompoundOpticalSystem_hybrid(npix=128):
    """ Test that the CompoundOpticalSystem container works for hybrid Fresnel+Fraunhofer systems

    Defining "works correctly" here is a bit arbitrary given the different physical assumptions.
    For the purpose of this test we consider a VERY simple case, mostly a Fresnel system. We split
    out the first optic and put that in a Fraunhofer system. We then test that a compound hybrid
    system yields the same results as the original fully-Fresnel system.

    Parameters
    ----------
    npix : int
        Number of pixels for the pupil sampling. Kept small by default to
        reduce test run time.
    """

    import poppy

    opt1 = poppy.SquareAperture()
    opt2 = poppy.CircularAperture(radius=0.55)

    ###### Simple test case to exercise the conversion functions, with only trivial propagation
    osys1 = poppy.OpticalSystem()
    osys1.add_pupil(opt1)
    osys2 = poppy.FresnelOpticalSystem()
    osys2.add_optic(poppy.ScalarTransmission())
    osys3 = poppy.OpticalSystem()
    osys3.add_pupil(poppy.ScalarTransmission())
    osys3.add_detector(fov_pixels=64, pixelscale=0.01)
    cosys = poppy.CompoundOpticalSystem([osys1, osys2, osys3])
    psf, ints = cosys.calc_psf(return_intermediates=True)
    assert len(ints) == 4, "Unexpected number of intermediate  wavefronts"
    assert isinstance(ints[0], poppy.Wavefront), "Unexpected output type"
    assert isinstance(ints[1],
                      poppy.FresnelWavefront), "Unexpected output type"
    assert isinstance(ints[2], poppy.Wavefront), "Unexpected output type"

    ###### Harder case involving more complex actual propagations

    #===== a single Fresnel optical system =====
    osys = poppy.FresnelOpticalSystem(beam_ratio=0.25,
                                      npix=128,
                                      pupil_diameter=2 * u.m)
    osys.add_optic(opt1)
    osys.add_optic(opt2, distance=10 * u.cm)
    osys.add_optic(poppy.QuadraticLens(1.0 * u.m))
    osys.add_optic(poppy.Detector(pixelscale=0.125 * u.micron / u.pixel,
                                  fov_pixels=512),
                   distance=1 * u.m)

    #===== two systems, joined into a CompoundOpticalSystem =====
    # First part is Fraunhofer then second is Fresnel
    osys1 = poppy.OpticalSystem(npix=128,
                                oversample=4,
                                name="FIRST PART, FRAUNHOFER")
    # Note for strict consistency we need to apply a half pixel shift to optics in the Fraunhofer part;
    # this accomodates the differences between different types of image centering.
    pixscl = osys.input_wavefront().pixelscale
    halfpixshift = (pixscl * 0.5 * u.pixel).to(u.m).value
    opt1shifted = poppy.SquareAperture(shift_x=halfpixshift,
                                       shift_y=halfpixshift)
    osys1.add_pupil(opt1shifted)

    osys2 = poppy.FresnelOpticalSystem(name='SECOND PART, FRESNEL')
    osys2.add_optic(opt2, distance=10 * u.cm)
    osys2.add_optic(poppy.QuadraticLens(1.0 * u.m))
    osys2.add_optic(poppy.Detector(pixelscale=0.125 * u.micron / u.pixel,
                                   fov_pixels=512),
                    distance=1 * u.m)

    cosys = poppy.CompoundOpticalSystem([osys1, osys2])

    #===== PSF calculations =====
    psf_simple = osys.calc_psf(return_intermediates=False)
    poppy.poppy_core._log.info(
        "******=========calculation divider============******")
    psf_compound = cosys.calc_psf(return_intermediates=False)

    np.testing.assert_allclose(
        psf_simple[0].data,
        psf_compound[0].data,
        err_msg=
        "PSFs do not match between equivalent simple and compound/hybrid optical systems"
    )