Using get_body for Celestial Bodies

The get_body() and get_body_pv() methods provide a flexible way to query any supported solar system body’s position relative to your observer (satellite, spacecraft, or ground station).

Setup

Before using get_body(), ensure the planetary ephemeris is loaded:

import rust_ephem

# Load planetary ephemeris (downloads DE440S if needed)
rust_ephem.ensure_planetary_ephemeris()

Basic Usage

import datetime as dt
import rust_ephem as re

# Create any ephemeris type
tle1 = "1 25544U 98067A   20344.91777778  .00002182  00000-0  46906-4 0  9991"
tle2 = "2 25544  51.6460  44.6055 0002398  79.4451  23.5248 15.49364984256518"
begin = dt.datetime(2024, 1, 1, 0, 0, 0, tzinfo=dt.timezone.utc)
end = dt.datetime(2024, 1, 1, 1, 0, 0, tzinfo=dt.timezone.utc)

ephem = re.TLEEphemeris(tle1, tle2, begin, end, step_size=60)

# Get body position/velocity data
sun_pv = ephem.get_body_pv("Sun")
moon_pv = ephem.get_body_pv("Moon")
mars_pv = ephem.get_body_pv("Mars")

print(f"Sun distance: {np.linalg.norm(sun_pv.position[0]):.0f} km")
print(f"Moon distance: {np.linalg.norm(moon_pv.position[0]):.0f} km")
print(f"Mars distance: {np.linalg.norm(mars_pv.position[0]):.0f} km")

# Get body as astropy SkyCoord (includes observer location)
sun_sc = ephem.get_body("Sun")
print(f"Sun RA/Dec: {sun_sc[0].ra}, {sun_sc[0].dec}")

Body Identifiers

Bodies can be specified by name or NAIF ID:

# By name (case-insensitive)
ephem.get_body("Sun")
ephem.get_body("moon")
ephem.get_body("Mars")
ephem.get_body("Jupiter barycenter")

# By NAIF ID as string
ephem.get_body("10")    # Sun
ephem.get_body("301")   # Moon
ephem.get_body("499")   # Mars (requires full DE440 kernel)
ephem.get_body("5")     # Jupiter barycenter

Note: The default DE440S kernel includes:

  • Sun (10), Moon (301), Earth (399)

  • Planetary barycenters (1-9): Mercury, Venus, Earth-Moon, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto

For planet center IDs (like 499 Mars, 599 Jupiter), use the full DE440 kernel.

Ground Observatory Example

# Create ground observatory
obs = re.GroundEphemeris(
    latitude=19.8207,    # Mauna Kea
    longitude=-155.468,
    height=4205.0,
    begin=begin,
    end=end,
    step_size=60
)

# Get bodies from observatory perspective
moon = obs.get_body("Moon")
jupiter = obs.get_body("Jupiter barycenter")

# The SkyCoord includes observer location for proper parallax
print(f"Observer location set: {moon.frame.obsgeoloc[0]}")

Calculating Separations

from astropy.coordinates import SkyCoord
import astropy.units as u

# Get positions
moon = ephem.get_body("Moon")
mars = ephem.get_body("Mars")

# Calculate angular separation
separation = moon.separation(mars)
print(f"Moon-Mars separation: {separation[0].to(u.deg):.2f}")

# Define a target and check separation from Sun
target = SkyCoord(ra=83.63*u.deg, dec=22.01*u.deg, frame='icrs')
sun = ephem.get_body("Sun")
sun_sep = target.separation(sun)
print(f"Target-Sun separation: {sun_sep[0].to(u.deg):.2f}")

JPL Horizons Fallback

For solar system bodies not in your SPICE kernel (asteroids, comets, spacecraft), enable JPL Horizons with use_horizons=True:

# Query asteroid Ceres (not in default DE440S kernel)
ceres = ephem.get_body("1", use_horizons=True)
print(f"Ceres RA/Dec: {ceres[0].ra}, {ceres[0].dec}")

# Or by name
apophis = ephem.get_body("Apophis", use_horizons=True)

# Get position/velocity data
ceres_pv = ephem.get_body_pv("1", use_horizons=True)
print(f"Distance: {np.linalg.norm(ceres_pv.position[0]):.0f} km")

Horizons is automatically queried only when:

  1. The body is not found in SPICE kernels

  2. use_horizons=True is explicitly set

  3. An internet connection is available

This provides the best of both worlds: fast SPICE lookups when available, with automatic fallback to Horizons for broader body coverage. For a comprehensive guide to Horizons usage including asteroid tracking, constraint integration, and troubleshooting, see JPL Horizons Integration.