!which python; python -V;
from astropy import units as u
import numpy as np
from poliastro.bodies import Earth, Mars, Sun
from poliastro.twobody import Orbit
# Needed to handle TLE into Poliastro's Orbit
from tletools import TLE
import plotly.io as pio
pio.renderers.default = "notebook_connected"
# Needed for defining manouvers
from poliastro.maneuver import Maneuver
from poliastro.twobody import thrust
from poliastro.twobody.propagation import cowell
from poliastro.plotting import OrbitPlotter2D, OrbitPlotter3D
from perylune.orbit_tools import *
/home/thomson/devel/perylune/venv/bin/python Python 3.8.5
# STEP 0: DEFINE TARGET ORBIT
# Let's get the TLE orbital data for the target satellite. As an example, let's pick a dead NOAA-17 sat.
# Its TLE data can be obtained from many places, such as celestrak or n2yo (https://www.n2yo.com/satellite/?s=27453)
tle_text = """NOAA-17
1 27453U 02032A 20263.80942421 -.00000011 +00000-0 +13583-4 0 9998
2 27453 098.5909 208.3215 0011096 327.5463 032.5033 14.25072668948324"""
tle_lines = tle_text.strip().splitlines()
tle_target = TLE.from_lines(*tle_lines)
orb_target = tle_target.to_orbit()
# STEP 1: LAUNCH
# Let's use ANDESITE sat as an example. It was launched as shareride by RocketLab's Electron on
# the latest rideshare launch on 2020 June 13. NORAD ID is 45726.
# TLE elements taken from n2yo website, details https://www.n2yo.com/satellite/?s=45726
tle_text = """GDASAT-1
1 45726U 20037D 20278.45278018 .00000608 00000-0 65390-4 0 9991
2 45726 97.7132 96.2906 0012962 283.6573 76.3213 14.92011802 14275"""
tle_lines = tle_text.strip().splitlines()
tle1 = TLE.from_lines(*tle_lines)
orb1 = tle1.to_orbit()
# Let's get the details about initial orbit
print_orb(orb1)
6961 x 6979 km x 97.7 deg (GCRS) orbit around Earth (♁) at epoch 2020-10-04T10:52:00.207552000 (UTC) a(𝑎)=6969.80km, b=6969.79km, e=0.00, i=97.71deg raan(Ω)=96.29deg argp(𝜔)=283.66deg nu(𝜈)=76.47deg period=5790.84s perapis=6961km(582.63km) apoapsis=6979km(600.70km)
# Let's take a look at the target orbit
print_orb(orb_target)
7178 x 7194 km x 98.6 deg (GCRS) orbit around Earth (♁) at epoch 2020-09-19T19:25:34.251744000 (UTC) a(𝑎)=7186.39km, b=7186.38km, e=0.00, i=98.59deg raan(Ω)=208.32deg argp(𝜔)=327.55deg nu(𝜈)=32.57deg period=6062.85s perapis=7178km(800.27km) apoapsis=7194km(816.22km)
# Let's visualize the orbits
# Obtain the orbit after applying Hohmann transfer.
plot = OrbitPlotter3D(dark=False)
#plot._layout['autosize'] = False
#print(plot._layout['template'])
plot.plot(orb1, color="orange", label="GDASAT-1, after orbital insertion")
plot.plot(orb_target, color="red", label="NOAA-17, target sat")
#plot._layout.width=1200
#plot._layout.height=800
plot.show();
# STEP 2: Synchronize Right Ascension of the Ascending Node. Changing RAAN as any other out of plane maneuver
# is in general a costly operation. However, there's a great saving trick that allows performing
# such change almost for free - the J2 perturbation (oblate earth)
# First, lets estimate how fast the orbit precesses in the course of 24 hours.
from poliastro.core.perturbations import J2_perturbation
tof = (24.0 * u.h).to(u.s)
orb24h = orb1.propagate(tof, method=cowell, ad=J2_perturbation, J2=Earth.J2.value, R=Earth.R.to(u.km).value)
print("Orbit precessed from RAAN=%s to %s in 24h, giving the rate of %s/24h." % (orb1.raan, orb24h.raan.to(u.deg), (orb24h.raan - orb1.raan).to(u.deg)))
# The target orbit has RAAN of 208.32deg. Our initial orbit has RANN of 96.29deg. The orbital plane needs to rotate by 112.03deg.
# Since the perfromance is 0.988 deg/24h, the rotation will take roughly 113 days. Let's do the 113 days in one simulation...
tof = (113 * 24 * u.h).to(u.s)
orb2 = orb1.propagate(tof, method=cowell, ad=J2_perturbation, J2=Earth.J2.value, R=Earth.R.to(u.km).value)
print("RAAN after 113 days is %s" % orb2.raan.to(u.deg))
# ... and then continue simulating one hour at a time until the RAAN is at least the same as that of target's orbit:
cnt = 0
while orb2.raan < orb_target.raan:
tof = (10*u.min).to(u.s)
cnt +=1
orb2 = orb2.propagate(tof, method=cowell, ad=J2_perturbation, J2=Earth.J2.value, R=Earth.R.to(u.km).value)
print("Orbit after J2 perturbation (%d days, %d mins):" % (113, cnt*10))
print_orb(orb2)
print("Target orbit:")
print_orb(orb_target)
Orbit precessed from RAAN=96.2906 deg to 97.27942040624983 deg in 24h, giving the rate of 0.9888204062498249 deg/24h. RAAN after 113 days is 207.57302661216775 deg Orbit after J2 perturbation (113 days, 1100 mins): 6953 x 6967 km x 97.7 deg (GCRS) orbit around Earth (♁) at epoch 2021-01-26T05:12:00.207552000 (UTC) a(𝑎)=6960.24km, b=6960.24km, e=0.00, i=97.72deg raan(Ω)=208.32deg argp(𝜔)=282.47deg nu(𝜈)=-56.73deg period=5778.93s perapis=6953km(575.25km) apoapsis=6967km(588.96km) Target orbit: 7178 x 7194 km x 98.6 deg (GCRS) orbit around Earth (♁) at epoch 2020-09-19T19:25:34.251744000 (UTC) a(𝑎)=7186.39km, b=7186.38km, e=0.00, i=98.59deg raan(Ω)=208.32deg argp(𝜔)=327.55deg nu(𝜈)=32.57deg period=6062.85s perapis=7178km(800.27km) apoapsis=7194km(816.22km)
# Let's visualize the orbits. The virtually identical RAAN value makes the orbits almost co-planar. However,
# the only difference is their slightly different inclination: 98.59deg vs 97.71 deg.
plot = OrbitPlotter3D()
plot.plot(orb2, color="orange", label="GDASAT-1, after RAAN drift")
plot.plot(orb_target, color="red", label="NOAA-17, target sat")
plot.show()
# STEP 3: Change orbital inclination.
# Our orbit has inclination of 97.7 deg, while the target is 98.6 deg. Let's calculate the delta inclination
delta_theta = orb_target.inc - orb1.inc
print("Need to change inclination by %s" % delta_theta)
# Now let's wait till the spacecraft flies to the descending node.
orb3 = propagate_to_desc_node(orb2)
man1 = plane_change_maneuver(orb2, delta_theta)
print("Plane change maneuver: %s" % man1)
print("maneuver 1 is %s" % repr(man1[0]))
orb4 = orb3.apply_maneuver(man1)
print("== Current orbit (after inclination change) ==")
print_orb(orb4)
print("== Target orbit ==")
print_orb(orb_target)
# The orbits are now fully co-planar!
# We can use Hohmann transfer now!
Need to change inclination by 0.8777000000000044 deg Plane change maneuver: Number of impulses: 1, Total cost: 0.115987 km / s maneuver 1 is (<Quantity 0. s>, <Quantity [ 0.05504824, -0.1007999 , 0.01619127] km / s>) == Current orbit (after inclination change) == 6955 x 6967 km x 98.6 deg (GCRS) orbit around Earth (♁) at epoch 2021-01-26T06:36:05.106362871 (UTC) a(𝑎)=6960.74km, b=6960.73km, e=0.00, i=98.60deg raan(Ω)=208.32deg argp(𝜔)=279.16deg nu(𝜈)=-99.16deg period=5779.54s perapis=6955km(576.41km) apoapsis=6967km(588.79km) == Target orbit == 7178 x 7194 km x 98.6 deg (GCRS) orbit around Earth (♁) at epoch 2020-09-19T19:25:34.251744000 (UTC) a(𝑎)=7186.39km, b=7186.38km, e=0.00, i=98.59deg raan(Ω)=208.32deg argp(𝜔)=327.55deg nu(𝜈)=32.57deg period=6062.85s perapis=7178km(800.27km) apoapsis=7194km(816.22km)
plot = OrbitPlotter2D()
plot.plot(orb4, color="orange", label="GDASAT-1, after inclination change")
plot.plot(orb_target, color="red", label="NOAA-17, target sat")
plot._layout.width=1200
plot._layout.height=800
plot.show()
# STEP 4: Hohmann transfer
# This is generally simple maneuver, however the classic Hohmann maneuver is between two circular orbits.
# Nevertheless we can reasonably use it as the orbits are nearly circular. Hohmann manevuer consists
# of two burns.
man23 = Maneuver.hohmann(orb4, 7178 * u.km)
print(man23)
print("Maneuver 2: %s" % repr(man23[0]))
print("Maneuver 3: %s" % repr(man23[1]))
print("== Current orbit (after Hohmann transfer) ==")
orb5 = orb4.apply_maneuver(man23)
print_orb(orb5)
Number of impulses: 2, Total cost: 0.115398 km / s Maneuver 2: (<Quantity 1590.30720989 s>, <Quantity [-49.52185833, -25.16882297, 8.85292832] m / s>) Maneuver 3: (<Quantity 2955.74292612 s>, <Quantity [52.07033882, 26.46405413, -9.30851532] m / s>) == Current orbit (after Hohmann transfer) == 7178 x 7178 km x 98.6 deg (GCRS) orbit around Earth (♁) at epoch 2021-01-26T07:51:51.156498878 (UTC) a(𝑎)=7178.00km, b=7178.00km, e=0.00, i=98.60deg raan(Ω)=208.32deg argp(𝜔)=0.00deg nu(𝜈)=99.16deg period=6052.24s perapis=7178km(799.86km) apoapsis=7178km(799.86km)
# STEP 5: Final corrections.
# The final touches needed to synchronize orbits. Our orbit is 800x800km, but the target is 800x816km.
# The elliptic burn can be done at the periapsis (which is equal to our altitude), so the raised
# point (apoapsis) will be higher.
# First we need to calculate the difference of orbital velocity of 800x800km orbit and
# the 800x816km orbit at apoapsis.
from poliastro.constants import GM_earth
r_i = 800000 *u.m + Earth.R
v800 = np.sqrt(GM_earth*(1/r_i))
print("Orbital velocity of 800x800km orbit is %s" % v800)
r_1 = 800000 *u.m + Earth.R
r_2 = 816000 *u.m + Earth.R
v816 = np.sqrt(GM_earth*(2/r_1 - 2/(r_1 + r_2)))
print("Orbital velocity of 800x816km orbit at periapsis is %s" % v816)
# This was first experimentally chosen value is 4.3 m/s. Later on, I've calculated
# the value (see astro-dynamics.ods, Hohmann sheet, M11), also the v800, v816 calculations above)
dv = v816 - v800
print("Required delta-v is %s" % dv)
## TODO: fix this to conduct the burn at a time that will sync argp
orb6 = propagate_to_periapsis(orb5)
man4 = prograde_maneuver(orb6, dv.value, None)
print("Maneuver 4 is %s" % repr(man4[0]))
orb7 = orb6.apply_maneuver(man4)
Orbital velocity of 800x800km orbit is 7451.831541112051 m / s Orbital velocity of 800x816km orbit at periapsis is 7455.978280403477 m / s Required delta-v is 4.146739291425547 m / s Maneuver 4 is (<Quantity 0. s>, <Quantity [-0.00029408, 0.00054566, 0.00410015] km / s>)
# SUMMARY: Orbits are now synced. There are minor differences, but the delta-v required to
# correct it is nagligible (e.g. the cost of raising apoapsis was in the order of 4m/s)
#
print("== Here's our orbit ==")
print_orb(orb7)
print("== Here's the target orbit ==")
print_orb(orb_target)
== Here's our orbit == 7178 x 7194 km x 98.6 deg (GCRS) orbit around Earth (♁) at epoch 2021-01-26T09:04:56.363403879 (UTC) a(𝑎)=7186.00km, b=7186.00km, e=0.00, i=98.60deg raan(Ω)=208.32deg argp(𝜔)=360.00deg nu(𝜈)=0.00deg period=6062.36s perapis=7178km(799.86km) apoapsis=7194km(815.86km) == Here's the target orbit == 7178 x 7194 km x 98.6 deg (GCRS) orbit around Earth (♁) at epoch 2020-09-19T19:25:34.251744000 (UTC) a(𝑎)=7186.39km, b=7186.38km, e=0.00, i=98.59deg raan(Ω)=208.32deg argp(𝜔)=327.55deg nu(𝜈)=32.57deg period=6062.85s perapis=7178km(800.27km) apoapsis=7194km(816.22km)
# The total delta-v of all maneuvers combined is as follows
delta_v = man1.get_total_cost() + man23.get_total_cost() + man4.get_total_cost()
delta_v.to(u.m/u.s)
from poliastro.util import norm
print(man1.get_total_cost().to(u.m/u.s))
print(norm(man23._dvs[0]))
print(norm(man23._dvs[1]))
print(man4.get_total_cost().to(u.m/u.s))
115.98743740537127 m / s 56.25174167464684 m / s 59.14655360541718 m / s 4.146739291425546 m / s