Source code for vmpt.coords
"""V2/V3 <-> RA/Dec coordinate transforms ported from footprint_emerald.ipynb."""
import numpy as np
import pysiaf
from astropy import units as u
from astropy.coordinates import SkyCoord
_siaf = pysiaf.Siaf("NIRSpec")
_msa_ap = _siaf["NRS_FULL_MSA"]
MSA_V2_REF: float = float(_msa_ap.V2Ref)
MSA_V3_REF: float = float(_msa_ap.V3Ref)
# V3IdlYAngle for NRS_FULL_MSA: rotation between V3 and the aperture's Ideal-Y.
# Used to convert PA_V3 <-> PA_AP for the eMPT pointing_summary export.
V3_IDL_Y_ANGLE: float = float(_msa_ap.V3IdlYAngle)
[docs]
def rot_matrix(rotation: float = 30.0) -> np.ndarray:
theta = np.radians(rotation)
c, s = np.cos(theta), np.sin(theta)
return np.array(((c, -s), (s, c)))
[docs]
def shutter_corners_v2v3(v2c: float, v3c: float, w: float = 0.20, h: float = 0.46) -> np.ndarray:
# 138.5 deg is the MSA tilt within the V2/V3 frame.
return np.array([v2c, v3c]) + np.dot(
np.array([[-w / 2, w / 2, w / 2, -w / 2],
[-h / 2, -h / 2, h / 2, h / 2]]).T,
rot_matrix(138.5),
)
[docs]
def v2v3_to_radec(coord_c: SkyCoord, pa_v3: float, corners_v2v3: np.ndarray) -> SkyCoord:
offsets = corners_v2v3 - np.array([MSA_V2_REF, MSA_V3_REF])
offsets = np.dot(offsets, rot_matrix(pa_v3))
return coord_c.spherical_offsets_by(
offsets.T[0] * u.arcsec, offsets.T[1] * u.arcsec
)
# NIRSpec fixed-slit apertures (always rendered on the canvas).
FIXED_SLIT_NAMES: tuple[str, ...] = (
"NRS_S200A1_SLIT",
"NRS_S200A2_SLIT",
"NRS_S400A1_SLIT",
"NRS_S1600A1_SLIT",
"NRS_S200B1_SLIT",
)
[docs]
def fixed_slit_corners_v2v3() -> dict[str, np.ndarray]:
"""Return {slit_name: (N, 2) corners in V2/V3 arcsec} for the five fixed slits."""
out: dict[str, np.ndarray] = {}
for name in FIXED_SLIT_NAMES:
ap = _siaf[name]
pts = np.array(ap.closed_polygon_points(to_frame="tel"))
# pysiaf returns (2, N) arrays of (v2, v3); transpose to (N, 2).
out[name] = pts.T if pts.shape[0] == 2 else pts
return out