Source code for ctapipe.coordinates.nominal_frame
"""
The code in this module is basically a copy of
https://docs.astropy.org/en/stable/_modules/astropy/coordinates/builtin_frames/skyoffset.html
We are just not creating a metaclass and a factory but directly building the
corresponding class.
"""
import astropy.units as u
from astropy.coordinates import (
AltAz,
Angle,
BaseCoordinateFrame,
CoordinateAttribute,
DynamicMatrixTransform,
EarthLocationAttribute,
FunctionTransform,
RepresentationMapping,
TimeAttribute,
UnitSphericalRepresentation,
frame_transform_graph,
)
from astropy.coordinates.matrix_utilities import matrix_transpose, rotation_matrix
__all__ = ["NominalFrame"]
[docs]
class NominalFrame(BaseCoordinateFrame):
"""
Nominal coordinate frame.
A Frame using a UnitSphericalRepresentation.
This is basically the same as a HorizonCoordinate, but the
origin is at an arbitrary position in the sky.
This is what astropy calls a SkyOffsetCoordinate
If the telescopes are in divergent pointing, this Frame can be
used to transform to a common system.
Attributes
----------
origin: astropy.coordinates.SkyCoord[AltAz]
Origin of this frame as a HorizonCoordinate
obstime: astropy.time.Time
Observation time
location: astropy.coordinates.EarthLocation
Location of the telescope
"""
frame_specific_representation_info = {
UnitSphericalRepresentation: [
RepresentationMapping("lon", "fov_lon"),
RepresentationMapping("lat", "fov_lat"),
]
}
default_representation = UnitSphericalRepresentation
origin = CoordinateAttribute(default=None, frame=AltAz)
obstime = TimeAttribute(default=None)
location = EarthLocationAttribute(default=None)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# make sure telescope coordinate is in range [-180°, 180°]
if isinstance(self._data, UnitSphericalRepresentation):
self._data.lon.wrap_angle = Angle(180, unit=u.deg)
@frame_transform_graph.transform(FunctionTransform, NominalFrame, NominalFrame)
def nominal_to_nominal(from_nominal_coord, to_nominal_frame):
"""Transform between two skyoffset frames."""
intermediate_from = from_nominal_coord.transform_to(from_nominal_coord.origin)
intermediate_to = intermediate_from.transform_to(to_nominal_frame.origin)
return intermediate_to.transform_to(to_nominal_frame)
@frame_transform_graph.transform(DynamicMatrixTransform, AltAz, NominalFrame)
def altaz_to_nominal(altaz_coord, nominal_frame):
"""Convert a reference coordinate to an sky offset frame."""
# Define rotation matrices along the position angle vector, and
# relative to the origin.
origin = nominal_frame.origin.represent_as(UnitSphericalRepresentation)
mat1 = rotation_matrix(-origin.lat, "y")
mat2 = rotation_matrix(origin.lon, "z")
return mat1 @ mat2
@frame_transform_graph.transform(DynamicMatrixTransform, NominalFrame, AltAz)
def nominal_to_altaz(nominal_coord, altaz_frame):
"""Convert an sky offset frame coordinate to the reference frame"""
# use the forward transform, but just invert it
mat = altaz_to_nominal(altaz_frame, nominal_coord)
# transpose is the inverse because mat is a rotation matrix
return matrix_transpose(mat)