Source code for ctapipe.coordinates.telescope_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__ = ["TelescopeFrame"]


_wrap_angle = Angle(180, unit=u.deg)


[docs]class TelescopeFrame(BaseCoordinateFrame): """ Telescope coordinate frame. A Frame using a UnitSphericalRepresentation. This is basically the same as a HorizonCoordinate, but the origin is at the telescope's pointing direction. This is used to specify coordinates in the field of view of a telescope that is independent of the optical properties of the telescope. ``fov_lon`` is aligned with azimuth and ``fov_lat`` is aligned with altitude of the horizontal coordinate frame as implemented in ``astropy.coordinates.AltAz``. This is what astropy calls a SkyOffsetCoordinate. Attributes ---------- telescope_pointing: SkyCoord[AltAz] Coordinate of the telescope pointing in AltAz obstime: Tiem Observation time location: EarthLocation Location of the telescope """ frame_specific_representation_info = { UnitSphericalRepresentation: [ RepresentationMapping("lon", "fov_lon"), RepresentationMapping("lat", "fov_lat"), ] } default_representation = UnitSphericalRepresentation telescope_pointing = 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 = _wrap_angle
@frame_transform_graph.transform(FunctionTransform, TelescopeFrame, TelescopeFrame) def telescope_to_telescope(from_telescope_coord, to_telescope_frame): """Transform between two skyoffset frames.""" intermediate_from = from_telescope_coord.transform_to( from_telescope_coord.telescope_pointing ) intermediate_to = intermediate_from.transform_to( to_telescope_frame.telescope_pointing ) return intermediate_to.transform_to(to_telescope_frame) @frame_transform_graph.transform(DynamicMatrixTransform, AltAz, TelescopeFrame) def altaz_to_telescope(altaz_coord, telescope_frame): """Convert a reference coordinate to an sky offset frame.""" # Define rotation matrices along the position angle vector, and # relative to the telescope_pointing. telescope_pointing = telescope_frame.telescope_pointing.represent_as( UnitSphericalRepresentation ) mat1 = rotation_matrix(-telescope_pointing.lat, "y") mat2 = rotation_matrix(telescope_pointing.lon, "z") return mat1 @ mat2 @frame_transform_graph.transform(DynamicMatrixTransform, TelescopeFrame, AltAz) def telescope_to_altaz(telescope_coord, altaz_frame): """Convert an sky offset frame coordinate to the reference frame""" # use the forward transform, but just invert it mat = altaz_to_telescope(altaz_frame, telescope_coord) # transpose is the inverse because mat is a rotation matrix return matrix_transpose(mat)