Source code for missiontools.orbit.shadow

"""Cylindrical shadow model for eclipse detection."""

from __future__ import annotations

import numpy as np
import numpy.typing as npt

from .constants import EARTH_SEMI_MAJOR_AXIS
from .frames import sun_vec_eci


[docs] def in_sunlight( r_eci: npt.ArrayLike, t: np.datetime64 | npt.NDArray[np.datetime64], body_radius: float = EARTH_SEMI_MAJOR_AXIS, ) -> np.bool_ | npt.NDArray[np.bool_]: """Determine whether a spacecraft is in sunlight using a cylindrical shadow model. Parameters ---------- r_eci : array_like, shape (3,) or (N, 3) Spacecraft position(s) in the ECI frame (m). t : datetime64 or array of datetime64 Epoch(s) for computing the Sun direction. body_radius : float, optional Central body equatorial radius (m). Defaults to Earth WGS84. Returns ------- np.bool_ or ndarray of bool ``True`` where the spacecraft is in sunlight. Scalar input gives a scalar result; array input gives an ``(N,)`` array. """ r = np.asarray(r_eci, dtype=np.float64) scalar = r.ndim == 1 r_2d = np.atleast_2d(r) # (N, 3) s = sun_vec_eci(t) # (3,) or (N, 3) s_2d = np.atleast_2d(s) # (N, 3) # Projection of r onto the sun direction proj = np.einsum('ij,ij->i', r_2d, s_2d) # (N,) # Perpendicular distance from the Earth–Sun line perp = r_2d - proj[:, np.newaxis] * s_2d # (N, 3) perp_dist = np.linalg.norm(perp, axis=1) # (N,) # In shadow when behind Earth (proj < 0) AND within the shadow cylinder lit = ~((proj < 0) & (perp_dist < body_radius)) # (N,) return lit[0] if scalar else lit