Interferograms

Prysm offers rich features for analysis of interferometric data. Interferogram objects are conceptually similar to Pupils and both inherit from the same base class, as they both have to do with optical phase. The construction of an Interferogram requires only a few parameters:

>>> import numpy as np
>>> from prysm import Interferogram
>>> x = y = np.arange(129)
>>> z = np.random.uniform((128,128))
>>> interf = Interferogram(phase=z, intensity=None, unit_x=x, unit_y=y, scale='mm', phase_unit='nm', meta={'wavelength': 632.8e-9})

Notable are the scale, and phase unit, which define the xy and z units, respectively. Any SI unit is accepted as well as angstroms. Imperial units not accepted. meta is a dictionary to store metadata. For several interferogram methods to work, the wavelength must be present in meters. This departure from prysm’s convention of preferred units is used to maintain compatability with Zygo dat files. Interferograms are usually created from such files:

>>> interf = Interferogram.from_zygo_dat(your_path_file_object_or_bytes)

and both the dat and datx format from Zygo are supported. Dat carries no dependencies, while datx requries the installation of h5py. In addition to properties inherited from the OpticalPhase class (pv, rms, Sa, std), Interferograms have a dropout_percentage property, which gives the percentage of NaN values within the phase array. These NaNs may be filled,

>>> interf.fill(_with=0)

with 0 as a default value; only constants are supported. The modification is done in-place and the method returns self. Piston, tip-tilt, and power may be removed:

>>> interf.fill()\
>>>     .remove_piston()\
>>>     .remove_tiptilt()\
>>>     .remove_power()

again done in-place and returning self, so methods can be chained. One line convenience wrappers exist:

>>> interf.remove_piston_tiptilt()
>>> interf.remove_piston_tiptilt_power()

spikes may also be clipped,

>>> interf.spike_clip(nsigma=3)  # default is 3

setting points with a value more than nsigma standard deviations from the mean to NaN.

If the data did not have a lateral calibration baked into it, you can provide one in prysm,

>>> i.latcal(plate_scale=0.1, unit='mm')
>>> i.latcal(0.1, 'mm')  # these two invocations are equal

Masks may be applied:

>>> your_mask = np.ones(interf.phase.shape)
>>> interf.mask(your_mask)
>>> interf.mask('circle', diameter=100)  # 100 <spatial_unit> diameter circle
>>> interf.mask('hexagon', diameter=5)

The truecircle mask should not be used on interferometric data. the phase is deleted (replaced with NaN) wherever the mask is equal to zero.

Interferograms may be cropped, deleting empty (NaN) regions around a measurment;

>>> interf.crop()

Convenience properties are provided for data size,

>>> interf.shape, interf.diameter_x, interf.diameter_y, interf.diameter, interf.semidiameter

shape mirrors the shape of the underlying ndarray. The x and y diameters are in units of interf.spatial_unit and diameter is the greater of the two.

The two dimensional Power Spectral Density (PSD) may be computed. The data may not contain NaNs, and piston tip and tilt should be removed prior. A 2D Welch window is used, so there is no need for concern about zero values creating a discontinuity at the edge of circular or other nonrectangular apertures.

>>> interf.crop().remove_piston_tiptilt_power().fill()
>>> ux, uy, psd = interf.psd()

x, y, and azimuthally averaged cuts of the PSD are also available

>>> psd_dict = interf.psd_xy_avg()
>>> ux, psd_x = psd_dict['x']
>>> uy, psd_y = psd_dict['y']
>>> ur, psd_r = psd_Dict['avg']

and the PSD may be plotted,

>>> interf.plot_psd2d(axlim=1, interp_method='lanczos', fig=None, ax=None)
>>> interf.plot_psd_xyavg(xlim=(1e0,1e3), ylim=(1e-7,1e2), fig=None, ax=None)

For the x/y and average plot, a Lorentzian model may be plotted alongside the data for e.g. visual verification of a requirement:

>>> interf.plot_psd_xyavg(a=1,b=1,c=1)

A bandlimited RMS value derived from the 2D PSD may also be evaluated,

>>> interf.bandlimited_rms(wllow=1, wlhigh=10, flow=1, fhigh=10)

only one of wavelength (wl; spatial period) or frequency (f) should be provided. f will overrule wavelength.

The complete API documentation is below.


class prysm.interferogram.Interferogram(phase, intensity=None, x=None, y=None, scale='px', phase_unit='nm', meta=None)[source]

Bases: prysm._phase.OpticalPhase

Class containing logic and data for working with interferometric data.

bandlimited_rms(wllow=None, wlhigh=None, flow=None, fhigh=None)[source]

Calculate the bandlimited RMS of a signal from its PSD.

wllow : float
short spatial scale
wlhigh : float
long spatial scale
flow : float
low frequency
fhigh : float
high frequency
float
band-limited RMS value.
crop()[source]

Crop data to rectangle bounding non-NaN region.

dropout_percentage

Percentage of pixels in the data that are invalid (NaN).

fill(_with=0)[source]

Fill invalid (NaN) values.

_with : float, optional
value to fill with
Interferogram
self
static from_zygo_dat(path, multi_intensity_action='first', scale='mm')[source]

Create a new interferogram from a zygo dat file.

path : path_like
path to a zygo dat file
multi_intensity_action : str, optional
see io.read_zygo_dat
scale : str, optional, {‘um’, ‘mm’}
what xy scale to label the data with, microns or mm
Interferogram
new Interferogram instance
latcal(plate_scale, unit='mm')[source]

Perform lateral calibration.

This probably won’t do what you want if your data already has spatial units of anything but pixels (px).

plate_scale : float
center-to-center sample spacing of pixels, in (unit)s.
unit : str, optional
unit associated with the plate scale.
self
modified Interferogram instance.
mask(shape=None, diameter=0, mask=None)[source]

Mask the signal.

The mask will be inscribed in the axis with fewer pixels. I.e., for a interferogram with 1280x1000 pixels, the mask will be 1000x1000 at largest.

shape : str
valid shape from prysm.geometry
diameter : float
diameter of the mask, in self.spatial_units
mask : numpy.ndarray
user-provided mask
self
modified Interferogram instance.
plot_psd2d(axlim=None, clim=(1e-09, 100.0), interp_method='lanczos', fig=None, ax=None)[source]

Plot the two dimensional PSD.

axlim : float, optional
symmetrical axis limit
power : float, optional
inverse of power to stretch image by
interp_method : str, optional
method used to interpolate the image, passed directly to matplotlib imshow
fig : matplotlib.figure.Figure
Figure containing the plot
ax : matplotlib.axes.Axis
Axis containing the plot
fig : matplotlib.figure.Figure
Figure containing the plot
ax : matplotlib.axes.Axis
Axis containing the plot
plot_psd_xy_avg(a=None, b=None, c=None, lw=3, xlim=None, ylim=None, fig=None, ax=None)[source]

Plot the x, y, and average PSD on a linear x axis.

a : float, optional
a coefficient of Lorentzian PSD model plotted alongside data
b : float, optional
b coefficient of Lorentzian PSD model plotted alongside data
c : float, optional
c coefficient of Lorentzian PSD model plotted alongside data
lw : float, optional
linewidth provided directly to matplotlib
xlim : tuple, optional
len 2 tuple of low, high x axis limits
ylim : tuple, optional
len 2 tuple of low, high y axis limits
fig : matplotlib.figure.Figure
Figure containing the plot
ax : matplotlib.axes.Axis
Axis containing the plot
fig : matplotlib.figure.Figure
Figure containing the plot
ax : matplotlib.axes.Axis
Axis containing the plot

if a, b given but not c, an AB / inverse power model will be used for the PSD. If a, b, c are given the Lorentzian model will be used.

psd()[source]

Power spectral density of the data., units (self.phase_unit^2)/((cy/self.spatial_unit)^2).

unit_x : numpy.ndarray
ordinate x frequency axis
unit_y : numpy.ndarray
ordinate y frequency axis
psd : numpy.ndarray
power spectral density
psd_xy_avg()[source]

Power spectral density of the data., units (self.phase_unit^2)/((cy/self.spatial_unit)^2).

dict
with keys x, y, avg. Each containing a tuple of (unit, psd)
pvr

Peak-to-Valley residual.

See: C. Evans, “Robust Estimation of PV for Optical Surface Specification and Testing” in Optical Fabrication and Testing, OSA Technical Digest (CD) (Optical Society of America, 2008), paper OWA4. http://www.opticsinfobase.org/abstract.cfm?URI=OFT-2008-OWA4

remove_piston()[source]

Remove piston from the data by subtracting the mean value.

remove_piston_tiptilt()[source]

Remove piston/tip/tilt from the data, see remove_tiptilt and remove_piston.

remove_piston_tiptilt_power()[source]

Remove piston/tip/tilt/power from the data.

remove_power()[source]

Remove power from the data by least squares fitting.

remove_tiptilt()[source]

Remove tip/tilt from the data by least squares fitting and subtracting a plane.

spike_clip(nsigma=3)[source]

Clip points in the data that exceed a certain multiple of the standard deviation.

nsigma : float
number of standard deviations to keep
self
this Interferogram instance.
total_integrated_scatter(wavelength, incident_angle=0)[source]

Calculate the total integrated scatter (TIS) for an angle or angles.

wavelength : float
wavelength of light in microns.
incident_angle : float or numpy.ndarray
incident angle(s) of light.
float or numpy.ndarray
TIS value.