e2m2e.algorithm.coordinate.standard_axes 源代码

"""常用坐标轴实现。"""

from __future__ import annotations

from pathlib import Path

import numpy as np
import numpy.typing as npt

from ...data.constants import SECONDS_PER_DAY
from ...data.frames.eop import CoordinateDataError, EopFile
from ...data.frames.gmat_fixture import gmat_fixture_path
from ...data.frames.leap_seconds import TaiUtcTable
from ...data.kernels._spice_loader import get_spiceypy
from .axes import Axes
from .coordinate_system import CoordinateSystem
from .gmat_itrf import GmatItrfReduction
from .gmat_time import TimeSystemConverter
from .iau_2006 import iau2000eq_matrix
from .standard_origins import InertialOrigin
from .xys import ErfaXysProvider, XysProvider


[文档] class ICRSAxes(Axes): """国际天球参考系(ICRF)坐标轴。"""
[文档] def rotation_matrix(self, et: float) -> npt.NDArray[np.floating]: return np.eye(3)
[文档] def rotation_and_rate( self, et: float ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]: return np.eye(3), np.zeros((3, 3))
[文档] class IAU2000EqAxes(Axes): """基于简化 IAU 2000/2006 岁差章动模型的近似惯性轴。""" _DEFAULT_TIME_STEP = 1.0 def __init__(self, time_step: float = _DEFAULT_TIME_STEP) -> None: self._time_step = time_step
[文档] def rotation_matrix(self, et: float) -> npt.NDArray[np.floating]: return iau2000eq_matrix(et)
[文档] class ITRFSpiceAxes(Axes): """SPICE-backed 高精度 ITRF 坐标轴,默认使用 ``ITRF93``。""" _DEFAULT_FRAME = "ITRF93" def __init__(self, frame: str = _DEFAULT_FRAME) -> None: self._frame = frame @property def frame(self) -> str: return self._frame
[文档] def rotation_matrix(self, et: float) -> npt.NDArray[np.floating]: try: return np.array(get_spiceypy().pxform(self._frame, "J2000", et)) except Exception as exc: # pragma: no cover raise CoordinateDataError( "SPICE ITRF transform unavailable. Load an LSK, text PCK, and Earth binary PCK " f"that define {self._frame}; no fallback to IAU_EARTH is performed." ) from exc
[文档] def rotation_and_rate( self, et: float ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]: try: transform = np.array(get_spiceypy().sxform(self._frame, "J2000", et)) except Exception as exc: # pragma: no cover raise CoordinateDataError( "SPICE ITRF state transform unavailable. Load an LSK, text PCK, and Earth binary " f"PCK that define {self._frame}; no fallback to IAU_EARTH is performed." ) from exc return transform[:3, :3], transform[3:, :3]
[文档] class ITRFAxes(ITRFSpiceAxes): """向后兼容别名:默认精确 ITRF 为 SPICE-backed ``ITRF93``。"""
[文档] class GMATITRFAxes(Axes): """显式 opt-in 的第一阶段 GMAT-compatible ITRF 坐标轴。""" def __init__( self, *, tai_utc_path: str | Path | None = None, eop_path: str | Path | None = None, xys_provider: XysProvider | None = None, eop_extrapolation: str = "raise", ) -> None: # EOP 越界策略显式由调用方选择:"raise"(越界报错)或 "clamp"(夹取)。 # 不再由兼容性开关隐式改写(#352:静默切换改显式)。 if eop_extrapolation not in {"raise", "clamp"}: raise ValueError("eop_extrapolation must be 'raise' or 'clamp'") tai_path = ( Path(tai_utc_path) if tai_utc_path is not None else gmat_fixture_path("tai-utc.dat") ) eop_fixture_path = ( Path(eop_path) if eop_path is not None else gmat_fixture_path("eopc04_08.62-now.trimmed") ) self._reduction = GmatItrfReduction( TimeSystemConverter(TaiUtcTable.from_file(tai_path)), EopFile.from_file(eop_fixture_path), xys_provider or ErfaXysProvider(), eop_extrapolation=eop_extrapolation, )
[文档] def rotation_matrix(self, et: float) -> npt.NDArray[np.floating]: rotation, _rate = self.rotation_and_rate(et) return rotation
[文档] def rotation_and_rate( self, et: float ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]: return self._reduction.rotation_and_rate(et)
[文档] class ITRFApproxAxes(Axes): """低精度/教学用途的近似 ITRF 坐标轴。""" _DEFAULT_TIME_STEP = 1.0 def __init__(self, time_step: float = _DEFAULT_TIME_STEP) -> None: self._iau2000eq = IAU2000EqAxes(time_step=time_step) self._time_step = time_step
[文档] def rotation_matrix(self, et: float) -> npt.NDArray[np.floating]: precession = self._iau2000eq.rotation_matrix(et) gast = _greenwich_apparent_sidereal_time(et) return precession @ _rotation3(gast)
[文档] def standard_itrf() -> ITRFSpiceAxes: """返回公共默认 ITRF:SPICE-backed ``ITRF93``。""" return ITRFSpiceAxes()
[文档] def standard_icrf() -> CoordinateSystem: """返回 ICRF 标准坐标系预设。 ICRF = ICRSAxes(恒等旋转,与 ICRF/J2000 同向)+ InertialOrigin(太阳系 质心,无平移)。这是地心 ITRF 转换的惯性别,常作为 ``CoordinateSystem`` 组合的一端。 与 ``standard_itrf`` 不同,这里直接返回完整 ``CoordinateSystem``—— ICRF 预设在调用方通常直接用作转换源/目标,不需再自行拼 Axes + Origin。 """ return CoordinateSystem(axes=ICRSAxes(), origin=InertialOrigin())
def _greenwich_apparent_sidereal_time(et: float) -> float: from .iau_2006 import _seconds_to_julian_centuries, nutation_angles t = _seconds_to_julian_centuries(et) days = et / SECONDS_PER_DAY gmst_deg = 280.46061837 + 360.98564736629 * days + 0.000387933 * t**2 - t**3 / 38710000.0 gmst = np.deg2rad(gmst_deg % 360.0) dpsi, _deps, eps0 = nutation_angles(t) return gmst + dpsi * np.cos(eps0) def _rotation3(angle: float) -> npt.NDArray[np.floating]: cos_angle = np.cos(angle) sin_angle = np.sin(angle) return np.array([[cos_angle, -sin_angle, 0.0], [sin_angle, cos_angle, 0.0], [0.0, 0.0, 1.0]])