Пример #1
0
def gaussian(sigma=0.5, samples=128):
    """Generate a gaussian mask with a given sigma.

    Parameters
    ----------
    sigma : `float`
        width parameter of the gaussian, expressed in samples of the output array

    samples : `int`
        number of samples in square array

    Returns
    -------
    `numpy.ndarray`
        mask with gaussian shape

    """
    s = sigma

    x = m.arange(0, samples, 1, dtype=config.precision)
    y = x[:, m.newaxis]

    # // is floor division in python
    x0 = y0 = samples // 2
    return m.exp(-4 * m.log(2) * ((x - x0)**2 + (y - y0)**2) /
                 (s * samples)**2)
Пример #2
0
def resample_2d_complex(array, sample_pts, query_pts):
    '''Resamples a 2D complex array.

    Works by interpolating the magnitude and phase independently and merging the results into a complex value.

    Parameters
    ----------
    array : `numpy.ndarray`
        complex 2D array
    sample_pts : `tuple`
        pair of `numpy.ndarray` objects that contain the x and y sample locations,
        each array should be 1D
    query_pts : `tuple`
        points to interpolate onto, also 1D for each array

    Returns
    -------
    `numpy.ndarray`
        array resampled onto query_pts via bivariate spline

    '''
    xq, yq = m.meshgrid(*query_pts)
    mag = abs(array)
    phase = m.angle(array)

    magfunc = interpolate.RegularGridInterpolator(sample_pts, mag)
    phasefunc = interpolate.RegularGridInterpolator(sample_pts, phase)

    interp_mag = magfunc((yq, xq))
    interp_phase = phasefunc((yq, xq))

    return interp_mag * m.exp(1j * interp_phase)
Пример #3
0
def longexposure_otf(nu, Cn, z, f, lambdabar, h_z_by_r=2.91):
    """Compute the long exposure OTF for given parameters.

    Parameters
    ----------
    nu : `numpy.ndarray`
        spatial frequencies, cy/mm
    Cn: `float`
        atmospheric structure constant of refractive index, ranges ~ 10^-13 - 10^-17
    z : `float`
        propagation distance through atmosphere, m
    f : `float`
        effective focal length of the optical system, mm
    lambdabar : `float`
        mean wavelength, microns
    h_z_by_r : `float`, optional
        constant for h[z/r] -- see Eq. 8.5-37 & 8.5-38 in Statistical Optics, J. Goodman, 2nd ed.

    Returns
    -------
    `numpy.ndarray`
        the OTF

    """
    # homogenize units
    nu = nu / 1e3
    f = f / 1e3
    lambdabar = lambdabar / 1e6

    power = 5 / 3
    const1 = -m.pi**2 * 2 * h_z_by_r * Cn**2
    const2 = z * f**power / (lambdabar**3)
    nupow = nu**power
    const = const1 * const2
    return m.exp(const * nupow)
Пример #4
0
def resample_2d_complex(array, sample_pts, query_pts):
    ''' Resamples a 2D complex array by interpolating the magnitude and phase
        independently and merging the results into a complex value.

    Args:
        array (numpy.ndarray): complex 2D array.

        sample_pts (tuple): pair of numpy.ndarray objects that contain the x and y sample locations,
            each array should be 1D.

        query_pts (tuple): points to interpolate onto, also 1D for each array.

    Returns:
        numpy.ndarray array resampled onto query_pts via bivariate spline

    '''
    xq, yq = np.meshgrid(*query_pts)
    mag = abs(array)
    phase = np.angle(array)

    magfunc = interpolate.RegularGridInterpolator(sample_pts, mag)
    phasefunc = interpolate.RegularGridInterpolator(sample_pts, phase)

    interp_mag = magfunc((yq, xq))
    interp_phase = phasefunc((yq, xq))

    return interp_mag * exp(1j * interp_phase)
Пример #5
0
def matrix_dft(f, alpha, npix, shift=None, unitary=False):
    '''
    A technique shamelessly stolen from Andy Kee @ NASA JPL
    Is it magic or math?
    '''
    if np.isscalar(alpha):
        ax = ay = alpha
    else:
        ax = ay = np.asarray(alpha)

    f = np.asarray(f)
    m, n = f.shape

    if np.isscalar(npix):
        M = N = npix
    else:
        M = N = np.asarray(npix)

    if shift is None:
        sx = sy = 0
    else:
        sx = sy = np.asarray(shift)

    # Y and X are (r,c) coordinates in the (m x n) input plane, f
    # V and U are (r,c) coordinates in the (M x N) output plane, F
    X = np.arange(n) - floor(n / 2) - sx
    Y = np.arange(m) - floor(m / 2) - sy
    U = np.arange(N) - floor(N / 2) - sx
    V = np.arange(M) - floor(M / 2) - sy

    E1 = exp(1j * -2 * np.pi * (ay / m) * np.outer(Y, V).T)
    E2 = exp(1j * -2 * np.pi * (ax / m) * np.outer(X, U))

    F = E1.dot(f).dot(E2)

    if unitary is True:
        norm_coef = sqrt((ay * ax) / (m * n * M * N))
        return F * norm_coef
    else:
        return F
Пример #6
0
    def fcn(self):
        """Complex wavefunction associated with the pupil."""
        phase = self.change_phase_unit(to='waves', inplace=False)

        fcn = m.exp(1j * 2 * m.pi *
                    phase)  # phase implicitly in units of waves, no 2pi/l
        # guard against nans in phase
        fcn[m.isnan(phase)] = 0

        if self.mask_target in ('fcn', 'both'):
            fcn *= self._mask

        return fcn
Пример #7
0
def blackbody_spectrum(temperature, wavelengths):
    ''' Computes the spectral power distribution of a black body at a given
        temperature.

    Args:
        temperature (`float`): body temp, in Kelvin.

        wavelengths (`numpy.ndarray`): array of wavelengths, in nanometers.

    Returns:
        numpy.ndarray: spectral power distribution in units of W/m^2/nm

    '''
    wavelengths = wavelengths.astype(config.precision) / 1e9
    return (2 * h * c ** 2) / (wavelengths ** 5) * \
        1 / (exp((h * c) / (wavelengths * k * temperature) - 1))
Пример #8
0
    def _phase_to_wavefunction(self):
        """Compute the wavefunction from the phase.

        Returns
        -------
        `Pupil`
            this pupil instance

        """
        phase = self.change_phase_unit(to='waves', inplace=False)

        self.fcn = m.exp(1j * 2 * m.pi *
                         phase)  # phase implicitly in units of waves, no 2pi/l
        # guard against nans in phase
        self.fcn[m.isnan(phase)] = 0
        return self
Пример #9
0
def gaussian(sigma=0.5, samples=128):
    ''' Generates a gaussian mask with a given sigma

    Args:
        sigma (`float`): width parameter of the gaussian, expressed in radii of
            the output array.

        samples (`int`): number of samples in square array.

    Returns:
        `numpy.ndarray`: mask with gaussian shape.

    '''
    s = sigma

    x = np.arange(0, samples, 1, float)
    y = x[:, np.newaxis]

    # // is floor division in python
    x0 = y0 = samples // 2
    return exp(-4 * log(2) * ((x - x0**2) + (y - y0)**2) / (s * samples)**2)
Пример #10
0
def merge_pupils(pupil1, pupil2):
    '''Merges the phase from two pupils and returns a new :class:`Pupil` instance

    Args:
        pupil1 (:class:`Pupil`): first pupil

        pupil2 (:class:`Pupil`): second pupil

    Returns
        :class:`Pupil`:  New pupil with merged phase

    '''
    if pupil1.sample_spacing != pupil2.sample_spacing or pupil1.samples != pupil2.samples:
        raise ValueError('Pupils must be identically sampled')

    # create a new pupil and copy Pupil1's dictionary into it
    retpupil = pupil1.clone()

    retpupil.phase = pupil1.phase + pupil2.phase
    retpupil.fcn = exp(1j * 2 * pi / retpupil.wavelength * retpupil.phase)
    retpupil.clip()
    return retpupil
Пример #11
0
    def total_integrated_scatter(self, wavelength, incident_angle=0):
        """Calculate the total integrated scatter (TIS) for an angle or angles.

        Parameters
        ----------
        wavelength : `float`
            wavelength of light in microns.
        incident_angle : `float` or `numpy.ndarray`
            incident angle(s) of light.

        Returns
        -------
        `float` or `numpy.ndarray`
            TIS value.

        """
        if self.spatial_unit != 'μm':
            raise ValueError('Use microns for spatial unit when evaluating TIS.')

        upper_limit = 1 / wavelength
        kernel = 4 * m.pi * m.cos(m.radians(incident_angle))
        kernel *= self.bandlimited_rms(upper_limit, None) / wavelength
        return 1 - m.exp(-kernel**2)
Пример #12
0
def synthesize_surface_from_psd(psd, nu_x, nu_y):
    """Synthesize a surface height map from PSD data.

    Parameters
    ----------
    psd : `numpy.ndarray`
        PSD data, units nm²/(cy/mm)²
    nu_x : `numpy.ndarray`
        x spatial frequency, cy/mm
    nu_y : `numpy.ndarray`
        y spatial frequency, cy_mm

    """
    # generate a random phase to be matched to the PSD
    randnums = m.rand(*psd.shape)
    randfft = m.fft2(randnums)
    phase = m.angle(randfft)


    # calculate the output window
    # the 0th element of nu_y has the greatest frequency in magnitude because of
    # the convention to put the nyquist sample at -fs instead of +fs for even-size arrays
    fs = -2 * nu_y[0]
    dx = dy = 1 / fs
    ny, nx = psd.shape
    x, y = m.arange(nx) * dx, m.arange(ny) * dy

    # calculate the area of the output window, "S2" in GH_FFT notation
    A = x[-1] * y[-1]

    # use ifft to compute the PSD
    signal = m.exp(1j * phase) * m.sqrt(A * psd)

    coef = 1 / dx / dy
    out = m.ifftshift(m.ifft2(m.fftshift(signal))) * coef
    out = out.real
    return x, y, out
Пример #13
0
 def _phase_to_wavefunction(self):
     ''' Computes the wavefunction from the phase
     '''
     self.fcn = exp(1j * 2 * pi / self.wavelength * self.phase)
     return self