e2m2e.algorithm.coordinate.rho_bridge 源代码

"""rho 无量纲坐标 ↔ ECI(J2000, km)坐标桥接。

将 qiao ``rho_to_eci`` / ``eci_to_rho`` 的功能用 e2m2e 的 ``EphemerisSystem``
+ ``SynodicAxes`` 重写。rho 坐标系以选定平动点为原点,使用 CR3BP 归一化单位,
轴向与瞬时 EMR 会合系对齐。

数学关系::

    r_ECI = C @ rho_km + r_LP
    v_ECI = C @ rhodot_km + Cdot @ rho_km + C @ v_LP

其中 C 是 EMR→J2000 旋转矩阵(与 qiao ``Calc_MoonParam`` 约定一致),
r_LP/v_LP 是平动点在 J2000 中的位置/速度。
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol, runtime_checkable

import numpy as np
import numpy.typing as npt

from ...data.constants import SECONDS_PER_DAY
from ...data.templates.enums import LibrationPoint
from .synodic_axes import SynodicAxes

if TYPE_CHECKING:
    from .ephemeris_system import EphemerisSystem


[文档] @runtime_checkable class RhoContext(Protocol): """rho↔ECI 桥接所需的最小上下文契约。 core 层不应认识 algorithms 的类型;本 Protocol 描述 rho_bridge 实际用到的 几个属性(归一化参数、平动点选择),让 :class:`NormalFormContext` 等上层 类型按结构匹配,消除 core → algorithms 的反向依赖。 """ LU: float TU: float jd0: float gamma: float libration_point: LibrationPoint
[文档] def compute_emr_rotation( et: float, system: EphemerisSystem ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]: """从 SPICE 构造 EMR 会合系旋转矩阵 C(t) 及导数 Cdot(t)。 约定与 qiao ``Calc_MoonParam`` 一致:C 满足 ``r_J2000 = C @ r_EMR`` (EMR → J2000),其列向量在 J2000 中给出 EMR 基向量。 ``SynodicAxes.rotation_matrix(et)`` 返回相同的矩阵。 Args: et: 历书时(秒)。 system: 星历系统,提供 SPICE 访问。 Returns: ``(C, Cdot)``,各为 ``(3, 3)`` 数组。C 从 EMR→J2000,Cdot 为其时间导数。 """ syn_axes = SynodicAxes(system.spice) return syn_axes.rotation_and_rate(et)
def _jd_to_et(jd: float, system: EphemerisSystem) -> float: """儒略日 → SPICE 历书时(秒)。 输入 jd 为 TDB 儒略日(与 qiao JD0 约定一致),使用 JDTDB 格式 避免 UTC/TDB 的 ~64 秒偏差。 """ return system.spice.utc_to_et(f"{jd:.20f} JDTDB")
[文档] def tu_to_et(t_nd: float, context: RhoContext, system: EphemerisSystem) -> float: """归一化时间 TU → SPICE 历书时(秒)。 与 :meth:`NormalFormContext.tu_to_seconds` 命名族对称:后者停在 SI 秒, 本函数进一步经 SPICE ``str2et("... JDTDB")`` 转到历书时,供 ``ForceModel`` 等 ET 驱动的力模型使用。 Args: t_nd: 归一化时间(TU)。 context: 标准形上下文,提供 ``jd0`` 与 ``TU``。 system: 星历系统,提供 SPICE 访问。 """ jd = context.jd0 + t_nd * context.TU / SECONDS_PER_DAY return _jd_to_et(jd, system)
def _compute_lp_state_j2000( et: float, context: RhoContext, system: EphemerisSystem, ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]: """计算平动点在 J2000 中的位置和速度。 对共线点 (L1/L2/L3):``r_LP = f_gamma * R_EM``,``v_LP = f_gamma * V_EM``。 对三角点 (L4/L5):通过旋转矩阵变换。 Args: et: 历书时(秒)。 context: 标准形上下文,提供平动点选择与归一化参数。 system: 星历系统。 Returns: ``(r_LP, v_LP)`` 各为 ``(3,)`` 数组,J2000 下 km / km/s。 """ moon_state = system.get_body_state("MOON", et) R_EM = moon_state[:3] V_EM = moon_state[3:] point = context.libration_point if point is LibrationPoint.L1: f = 1.0 - context.gamma return f * R_EM, f * V_EM if point is LibrationPoint.L2: f = 1.0 + context.gamma return f * R_EM, f * V_EM if point is LibrationPoint.L3: f = -context.gamma return f * R_EM, f * V_EM # L4 / L5:需要 C 矩阵做旋转(C 从 EMR→J2000) C, _Cdot = compute_emr_rotation(et, system) ang = -np.pi / 3.0 if point is LibrationPoint.L4 else np.pi / 3.0 R_mat = np.array( [ [np.cos(ang), np.sin(ang), 0.0], [-np.sin(ang), np.cos(ang), 0.0], [0.0, 0.0, 1.0], ] ) M = C @ R_mat @ C.T return M @ R_EM, M @ V_EM
[文档] def rho_to_eci( rho_nd: npt.ArrayLike, rhodot_nd: npt.ArrayLike, t_nd: float, context: RhoContext, system: EphemerisSystem, ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]: """rho 无量纲坐标 → ECI(J2000, km, km/s)。 Args: rho_nd: 无量纲位置 ``(3,)``,以平动点为原点。 rhodot_nd: 无量纲速度 ``(3,)``。 t_nd: 无量纲时间(TU)。 context: 标准形上下文,提供 LU、TU、平动点选择等。 system: 星历系统。 Returns: ``(r_eci, v_eci)`` 各为 ``(3,)`` 数组,km 和 km/s。 """ rho_nd = np.asarray(rho_nd, dtype=float) rhodot_nd = np.asarray(rhodot_nd, dtype=float) LU = context.LU TU = context.TU jd = context.jd0 + t_nd * TU / SECONDS_PER_DAY et = _jd_to_et(jd, system) C, Cdot = compute_emr_rotation(et, system) r_LP, v_LP = _compute_lp_state_j2000(et, context, system) rho_km = rho_nd * LU rhodot_km = rhodot_nd * LU / TU # EMR → J2000(C 从 EMR→J2000)。v_LP 已是 J2000 速度,不再旋转。 r_eci = C @ rho_km + r_LP v_eci = C @ rhodot_km + Cdot @ rho_km + v_LP return r_eci, v_eci
[文档] def eci_to_rho( r_eci: npt.ArrayLike, v_eci: npt.ArrayLike, t_nd: float, context: RhoContext, system: EphemerisSystem, ) -> tuple[npt.NDArray[np.floating], npt.NDArray[np.floating]]: """ECI(J2000, km, km/s)→ rho 无量纲坐标。 Args: r_eci: J2000 位置 ``(3,)``,km。 v_eci: J2000 速度 ``(3,)``,km/s。 t_nd: 无量纲时间(TU)。 context: 标准形上下文。 system: 星历系统。 Returns: ``(rho_nd, rhodot_nd)`` 各为 ``(3,)`` 无量纲数组。 """ r_eci = np.asarray(r_eci, dtype=float) v_eci = np.asarray(v_eci, dtype=float) LU = context.LU TU = context.TU jd = context.jd0 + t_nd * TU / SECONDS_PER_DAY et = _jd_to_et(jd, system) C, Cdot = compute_emr_rotation(et, system) r_LP, v_LP = _compute_lp_state_j2000(et, context, system) # J2000 → EMR(C.T 从 J2000→EMR)。v_LP 已是 J2000 速度,逆变换时需转回 EMR。 # rho_km = C.T @ (r_eci - r_LP) # rhodot_km = C.T @ v_eci - C.T @ Cdot @ rho_km - C.T @ v_LP rho_km = C.T @ (r_eci - r_LP) rho_nd = rho_km / LU rhodot_km = C.T @ v_eci - C.T @ Cdot @ rho_km - C.T @ v_LP rhodot_nd = rhodot_km * TU / LU return rho_nd, rhodot_nd