astra.utils.ephemeris#

Solar position analysis and celestial body coordinate lookup.

Also provides pre-computed ephemerides for non-sidereal (moving) targets, sourced either from astropy’s built-in solar system ephemeris or from JPL Horizons (including TLE-defined satellites).

Functions

astrometric_icrs(body, obstime, obs_location)

Return the astrometric ICRS direction of a solar system body from the observer.

compute_nonsidereal_rates_from_interp(...[, dt])

Compute ASCOM RightAscensionRate and DeclinationRate from pre-computed interpolators.

get_body_coordinates(body_name, obs_time, ...)

Get the ICRS position of a celestial body (Solar System or Deep Sky).

is_solar_system_body(body_name)

Return True if name is a known solar system body in the astropy ephemeris.

is_sun_rising(obs_location)

Determine solar motion and flat field observation readiness.

precompute_ephemeris(body_name, start_time, ...)

Pre-compute a moving body's sky positions over a time window.

Exceptions

NotMovingBodyError

Raised when a lookup_name cannot be resolved as a solar system or minor body.

exception astra.utils.ephemeris.NotMovingBodyError[source]#

Bases: ValueError

Raised when a lookup_name cannot be resolved as a solar system or minor body.

astra.utils.ephemeris.is_sun_rising(obs_location: EarthLocation) Tuple[bool, bool, AltAz][source]#

Determine solar motion and flat field observation readiness.

Analyzes sun position and movement to determine if conditions are suitable for flat field calibration observations, which require specific twilight conditions.

Parameters:

obs_location (EarthLocation) – Observer’s geographic location.

Returns:

Tuple[bool, bool, AltAz]

Solar status as (rising, flat_ready, position):
  • rising: True if sun is rising, False if setting

  • flat_ready: True if optimal for flats (sun altitude -12° to -1°)

  • position: Current sun position in alt-az coordinates

astra.utils.ephemeris.astrometric_icrs(body: SkyCoord, obstime: Time, obs_location: EarthLocation) SkyCoord[source]#

Return the astrometric ICRS direction of a solar system body from the observer.

get_body returns GCRS coordinates. Those include annual aberration, so they differ from a catalogue (ICRS) direction by up to 20 arcseconds. JPL Horizons and SIMBAD give astrometric ICRS. This function brings the astropy result onto the same footing, so every source Astra uses is in one frame.

The direction is the light-time corrected barycentric position of the body, which astropy provides, minus the barycentric position of the observer. Do not use transform_to(ICRS()) on a get_body result for this. That returns the direction from the solar system barycentre, not from the observer.

Parameters:
  • body – Output of get_body, one or many times.

  • obstime – The time(s) the body was evaluated at.

  • obs_location – The observer location used for get_body.

Returns:

SkyCoord in ICRS with no distance, one entry per input time.

astra.utils.ephemeris.get_body_coordinates(body_name: str, obs_time: Time, obs_location: EarthLocation) SkyCoord[source]#

Get the ICRS position of a celestial body (Solar System or Deep Sky).

Calculates the astrometric ICRS coordinates of a solar system body as seen from the observer, or resolves the coordinates of a deep sky object by name. Both are in the same frame as the schedule’s ra/dec.

This returns a single position and is for targets that are tracked sidereally. Moving targets that need differential tracking – minor bodies and TLE-defined satellites – go through precompute_ephemeris() instead, which returns interpolators over the whole observation window.

Parameters:
  • body_name (str) – Name of the body (e.g., ‘mars’, ‘jupiter’, ‘M31’, ‘Vega’).

  • obs_time (Time) – Observation time (used for solar system bodies).

  • obs_location (EarthLocation) – Observer’s geographic location (used for solar system bodies).

Returns:

SkyCoord – Position of the body in the sky.

astra.utils.ephemeris.is_solar_system_body(body_name: str) bool[source]#

Return True if name is a known solar system body in the astropy ephemeris.

O(1) lookup on a lowercase name against the ephemeris bodies set.

astra.utils.ephemeris.precompute_ephemeris(body_name: str, start_time: Time, duration_hours: float, obs_location: EarthLocation, interval_minutes: float | None = 1.0, tle_data: str | None = None, return_rates: bool = False) tuple[interp1d, interp1d] | tuple[interp1d, interp1d, interp1d, interp1d][source]#

Pre-compute a moving body’s sky positions over a time window.

Samples the body’s position over the whole window and returns cubic interpolation functions keyed on seconds since start_time. Planets, the Moon and the Sun come from vectorised astropy get_body() calls. Minor bodies and TLE-defined satellites come from JPL Horizons, in one query, or two where the interval is chosen automatically and the target moves fast enough to need a finer grid. Reading the interpolators at runtime is much faster than repeated position lookups.

Args:
body_name: Name of the body (e.g. ‘mars’, ‘moon’). Must be present in

astropy’s built-in solar system ephemeris, resolvable by JPL Horizons, or ‘TLE’ if tle_data is provided.

start_time: Start of the observation window. duration_hours: Length of the window in hours. obs_location: Observer EarthLocation. interval_minutes: Ephemeris sampling interval in minutes. Pass None to

choose it from the target’s own sky motion, which a fast target needs: sampled once a minute, the ISS lands 8 degrees from its true position between samples, because cubic interpolation cannot describe an arc it never sampled. The search starts at a few seconds for a TLE and at one minute for everything else, so one query is normally enough, and then checks that against the sampled rates. The interval is held between one second and one minute, and never fine enough to exceed the sample ceiling.

tle_data: Two-line element (TLE) data as a string with two lines separated

by newline. Required when body_name is ‘TLE’. Example format: “1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927n

2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537”

Returns:
If return_rates is False (default):

(ra_interp, dec_interp): Two callables mapping elapsed seconds to degrees. RA is unwrapped (continuous, not modulo 360) to avoid discontinuities at wrap boundaries.

If return_rates is True:

(ra_interp, dec_interp, ra_rate_interp, dec_rate_interp), where ra_rate_interp and dec_rate_interp map elapsed seconds to ASCOM tracking units (RightAscensionRate in seconds of RA per sidereal second and DeclinationRate in arcseconds per SI second).

Raises:
NotMovingBodyError: If body cannot be resolved as a solar system body,

minor body, or TLE.

ValueError: If body_name is ‘TLE’ but tle_data is not provided.

Get the position of Mars as observed from Greenwich at the current time:

from astropy.coordinates import get_body, EarthLocation, solar_system_ephemeris from astropy.time import Time location = EarthLocation.of_site(‘greenwich’) ra_interp, dec_interp = precompute_ephemeris(‘mars’, Time.now(), 4, location)

Get position of ISS using TLE data:

tle = “1 25544U 98067A 23001.00000000 .00016717 00000-0 29641-3 0 9991

2 25544 51.6416 339.8014 0002571 235.7582 1.5976 15.54178122381131”

ra_interp, dec_interp = precompute_ephemeris(‘TLE’, start_time, 4, location, tle_data=tle)

astra.utils.ephemeris.compute_nonsidereal_rates_from_interp(ra_interp: interp1d, dec_interp: interp1d, t_seconds: float, dt: float = 60.0) tuple[float, float][source]#

Compute ASCOM RightAscensionRate and DeclinationRate from pre-computed interpolators.

Uses a finite difference on the interpolated ephemeris so no additional get_body() calls are needed at runtime.

Parameters:
  • ra_interp – RA interpolator (seconds to degrees, unwrapped/continuous).

  • dec_interp – Dec interpolator (seconds to degrees).

  • t_seconds – Elapsed seconds since the ephemeris start_time.

  • dt – Finite-difference step in seconds (default 60).

Returns:

(ra_rate, dec_rate) where – ra_rate - seconds of time per sidereal second (ASCOM RightAscensionRate) dec_rate - arcseconds per SI second (ASCOM DeclinationRate)