Source code for ephemeris_tools.constants

"""Fixed constants used across ephemeris, viewer, and tracker.

From FORTRAN tools.inc. Categories include: body IDs (NAIF), spacecraft
names/codes and planet number mapping; time constants (seconds per minute/hour/
day, noon offset); angle/unit constants (degrees per circle, DMS/arcmin/arcsec,
RA degrees per hour); and configuration constants (default interval, min
interval, max FOV, alignment, string lengths).
"""

# Body IDs (NAIF)
SUN_ID = 10
EARTH_ID = 399
MOON_ID = 301

# Time: seconds per unit (for interval conversion and sexagesimal)
SECONDS_PER_MINUTE = 60.0
SECONDS_PER_HOUR = 3600.0
SECONDS_PER_DAY = 86400.0
NOON_SECONDS_OFFSET = 12.0 * 3600.0  # seconds from midnight to noon (reference epochs)

# Angle: degrees per circle and sexagesimal (DMS/arcmin/arcsec)
DEGREES_PER_CIRCLE = 360.0
HALF_CIRCLE_DEGREES = 180.0
ARCMIN_PER_DEGREE = 60.0
ARCSEC_PER_DEGREE = 3600.0
DEGREES_PER_HOUR_RA = 15.0  # right ascension: 360° / 24 h

# Defaults and thresholds (configuration)
DEFAULT_INTERVAL = 1.0  # default time step when env invalid or not set
DEFAULT_MIN_INTERVAL_SECONDS = 1.0  # minimum interval for interval_seconds()
MAX_FOV_DEGREES = 90.0  # FORTRAN viewer clamps FOV to prevent projection singularities
DEFAULT_ALIGN_LOC_POINTS = 108.0  # caption alignment from left edge (PostScript points)

# String length constants (for compatibility with FORTRAN column widths)
EPHEM_ID_LEN = 4
MOON_ID_LEN = 4
SC_ID_LEN = 4
COL_ID_LEN = 8
FOV_LEN = 12

# Spacecraft: full name, abbreviation, NAIF code (from tools.inc)
SPACECRAFT_NAMES = (
    'Voyager 1',
    'Voyager 2',
    'Galileo',
    'Cassini',
    'New Horizons',
    'Juno',
    'Europa Clipper',
    'JUICE',
    'JWST',
    'HST',
)
SPACECRAFT_IDS = (
    'VG1',
    'VG2',
    'GLL',
    'CAS',
    'NH',
    'JNO',
    'EC',
    'JCE',
    'JWST',
    'HST',
)
SPACECRAFT_CODES = (-31, -32, -77, -82, -98, -61, -159, -28, -170, -48)

NSPACECRAFTS = 10

# Planet number (4=Mars .. 9=Pluto) -> NAIF planet ID
PLANET_NUM_TO_ID: dict[int, int] = {
    4: 499,  # Mars
    5: 599,  # Jupiter
    6: 699,  # Saturn
    7: 799,  # Uranus
    8: 899,  # Neptune
    9: 999,  # Pluto
}

# NAIF planet ID -> planet number
PLANET_ID_TO_NUM: dict[int, int] = {v: k for k, v in PLANET_NUM_TO_ID.items()}

# Planet number → ephemeris kernel description (right-hand side of "Ephemeris:" in plot).
# FORTRAN/web use "NNN DESCRIPTION"; we show DESCRIPTION.
# Matches web/tools/EPHEMERIS_INFO.shtml.
EPHEM_DESCRIPTIONS_BY_PLANET: dict[int, str] = {
    4: 'MAR097 + DE440',
    5: 'JUP365 + DE440',
    6: 'SAT415 + SAT441 + DE440',
    7: 'URA111 + URA115 + DE440',
    8: 'NEP095 + NEP097 + NEP101 + DE440',
    9: 'PLU058 + DE440',
}

# Planet number → display name (for column descriptions).
PLANET_NUM_TO_NAME: dict[int, str] = {
    4: 'Mars',
    5: 'Jupiter',
    6: 'Saturn',
    7: 'Uranus',
    8: 'Neptune',
    9: 'Pluto',
}

# General column ID → display template; use {planet} for planet-specific columns.
COL_DISPLAY_TEMPLATES: dict[int, str] = {
    1: 'Modified Julian Date',
    2: 'Year, Month, Day, Hour, Minute',
    3: 'Year, Month, Day, Hour, Minute, Second',
    4: 'Year, DOY, Hour, Minute',
    5: 'Year, DOY, Hour, Minute, Second',
    6: 'Observer-{planet} distance',
    7: 'Sun-{planet} distance',
    8: '{planet} phase angle',
    9: 'Ring plane opening angle to observer',
    10: 'Ring plane opening angle to Sun',
    11: 'Sub-observer inertial longitude',
    12: 'Sub-solar inertial longitude',
    13: 'Sub-observer latitude & rotating longitude',
    14: 'Sub-solar latitude & rotating longitude',
    15: '{planet} RA & Dec',
    16: 'Earth RA & Dec',
    17: 'Sun RA & Dec',
    18: '{planet} projected equatorial radius (arcsec)',
    19: '{planet} projected equatorial radius (deg)',
    20: 'Lunar phase angle',
    21: 'Sun-{planet} sky separation angle',
    22: 'Lunar-{planet} sky separation angle',
}

# Moon column ID → display name (ephemeris Input Parameters section).
MCOL_DISPLAY_BY_ID: dict[int, str] = {
    1: 'Observer-moon distance',
    2: 'Moon phase angle',
    3: 'Sub-observer latitude & rotating longitude',
    4: 'Sub-solar latitude & rotating longitude',
    5: 'RA & Dec',
    6: 'Offset RA & Dec from the moon (arcsec)',
    7: 'Offset RA & Dec from the moon (deg)',
    8: 'Orbital longitude relative to observer',
    9: 'Orbit plane opening angle to observer',
}


[docs] def spacecraft_code_to_id(sc_code: int) -> str | None: """Return spacecraft abbreviation for NAIF code (port of RSPK_GetSCID inverse). Parameters: sc_code: NAIF spacecraft ID (e.g. -82 for Cassini). Returns: Abbreviation (e.g. 'CAS') or None if unknown. """ for i, code in enumerate(SPACECRAFT_CODES): if code == sc_code: return SPACECRAFT_IDS[i] return None
[docs] def spacecraft_name_to_code(name: str) -> int | None: """Return NAIF ID for spacecraft by name or abbreviation (port of RSPK_GetSCID). Parameters: name: Full name or ID (e.g. 'VG1', 'Voyager 1', 'CASSINI'). Returns: NAIF integer ID or None if not found. """ name_upper = name.strip().upper() for i, abbr in enumerate(SPACECRAFT_IDS): if abbr == name_upper: return SPACECRAFT_CODES[i] for i, full_name in enumerate(SPACECRAFT_NAMES): if full_name.upper() == name_upper: return SPACECRAFT_CODES[i] return None