Catch that asteroid!


In [1]:
import matplotlib.pyplot as plt
plt.ion()

from astropy import units as u
from astropy.time import Time

In [2]:
from astropy.utils.data import conf
conf.dataurl


Out[2]:
'http://data.astropy.org/'

In [3]:
conf.remote_timeout


Out[3]:
10.0

First, we need to increase the timeout time to allow the download of data occur properly


In [4]:
conf.remote_timeout = 10000

In [5]:
from astropy.coordinates import solar_system_ephemeris
solar_system_ephemeris.set("jpl")

from poliastro.bodies import *
from poliastro.twobody import Orbit
from poliastro.plotting import OrbitPlotter, plot

EPOCH = Time("2017-09-01 12:05:50", scale="tdb")

In [6]:
earth = Orbit.from_body_ephem(Earth, EPOCH)
earth


Out[6]:
1 x 1 AU x 23.4 deg orbit around Sun (☉)

In [7]:
plot(earth, label=Earth)


Out[7]:
[<matplotlib.lines.Line2D at 0x7f1867880470>,
 <matplotlib.lines.Line2D at 0x7f186a591710>]

In [8]:
from poliastro.neos import neows

In [9]:
florence = neows.orbit_from_name("Florence")
florence


Out[9]:
1 x 3 AU x 22.1 deg orbit around Sun (☉)

Two problems: the epoch is not the one we desire, and the inclination is with respect to the ecliptic!


In [10]:
florence.epoch


Out[10]:
<Time object: scale='tdb' format='jd' value=2458200.5>

In [11]:
florence.epoch.iso


Out[11]:
'2018-03-23 00:00:00.000'

In [12]:
florence.inc


Out[12]:
$22.144812 \; \mathrm{{}^{\circ}}$

We first propagate:


In [13]:
florence = florence.propagate(EPOCH)
florence.epoch.tdb.iso


Out[13]:
'2017-09-01 12:05:50.000'

And now we have to convert to another reference frame, using http://docs.astropy.org/en/stable/coordinates/.


In [14]:
from astropy.coordinates import (
    ICRS, GCRS,
    CartesianRepresentation, CartesianDifferential
)
from poliastro.frames import HeliocentricEclipticJ2000

The NASA servers give the orbital elements of the asteroids in an Heliocentric Ecliptic frame. Fortunately, it is already defined in Astropy:


In [15]:
florence_heclip = HeliocentricEclipticJ2000(
    x=florence.r[0], y=florence.r[1], z=florence.r[2],
    v_x=florence.v[0], v_y=florence.v[1], v_z=florence.v[2],
    representation=CartesianRepresentation,
    differential_type=CartesianDifferential,
    obstime=EPOCH
)
florence_heclip


Out[15]:
<HeliocentricEclipticJ2000 Coordinate (obstime=2017-09-01 12:05): (x, y, z) in km
    (1.45898575e+08, -58567565.21690369, 2279107.68675573)
 (v_x, v_y, v_z) in km / s
    (7.40829055, 31.1115145, 12.7966945)>

Now we just have to convert to ICRS, which is the "standard" reference in which poliastro works:


In [16]:
florence_icrs_trans = florence_heclip.transform_to(ICRS)
florence_icrs_trans.representation = CartesianRepresentation
florence_icrs_trans


Out[16]:
<ICRS Coordinate: (x, y, z) in km
    (1.46265478e+08, -53881737.1203532, -20898600.3888003)
 (v_x, v_y, v_z) in km / s
    (7.39988214, 23.46299458, 24.12028277)>

In [17]:
florence_icrs = Orbit.from_vectors(
    Sun,
    r=[florence_icrs_trans.x, florence_icrs_trans.y, florence_icrs_trans.z] * u.km,
    v=[florence_icrs_trans.v_x, florence_icrs_trans.v_y, florence_icrs_trans.v_z] * (u.km / u.s),
    epoch=florence.epoch
)
florence_icrs


Out[17]:
1 x 3 AU x 44.5 deg orbit around Sun (☉)

In [18]:
florence_icrs.rv()


Out[18]:
(<Quantity [ 1.46265478e+08, -5.38817371e+07, -2.08986004e+07] km>,
 <Quantity [ 7.39988214, 23.46299458, 24.12028277] km / s>)

Let us compute the distance between Florence and the Earth:


In [19]:
from poliastro.util import norm

In [20]:
norm(florence_icrs.r - earth.r) - Earth.R


Out[20]:
$7057830.8 \; \mathrm{km}$
This value is consistent with what ESA says! $7\,060\,160$ km

In [21]:
from IPython.display import HTML

HTML(
"""<blockquote class="twitter-tweet" data-lang="en"><p lang="es" dir="ltr">La <a href="https://twitter.com/esa_es">@esa_es</a> ha preparado un resumen del asteroide <a href="https://twitter.com/hashtag/Florence?src=hash">#Florence</a> 😍 <a href="https://t.co/Sk1lb7Kz0j">pic.twitter.com/Sk1lb7Kz0j</a></p>&mdash; AeroPython (@AeroPython) <a href="https://twitter.com/AeroPython/status/903197147914543105">August 31, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>"""
)


Out[21]:

And now we can plot!


In [22]:
frame = OrbitPlotter()

frame.plot(earth, label="Earth")

frame.plot(Orbit.from_body_ephem(Mars, EPOCH))
frame.plot(Orbit.from_body_ephem(Venus, EPOCH))
frame.plot(Orbit.from_body_ephem(Mercury, EPOCH))

frame.plot(florence_icrs, label="Florence")


Out[22]:
[<matplotlib.lines.Line2D at 0x7f18677f1860>,
 <matplotlib.lines.Line2D at 0x7f18675dd710>]

The difference between doing it well and doing it wrong is clearly visible:


In [23]:
frame = OrbitPlotter()

frame.plot(earth, label="Earth")

frame.plot(florence, label="Florence (Ecliptic)")
frame.plot(florence_icrs, label="Florence (ICRS)")


Out[23]:
[<matplotlib.lines.Line2D at 0x7f18677b8da0>,
 <matplotlib.lines.Line2D at 0x7f18677a3b00>]

And now let's do something more complicated: express our orbit with respect to the Earth! For that, we will use GCRS, with care of setting the correct observation time:


In [24]:
florence_gcrs_trans = florence_heclip.transform_to(GCRS(obstime=EPOCH))
florence_gcrs_trans.representation = CartesianRepresentation
florence_gcrs_trans


Out[24]:
<GCRS Coordinate (obstime=2017-09-01 12:05, obsgeoloc=(0., 0., 0.) m, obsgeovel=(0., 0., 0.) m / s): (x, y, z) in km
    (4960528.72631646, -5020203.94532577, 306195.48128127)
 (v_x, v_y, v_z) in km / s
    (-2.76863626, -1.95773252, 13.09966916)>

In [25]:
florence_hyper = Orbit.from_vectors(
    Earth,
    r=[florence_gcrs_trans.x, florence_gcrs_trans.y, florence_gcrs_trans.z] * u.km,
    v=[florence_gcrs_trans.v_x, florence_gcrs_trans.v_y, florence_gcrs_trans.v_z] * (u.km / u.s),
    epoch=EPOCH
)
florence_hyper


Out[25]:
7064205 x -7068561 km x 104.3 deg orbit around Earth (♁)

Notice that the ephemerides of the Moon is also given in ICRS, and therefore yields a weird hyperbolic orbit!


In [26]:
moon = Orbit.from_body_ephem(Moon, EPOCH)
moon


Out[26]:
151218466 x -151219347 km x 23.3 deg orbit around Earth (♁)

In [27]:
moon.a


Out[27]:
$-440.42131 \; \mathrm{km}$

In [28]:
moon.ecc


Out[28]:
$343350.57 \; \mathrm{}$

So we have to convert again.


In [29]:
moon_icrs = ICRS(
    x=moon.r[0], y=moon.r[1], z=moon.r[2],
    v_x=moon.v[0], v_y=moon.v[1], v_z=moon.v[2],
    representation=CartesianRepresentation,
    differential_type=CartesianDifferential
)
moon_icrs


Out[29]:
<ICRS Coordinate: (x, y, z) in km
    (1.41399531e+08, -49228391.42507221, -21337616.62766309)
 (v_x, v_y, v_z) in km / s
    (11.10890252, 25.6785744, 11.0567569)>

In [30]:
moon_gcrs = moon_icrs.transform_to(GCRS(obstime=EPOCH))
moon_gcrs.representation = CartesianRepresentation
moon_gcrs


Out[30]:
<GCRS Coordinate (obstime=2017-09-01 12:05, obsgeoloc=(0., 0., 0.) m, obsgeovel=(0., 0., 0.) m / s): (x, y, z) in km
    (94189.90120828, -367278.24304992, -133087.21297573)
 (v_x, v_y, v_z) in km / s
    (0.94073662, 0.25786326, 0.03569047)>

In [31]:
moon = Orbit.from_vectors(
    Earth,
    [moon_gcrs.x, moon_gcrs.y, moon_gcrs.z] * u.km,
    [moon_gcrs.v_x, moon_gcrs.v_y, moon_gcrs.v_z] * (u.km / u.s),
    epoch=EPOCH
)
moon


Out[31]:
367937 x 405209 km x 19.4 deg orbit around Earth (♁)

And finally, we plot the Moon:


In [32]:
plot(moon, label=Moon)
plt.gcf().autofmt_xdate()


And now for the final plot:


In [33]:
frame = OrbitPlotter()

# This first plot sets the frame
frame.plot(florence_hyper, label="Florence")

# And then we add the Moon
frame.plot(moon, label=Moon)

plt.xlim(-1000000, 8000000)
plt.ylim(-5000000, 5000000)

plt.gcf().autofmt_xdate()


Per Python ad astra!