예제 #1
0
def mft_f2p(E_foc, fl, wavelength, dxi, deta, dx, N, centering='pixel'):
    """
    Propagate a field from a focal plane to a pupil plane, using a matrix-multiply DFT.

    Parameters
    ----------
    E_foc : array_like
        Electric field array in focal plane
    fl : float
        Focal length of Fourier transforming lens
    wavelength : float
        Propagation wavelength
    dxi : float
        Step size along horizontal axis of focal plane
    deta : float
        Step size along vertical axis of focal plane
    dx : float
        Step size along either axis of focal plane.  The vertical and horizontal step sizes are
        assumed to be equal.
    N : int
        Number of datapoints along each side of the pupil-plane (output) array
    centering : string
        Whether the input and output arrays are pixel-centered or inter-pixel-centered.
        Possible values: 'pixel', 'interpixel'

    Returns
    -------
    array_like
        Field in pupil plane, after propagating through Fourier transforming lens

    """
    if centering not in _VALID_CENTERING:
        raise ValueError(_CENTERING_ERR)
    check.twoD_array(E_foc, 'E_foc', TypeError)
    check.real_scalar(fl, 'fl', TypeError)
    check.real_positive_scalar(wavelength, 'wavelength', TypeError)
    check.real_positive_scalar(dxi, 'dxi', TypeError)
    check.real_positive_scalar(deta, 'deta', TypeError)
    check.real_positive_scalar(dx, 'dx', TypeError)
    check.positive_scalar_integer(N, 'N', TypeError)

    Neta, Nxi = E_foc.shape
    dy = dx  # Assume equal sample spacing along both directions

    # Focal-plane coordinates
    xi = util.create_axis(Nxi, dxi, centering=centering)[:, None]  # Broadcast to column vector
    eta = util.create_axis(Neta, dxi, centering=centering)[None, :]  # Broadcast to row vector

    # Pupil-plane coordinates
    x = util.create_axis(N, dx, centering=centering)[None, :]  # Broadcast to row vector
    y = x.T  # Column vector

    # Fourier transform matrices
    pre = np.exp(-2 * np.pi * 1j * (y * eta) / (wavelength * fl))
    post = np.exp(-2 * np.pi * 1j * (xi * x) / (wavelength * fl))

    # Constant scaling factor in front of Fourier transform
    scaling = np.sqrt(dx * dy * dxi * deta) / (1 * wavelength * fl)

    return scaling * np.linalg.multi_dot([pre, E_foc, post])
예제 #2
0
    def test_positive_scalar_integer_good(self):
        """
        Verify checker works correctly for valid input.

        Type: positive scalar integer
        """
        try:
            check.positive_scalar_integer(1, 'psi', TestCheckException)
        except check.CheckException:
            self.fail('positive_scalar_integer failed on valid input')
        pass
예제 #3
0
    def test_positive_scalar_integer_bad_var(self):
        """
        Fail on invalid variable type.

        Type: positive scalar integer
        """
        for v0 in [1.0, -1, 0, 1j, (1., ), [5, 5], 'psi']:
            with self.assertRaises(TestCheckException):
                check.positive_scalar_integer(v0, 'psi', TestCheckException)
                pass
            pass
        pass
예제 #4
0
 def test_positive_scalar_integer_bad_vexc(self):
     """Fail on input vexc not an Exception."""
     with self.assertRaises(check.CheckException):
         check.positive_scalar_integer(1, 'psi', 'TestCheckException')
         pass
     pass
예제 #5
0
 def test_positive_scalar_integer_bad_vname(self):
     """Fail on invalid input name for user output."""
     with self.assertRaises(check.CheckException):
         check.positive_scalar_integer(1, (1, ), TestCheckException)
         pass
     pass
예제 #6
0
def apply_neighbor_rule(Vin, Vlim, Nact):
    """
    Apply the neighbor rule to DM commands.

    Find neighboring actuators that exceed a specified difference
    in voltage and to scale down those voltages until the rule is met.

    Parameters
    ----------
    Vin : numpy ndarray
        2-D array of DM voltage commands
    Vlim : float
        maximum difference in command values between neighboring actuators
    Nact : int
        Number of actuators across the DM

    Returns
    -------
    Vout : numpy ndarray
        2-D array of DM voltage commands
    indPair : numpy ndarray
        [nPairs x 2] array of tied actuator linear indices

    """
    check.twoD_array(Vin, 'Vin', TypeError)
    check.real_scalar(Vlim, 'Vlim', TypeError)
    check.positive_scalar_integer(Nact, 'Nact', TypeError)
    
    Vout = Vin  # Initialize output voltage map
    indPair = np.zeros((0,2))  # Initialize the paired indices list. [nPairs x 2]
    
    kx1 = np.array([[0, 1], [1, 1], [1, 0]])              # R1-C1
    kx2 = np.array([[0,1], [1,1], [1,0], [1,-1]])         # R1, C2 - C47
    kx3 = np.array([[1,0], [1,-1]])                       # R1, C48
    kx4 = np.array([[-1,1], [0,1], [1,1], [1,0]])         # R2-R47, C1
    kx5 = np.array([[-1,1], [0,1], [1,1], [1,0], [1,-1]]) # R2-47, C2-47
    kx6 = np.array([[1,0], [1,-1]])                       # R2-47, C8
    kx7 = np.array([[-1,1], [0,1]])                       # R48, C1 - C47
    kx8 = np.array([[-1,-1]])                             # R48, C48
    
    for jj in range(Nact):            # Row
        for ii in range(Nact):        # Col
                    
            if jj == 0:
                if ii == 0:
                    kx = kx1
                elif ii < Nact-1:
                    kx = kx2
                else:
                    kx = kx3
            elif jj < Nact-1:
                if ii == 0:
                    kx = kx4
                elif ii < Nact-1:
                    kx = kx5
                else:
                    kx = kx6
            else:
                if ii < Nact-1:
                    kx = kx7
                else:
                    kx = kx8
                
            kr = jj + kx[:,0]
            kc = ii + kx[:,1]
            nNbr = kr.size  # length(kr); # Number of neighbors
                    
            if nNbr >= 1:
                for iNbr in range(nNbr):
                    
                    a1 = Vout[jj, ii] - Vout[kr[iNbr],kc[iNbr]] # Compute the delta voltage
                    
                    if (np.abs(a1) > Vlim):  # If neighbor rule is violated
                        
                        indLinCtr = (ii-1)*Nact + jj  # linear index of center actuator
                        indLinNbr = (kc[iNbr]-1)*Nact + kr[iNbr]  # linear index of neigboring actuator
                        indPair = np.array([indPair, np.array([indLinCtr, indLinNbr]).reshape(1, 2)])
                        indPair = np.vstack([indPair, np.array([indLinCtr, indLinNbr]).reshape(1, 2)])
    
                        fx = (np.abs(a1) - Vlim) / 2.
                        Vout[jj, ii] = Vout[jj, ii] - np.sign(a1)*fx
                        Vout[kr[iNbr], kc[iNbr]] = Vout[kr[iNbr], kc[iNbr]] +\
                                                    np.sign(a1)*fx

    return Vout, indPair
예제 #7
0
def gen_surf_from_act(dm, dx, N):
    """
    Function to compute the surface shape of a deformable mirror. Uses PROPER.

    Parameters
    ----------
    dm : ModelParameters
        Structure containing parameter values for the DM
    dx : float
        Pixel width [meters] at the DM plane
    N : int
        Number of points across the array to return at the DM plane

    Returns
    -------
    DMsurf : array_like
        2-D surface map of the DM

    """
    check.real_positive_scalar(dx, 'dx', TypeError)
    check.positive_scalar_integer(N, 'N', TypeError)
    # if type(dm) is not falco.config.Object:
    #     raise TypeError('Input "dm" must be of type falco.config.Object')

    # Set the order of operations
    flagXYZ = True
    if(hasattr(dm, 'flagZYX')):
        if(dm.flagZYX):
            flagXYZ = False

    # Adjust the centering of the output DM surface. The shift needs to be in
    # units of actuators, not meters, for prop_dm.m.
    Darray = dm.NdmPad*dm.dx
    Narray = dm.NdmPad
    if dm.centering == 'interpixel':
        cshift = -Darray/2./Narray/dm.dm_spacing
    elif dm.centering == 'pixel':
        cshift = 0

    pupil_ratio = 1  # beam diameter fraction
    wl_dummy = 1e-6  # dummy value needed to initialize PROPER (meters)

    bm = proper.prop_begin(N*dx, wl_dummy, N, pupil_ratio)

    # Apply various constraints to DM commands
    dm = enforce_constraints(dm)

    # Quantization of DM actuation steps based on least significant bit of the
    # DAC (digital-analog converter). In height, so called HminStep
    # If HminStep (minimum step in H) is defined, then quantize the DM voltages
    if(hasattr(dm, 'HminStep')):
        if not(hasattr(dm, 'HminStepMethod')):
            dm.HminStepMethod = 'round'
        # Discretize/Quantize the DM voltages (creates dm.Vquantized)
        dm = discretize_surf(dm, dm.HminStepMethod)
        heightMap = dm.VtoH*dm.Vquantized
    else:  # Quantization not desired; send raw, continuous voltages
        heightMap = dm.VtoH*dm.V

    if hasattr(dm, 'orientation'):
        if dm.orientation.lower() == 'rot0':
            pass  # no change
        elif dm.orientation.lower() == 'rot90':
            heightMap = np.rot90(heightMap, 1)
        elif dm.orientation.lower() == 'rot180':
            heightMap = np.rot90(heightMap, 2)
        elif dm.orientation.lower() == 'rot270':
            heightMap = np.rot90(heightMap, 3)
        elif dm.orientation.lower() == 'flipxrot0':
            heightMap = np.flipx(heightMap)
        elif dm.orientation.lower() == 'flipxrot90':
            heightMap = np.rot90(np.flipx(heightMap), 1)
        elif dm.orientation.lower() == 'flipxrot180':
            heightMap = np.rot90(np.flipx(heightMap), 2)
        elif dm.orientation.lower() == 'flipxrot270':
            heightMap = np.rot90(np.flipx(heightMap), 3)
        else:
            raise ValueError('invalid value of dm.orientation')

    # Generate the DM surface
    DMsurf = falco.dm.propcustom_dm(bm, heightMap, dm.xc-cshift, dm.yc-cshift,
    dm.dm_spacing, XTILT=dm.xtilt, YTILT=dm.ytilt, ZTILT=dm.zrot, XYZ=flagXYZ,
    inf_sign=dm.inf_sign, inf_fn=dm.inf_fn)

    return DMsurf
예제 #8
0
def mft_p2f(E_pup, fl, wavelength, dx, dxi, Nxi, deta, Neta, centering='pixel'):
    """
    Propagate a pupil to a focus using a matrix-multiply DFT.

    Parameters
    ----------
    E_pup : array_like
        Electric field array in pupil plane
    fl : float
        Focal length of Fourier transforming lens
    wavelength : float
        Propagation wavelength
    dx : float
        Step size along either axis of focal plane.  The vertical and horizontal step sizes are
        assumed to be equal.
    dxi : float
        Step size along horizontal axis of focal plane
    Nxi : int
        Number of samples along horizontal axis of focal plane.
    deta : float
        Step size along vertical axis of focal plane
    Neta : int
        Number of samples along vertical axis of focal plane.
    centering : string
        Whether the input and output arrays are pixel-centered or inter-pixel-centered.
        Possible values: 'pixel', 'interpixel'

    Returns
    -------
    array_like
        Field in pupil plane, after propagating through Fourier transforming lens

    """
    if centering not in _VALID_CENTERING:
        raise ValueError(_CENTERING_ERR)
    check.twoD_array(E_pup, 'E_pup', TypeError)
    check.real_scalar(fl, 'fl', TypeError)
    check.real_positive_scalar(wavelength, 'wavelength', TypeError)
    check.real_positive_scalar(dx, 'dx', TypeError)
    check.real_positive_scalar(dxi, 'dxi', TypeError)
    check.positive_scalar_integer(Nxi, 'Nxi', TypeError)
    check.real_positive_scalar(deta, 'deta', TypeError)
    check.positive_scalar_integer(Neta, 'Neta', TypeError)

    dy = dx
    M, N = E_pup.shape
    if M != N:
        raise ValueError('Input array is not square')

    # Pupil-plane coordinates
    x = util.create_axis(N, dx, centering=centering)[:, None]  # Broadcast to column vector
    y = x.T  # Row vector

    # Focal-plane coordinates
    xi = util.create_axis(Nxi, dxi, centering=centering)[None, :]  # Broadcast to row vector
    eta = util.create_axis(Neta, deta, centering=centering)[:, None]  # Broadcast to column vector

    # Fourier transform matrices
    pre = np.exp(-2 * np.pi * 1j * (eta * y) / (wavelength * fl))
    post = np.exp(-2 * np.pi * 1j * (x * xi) / (wavelength * fl))

    # Constant scaling factor in front of Fourier transform
    scaling = np.sqrt(dx * dy * dxi * deta) / (1 * wavelength * fl)

    return scaling * np.linalg.multi_dot([pre, E_pup, post])