distortion_fit#

esis.flights.f1.optics.distortion_fit(grid=None, axis_channel='channel', num_distribution=11, axis_time=None)[source]#

Apply the best-fit distortion parameters to the ESIS-I design().

The per-channel parameters are loaded from _data/distortion_reference.ecsv, the best distortion fit of the ESIS-I flight data, optimized against the time=15 frame of the 2019-09-30 flight (esis.flights.f1.data.level_1(), with a start time of 2019-09-30T18:08:41.642 UTC). The provenance of the fit is recorded in the file header.

If axis_time is given, the instrument pointing additionally carries the fitted per-frame payload pointing along that axis, one element per frame of esis.flights.f1.data.level_1(). During the flight the payload pointing drifted by several arcseconds (dominated by yaw, which sweeps monotonically from \(+3.3''\) at the first frame to \(-4.4''\) at the last); the optics are otherwise held fixed at the reference fit. The offsets are common to all four channels (a rigid-payload model) and are loaded from _data/distortion_pointing.ecsv, which records the provenance of the per-frame fits in its header.

Parameters:
  • grid (None | ObjectVectorArray) – sampling of wavelength, field, and pupil positions that will be used to characterize the optical system.

  • axis_channel (str) – The name of the logical axis corresponding to changing camera channel.

  • num_distribution (int) – number of Monte Carlo samples to draw when computing uncertainties

  • axis_time (None | str) – The name of the logical axis corresponding to changing time. If None, the pointing is that of the time=15 reference fit; otherwise the pitch, yaw, and roll gain one element per Level-1 frame.

Return type:

Instrument

Examples

Overplot the ray-traced detector footprint of each spectral line onto the Level-1 frame that the distortion fit was optimized against. Each line’s footprint should land on its corresponding image of the field stop.

import numpy as np
import astropy.units as u
import named_arrays as na
import esis

l1 = esis.flights.f1.data.level_1()[dict(time=15)]
model = esis.flights.f1.optics.distortion_fit(num_distribution=0)

rays = model.system.rayfunction_default.outputs
position = rays.position.to(u.um).mean(axis=("pupil_x", "pupil_y"))
position = position / model.camera.sensor.width_pixel * u.pixel

fig, ax = na.plt.subplots(
    figsize=(8, 17),
    constrained_layout=True,
    axis_rows="channel",
    nrows=l1.shape["channel"],
    sharex=True,
    origin="upper",
)
fig.suptitle(
    "ESIS-I distortion fit vs. Level-1 data"
    " (2019-09-30 18:08:41 UTC)"
)
na.plt.set_xlabel("detector $x$ (pix)", ax=ax[dict(channel=~0)])
na.plt.set_ylabel("detector $y$ (pix)", ax=ax)
na.plt.set_aspect("equal", ax=ax)
na.plt.pcolormesh(
    l1.inputs.pixel.x,
    l1.inputs.pixel.y,
    C=l1.outputs.value,
    ax=ax,
    vmax=np.percentile(l1.outputs.value, 99),
)
na.plt.text(
    x=0.5,
    y=1.01,
    s=l1.channel,
    transform=na.plt.transAxes(ax),
    ax=ax,
    ha="center",
    va="bottom",
)
spectral_lines = ["He I", "Mg X", "O V"]
colors = ["red", "orange", "yellow"]
for i in range(len(spectral_lines)):
    j = dict(wavelength=i)
    na.plt.scatter(
        position.x[j] + 1024 * u.pixel,
        position.y[j] + 512 * u.pixel,
        color=colors[i],
        ax=ax,
        s=8,
        where=rays.unvignetted[j],
        label=spectral_lines[i],
    )
ax.ndarray[0].legend(loc="upper right");
../_images/esis.flights.f1.optics.distortion_fit_0_2.png