# -*- coding: utf-8 -*- """Raw transforms between coordinate frames, as NumPy matrices.""" from numpy import array from .constants import ANGVEL, ASEC2RAD, DAY_S, tau from .data.spice import inertial_frames as _inertial_frames from .functions import mxm, rot_x, rot_z def build_matrix(): # 'xi0', 'eta0', and 'da0' are ICRS frame biases in arcseconds taken # from IERS (2003) Conventions, Chapter 5. xi0 = -0.0166170 * ASEC2RAD eta0 = -0.0068192 * ASEC2RAD da0 = -0.01460 * ASEC2RAD # Compute elements of rotation matrix. yx = -da0 zx = xi0 xy = da0 zy = eta0 xz = -xi0 yz = -eta0 # Include second-order corrections to diagonal elements. xx = 1.0 - 0.5 * (yx * yx + zx * zx) yy = 1.0 - 0.5 * (yx * yx + zy * zy) zz = 1.0 - 0.5 * (zy * zy + zx * zx) return array(((xx, xy, xz), (yx, yy, yz), (zx, zy, zz))) ICRS_to_J2000 = build_matrix() del build_matrix _identity = array([(1,0,0), (0,1,0), (0,0,1)]) class ICRS(object): """The International Celestial Reference System (ICRS). The ICRS is a permanent reference frame which has replaced J2000, with which its axes agree to within 0.02 arcseconds (closer than the precision of J2000 itself). The ICRS also supersedes older equinox-based systems like B1900 and B1950. """ @staticmethod def rotation_at(t): return _identity class mean_equator_and_equinox_of_date(object): """The coordinate frame of Earth’s mean equator and equinox. This frame is used for measuring right ascension and declination. It tracks the Earth’s ‘mean’ equator and equinox which shift slowly across the sky due to precession, but ignores the smaller effects of nutation. """ @staticmethod def rotation_at(t): return mxm(t.P, ICRS_to_J2000) class true_equator_and_equinox_of_date(object): """The dynamical frame of Earth’s true equator and true equinox of date. This frame is used for measuring right ascension and declination. Unlike the fixed reference frames J2000 and the ICRS, this ‘TETE’ frame rotates slowly as the Earth’s precession and nutation shift the equinox point. Unlike the :class:`~skyfield.sgp4lib.TEME` frame, this frame doesn’t ignore nutation. This is supplied as an explicit reference frame in case you want |xyz| coordinates; if you want angles, it’s better to use the standard position method ``radec(epoch='date')`` since that will return the conventional units of hours-of-right-ascension instead of the degrees-of-longitude that ``frame_latlon()`` would return. This reference frame combines current theories of the Earth’s precession and nutation with a small offset between the ITRS and J2000 systems to produce right ascension and declination for a given date relative to the Earth’s axis and equator of rotation. """ @staticmethod def rotation_at(t): return t.M _itrs_angvel_matrix = array(( (0.0, DAY_S * ANGVEL, 0.0), (-DAY_S * ANGVEL, 0.0, 0.0), (0.0, 0.0, 0.0), )) class tirs(object): """The Terrestrial Intermediate Reference System (TIRS). Coordinates in this Earth-centered Earth-fixed (ECEF) system are measured from the axis and equator of the Earth’s rotation, ignoring the few tenths of an arcsecond by which the Earth’s actual crust and continents might be askance from the axis. (More precisely: like the ITRS this frame accounts for precession and nutation, but neglects polar motion and the TIO locator.) """ @staticmethod def rotation_at(t): return mxm(rot_z(-t.gast * tau / 24.0), t.M) @staticmethod def _dRdt_times_RT_at(t): # TODO: taking the derivative of the instantaneous angular # velocity provides a more accurate transform. return _itrs_angvel_matrix tirs = tirs() class itrs(object): """The International Terrestrial Reference System (ITRS). This is the IAU standard for an Earth-centered Earth-fixed (ECEF) coordinate system, anchored to the Earth’s crust and continents. This reference frame combines three other reference frames: the Earth’s true equator and equinox of date, the Earth’s rotation with respect to the stars, and (if your ``Timescale`` has polar offsets loaded) the polar wobble of the crust with respect to the Earth’s pole of rotation. .. versionadded:: 1.34 """ @staticmethod def rotation_at(t): R = mxm(rot_z(-t.gast * tau / 24.0), t.M) if t.ts.polar_motion_table is not None: R = mxm(t.polar_motion_matrix(), R) return R @staticmethod def _dRdt_times_RT_at(t): # TODO: taking the derivative of the instantaneous angular # velocity provides a more accurate transform. return _itrs_angvel_matrix itrs = itrs() def build_ecliptic_matrix(t): # Build the matrix to rotate an ICRF vector into ecliptic coordinates. _, d_eps = t._nutation_angles_radians true_obliquity = t._mean_obliquity_radians + d_eps return mxm(rot_x(- true_obliquity), t.M) class ecliptic_frame(object): """Reference frame of the true ecliptic and equinox of date.""" @staticmethod def rotation_at(t): return build_ecliptic_matrix(t) ecliptic_frame = ecliptic_frame() class InertialFrame(object): def __init__(self, doc, matrix): self.__doc__ = doc self._matrix = matrix def rotation_at(self, t): return self._matrix equatorial_B1950_frame = InertialFrame( 'Reference frame of the Earth’s mean equator and equinox at B1950.', _inertial_frames['B1950'], ) ecliptic_J2000_frame = InertialFrame( 'Reference frame of the true ecliptic and equinox at J2000.', _inertial_frames['ECLIPJ2000'], ) galactic_frame = InertialFrame( 'Galactic System II reference frame.', _inertial_frames['GALACTIC'], )