Join the Discussion

Feel free to share ideas and ask questions over at AstroForge’s discussion page.

Welcome to AstroForge’s documentation!#

AstroForge is a Python library of astrodynamics tools. It is meant to be used for satellite state propagation and the computation of observations from ground-based or space-based sensors. Precise orbit fitting requires pristine knowledge of the observer’s site location in inertial coordinates, which in turn requires methodical handling of coordinate systems and the conversions between them. As such, much of this library is devoted to coordinate conversions.

Example Usage#

Let’s start with a simple example of propagating a satellite in Low Earth Orbit (LEO) and computing the angles-only observations of the satellite from a ground-based sensor.

Defining the Orbit#

AstroForge has some constants defined for us, such as the equatorial radius (R_earth) and gravitational parameter (GM) of the Earth. We’ll use those to define the position and velocity vectors for a satellite in a circular equatorial orbit with an altitude of 400 km.

Setup an orbital state to propagate#
import numpy as np
import astroforge as af

alt = 400.0
r = alt + af.R_earth
v = np.sqrt(af.GM / r)

x = np.array([r, 0.0, 0.0])
v = np.array([0.0, v, 0.0])

with np.printoptions(suppress=True):
    print(f"{x = }")
    print(f"{v = }")
x = array([6778.137,    0.   ,    0.   ])
v = array([0.        , 7.66855818, 0.        ])

Propagating the Orbit#

We can propagate that orbital state using a medium-fidelity propagator within AstroForge. Alternatively, you can write your own force model and provide that as the first argument to astroforge.propagators.propagator.

Propagate using a medium-fidelity force model#
force_model = af.force_models.F_mp
propagator = af.propagators.propagator

x0 = np.hstack([x, v])
t = np.array([51720.0, 51721.0]) * 86400.0

states = propagator(force_model, x0, t)
assert (states[0] == x0).all()

xf, vf = states[1][:3], states[1][3:]
with np.printoptions(suppress=True):
    print(f"{xf = }")
    print(f"{vf = }")
xf = array([-5406.18389055, -4058.16278305,     0.00903747])
vf = array([ 4.61189691, -6.1527153 , -0.00000629])

The output of the propagator is the satellite state at the times given, so the first element of the states vector should be exactly equal to the input.

Note

The astroforge.force_models.F_mp() function needs the absolute time (MJD) in order to compute the position of the Sun and Moon, but the numerical integration uses SI units. So, the time input here is the MJD converted to seconds.

Compute Observations#

Finally, let’s compute observations from a ground-based sensor. In this case we’ll model the sensor as an optical telescope, which reports inertial angles-only measurements of the satellite— right ascension (\(\alpha\)), declination (\(\delta\)), and their rates of change (\(\dot{\alpha}, \dot{\delta}\)). This article has more information, if you are interested.

Compute angles-only observations from ground-based sensor#
# make up a site location
lat, lon, alt = 42.459629, -71.267319, 0.0

# rotate site location into inertial coordinates
x_site_itrs = af.coordinates.LatLonAltToITRS(lat, lon, alt)
v_site_itrs = np.zeros(3)
x_site, v_site = af.coordinates.PosVelConversion(
    af.coordinates.ITRSToTETED,
    t[1] / 86400.0,
    x_site_itrs,
    v_site_itrs,
)

angles, _, _ = af.coordinates.PosVelToFPState(xf, vf, x_site, v_site)
ra = angles[0, 0] * 180 / np.pi
dec = angles[0, 1] * 180 / np.pi
rates = angles[0, 2:4] * 180 * 3600 / np.pi

print(f"Angles (ra, dec) = ({ra:.6f}, {dec:.6f}) deg")
print(f"Rates (ra-rate, dec-rate) = ({rates[0]:.3f}, {rates[1]:.3f}) arcsec/sec")
Angles (ra, dec) = (-115.952570, -60.208808) deg
Rates (ra-rate, dec-rate) = (553.427, 119.284) arcsec/sec

Documentation Contents#