{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Interferograms\n", "\n", "Prysm offers rich features for analysis of interferometric data. Interferogram objects are conceptually similar to [Pupils](./Pupils.ipynb) and both inherit from the same base class, as they both have to do with optical phase. We begin by performing a few imports:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from prysm import Interferogram, sample_files\n", "\n", "from matplotlib import pyplot as plt\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The construction of an `Interferogram` requires only a few parameters:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = y = np.arange(129)\n", "z = np.random.rand(128, 128)\n", "interf = Interferogram(phase=z, intensity=None, x=x, y=y, meta=dict(), xy_unit=None, z_unit=None, labels=None)\n", "print(interf)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`xy/z_units` and `labels` have default values of `None` and control the units the data is in as well as the labels used for plotting. For more information, see the documentation on [units and labels](./units-and-labels.html) meta is a dictionary to store metadata. Interferograms are usually created from Zygo dat files. One is provided as a sample file:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf = Interferogram.from_zygo_dat(sample_files('dat'))\n", "interf.plot2d()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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 `PVr` property, for [C. Evan's Robust PV metric](https://www.spiedigitallibrary.org/journals/Optical-Engineering/volume-48/issue-4/043605/PVr-a-robust-amplitude-parameter-for-optical-surface-specification/10.1117/1.3119307.short?SSO=1), and `dropout_percentage` property, which gives the percentage of NaN values within the phase array. These NaNs may be filled," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf.fill(_with=0)\n", "interf.plot2d()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# no plot here - the sample file already has this processing done\n", "interf.remove_piston().remove_tiptilt().remove_power()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "again done in-place and returning self, so methods can be chained. You should remove these terms (or, indeed do anything with Zernikes) before filling NaNs. One line convenience wrappers exist:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf.remove_piston_tiptilt()\n", "interf.remove_piston_tiptilt_power()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "spikes may also be clipped," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf.spike_clip(nsigma=3) # default is 3\n", "interf.plot2d()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "setting points with a value more than nsigma standard deviations from the mean to NaN.\n", "\n", "Lateral calibrations can be stripped and applied:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# this is not going to do what you want if your data is already calibrated.\n", "interf.strip_latcal()\n", "interf.latcal(plate_scale=0.1, unit='mm')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Masks may be applied:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "your_mask = np.ones(interf.phase.shape)\n", "interf.mask(your_mask)\n", "interf.mask('circle', diameter=40) # 30 interf.units.x diameter circle\n", "interf.plot2d()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `truecircle` mask should not be used on interferometric data. the phase is deleted (replaced with NaN) wherever the mask is equal to zero.\n", "\n", "Interferograms may be cropped, deleting empty (NaN) regions around a measurment;" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf.crop()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or padded," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf.pad(3) # this works out to a 10% of diameter on each side pad\n", "interf.plot2d()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convenience properties are provided for data size," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf.shape, interf.size, interf.diameter_x, interf.diameter_y, interf.diameter, interf.semidiameter" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`shape` and `size` mirrors the underlying `interf.phase` ndarray. The x and y diameters are in units of `interf.spatial_unit` and `diameter` is the greater of the two.\n", "\n", "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 for circular data and a 2D Hanning window for rectangular data, so there is no need for concern about zero values creating a discontinuity at the edge of circular or other nonrectangular apertures." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf.crop().remove_piston_tiptilt_power().fill()\n", "psd = interf.psd()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The psd variable is a `PSD` (`RichData`) object, so it can be used as any other, e.g. for [slicing](./slicing.html) or plotting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When plotting PSD slices, power law models can be overlain:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from prysm.plotting import add_psd_model\n", "\n", "# a, b, and c given => abc model\n", "fig, ax = psd.slices().plot('azavg', invert_x=True, ylim=(1e-4, 1e4))\n", "fig, ax = add_psd_model(psd, fig=fig, ax=ax, invert_x=True,\n", " lw=3, ls='--', color='r', alpha=0.6,\n", " a=3e3, b=2/10, c=4)\n", "\n", "# a, b given => ab model\n", "fig, ax = add_psd_model(psd, fig=fig, ax=ax, invert_x=True,\n", " color='g', alpha=0.6, lw=2, ls=':',\n", " a=1, b=3.5)\n", "\n", "# you can also pass in your own model via the `psd_fcn` keyword argument.\n", "# Its signature should look like:\n", "def your_own_psd_fcn(nu, **kwargs):\n", " pass" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(slices of) the PSD can be fit to these analytic functions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from prysm.interferogram import fit_psd\n", "\n", "a, b, c = fit_psd(*psd.slices().azavg, guess=(2e3, 2/10, 3.75))\n", "\n", "print(a, b, c)\n", "\n", "fig, ax = psd.slices().plot('azavg', invert_x=True, ylim=(1e-4, 1e4))\n", "fig, ax = add_psd_model(psd, fig=fig, ax=ax, invert_x=True, a=a, b=b, c=c, alpha=0.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A bandlimited RMS value derived from the 2D PSD may also be evaluated," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "interf.bandlimited_rms(wllow=1, wlhigh=10, flow=1, fhigh=10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "only one of wavelength (wl; spatial period) or frequency (f) should be provided. f will clobber wavelength." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For details on plotting or slicing interferograms, see [plotting](./plotting.html) and [slicing](./slicing.html)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.4" } }, "nbformat": 4, "nbformat_minor": 2 }