def get_easy_orbit(launch_time, arrival_time): """ get_easy_orbit """ launch_span = time_range(launch_time, end=arrival_time) launch_time_plus_one_day = ( datetime.datetime.strptime(launch_time, '%Y-%m-%d') + datetime.timedelta(days=1)).date().isoformat() arrival_span = time_range(launch_time_plus_one_day, end=arrival_time) dv_dpt, dv_arr, c3dpt, c3arr, tof = porkchop(Earth, Mars, launch_span, arrival_span) opt_arr_time_idx, opt_dpt_time_idx = np.unravel_index( np.nanargmin(c3arr, axis=None), c3arr.shape) opt_dpt_time = launch_span[opt_dpt_time_idx].value opt_arr_time = arrival_span[opt_arr_time_idx].value return { 'launch_time': opt_dpt_time.split(' ')[0], 'arrivial_time': opt_arr_time.split(' ')[0] }
def test_time_range_raises_error_wrong_arguments(): exception_message = "ValueError: Either 'end' or 'spacing' must be specified" with pytest.raises(ValueError) as excinfo_1: util.time_range("2017-10-12 00:00") with pytest.raises(ValueError) as excinfo_2: util.time_range("2017-10-12 00:00", spacing=0, end=0, periods=0) assert exception_message in excinfo_1.exconly() assert exception_message in excinfo_2.exconly()
def test_porkchop_plotting(): fig, ax = plt.subplots() launch_span = time_range("2005-04-30", end="2005-10-07") arrival_span = time_range("2005-11-16", end="2006-12-21") dv_dpt, dv_arr, c3dpt, c3arr, tof = porkchop(Earth, Mars, launch_span, arrival_span, ax=ax) return fig
def _plot_body_orbit( self, body, epoch, *, label=None, color=None, trail=False, ): if color is None: color = BODY_COLORS.get(body.name) self.set_attractor(body.parent) # Get approximate, mean value for the period period = get_mean_elements(body, epoch).period label = generate_label(epoch, label or str(body)) epochs = time_range(epoch, periods=self._num_points, end=epoch + period, scale="tdb") ephem = Ephem.from_body(body, epochs, attractor=body.parent, plane=self.plane) return self._plot_ephem(ephem, epoch, label=label, color=color, trail=trail)
def distance_chart(body1, body2, date_start, interval, steps): """Generates a distance chart between body1 (e.g. Earth) and body2 (e.g. Mars) from date_start till interval (e.g. 10 days) and steps (36). Returns plotly's Figure.""" eph1 = Ephem.from_body( body1, time_range(date_start, end=date_start + steps * interval)) eph2 = Ephem.from_body( body2, time_range(date_start, end=date_start + steps * interval)) # Solve for departure and target orbits orb1 = Orbit.from_ephem(Sun, eph1, date_start) orb2 = Orbit.from_ephem(Sun, eph2, date_start) t_tbl = [] dist_tbl = [] t = date_start for i in range(1, steps): day = str(orb1.epoch)[:10] # take the first "2020-01-01" from the date t_tbl.append(day) dist = np.linalg.norm(orb1.r - orb2.r) dist_tbl.append(dist.value) orb1 = orb1.propagate(interval) orb2 = orb2.propagate(interval) fig = go.Figure() fig.add_trace( go.Scatter(x=t_tbl, y=dist_tbl, mode="lines+markers", name="Earth - Mars distance")) name = body1.name + "-" + body2.name + " distance" fig.update_layout(legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1), xaxis_title="date", yaxis_title="Distance [km]", title=name, margin=dict(l=20, r=20, t=40, b=20)) return fig
def test_time_range_spacing_periods(): start_time = "2017-10-12 00:00:00" end_time = "2017-10-12 00:04:00" spacing = 1 * u.minute periods = 5 expected_scale = "utc" expected_duration = 4 * u.min result_1 = util.time_range(start_time, spacing=spacing, periods=periods) result_2 = util.time_range(start_time, end=end_time, periods=periods) result_3 = util.time_range(Time(start_time), end=Time(end_time), periods=periods) assert len(result_1) == len(result_2) == len(result_3) == periods assert result_1.scale == result_2.scale == result_3.scale == expected_scale assert_quantity_allclose((result_1[-1] - result_1[0]).to(u.s), expected_duration) assert_quantity_allclose((result_2[-1] - result_2[0]).to(u.s), expected_duration) assert_quantity_allclose((result_3[-1] - result_3[0]).to(u.s), expected_duration)
def dist_chart(asteroid, date, timespan): solar_system_ephemeris.set('jpl') EPOCH = Time(date, scale="tdb") epochs = time_range(EPOCH - TimeDelta(timespan), end=EPOCH + TimeDelta(timespan)) epochs_moon = time_range(EPOCH - TimeDelta(15 * u.day), end=EPOCH + TimeDelta(15 * u.day)) moon = Ephem.from_body(Moon, epochs_moon, attractor=Earth) aster = Ephem.from_horizons(asteroid, epochs, attractor=Earth) plotter = StaticOrbitPlotter() plotter.set_attractor(Earth) plotter.set_body_frame(Moon) plotter.plot_ephem(moon, EPOCH, label=Moon) plotter.plot_ephem(aster, EPOCH, label=asteroid) return plotter
def test_plot_ephem_no_epoch(): epoch = Time("2020-02-14 00:00:00") ephem = Ephem.from_horizons( "2020 CD3", time_range(Time("2020-02-13 12:00:00"), end=Time("2020-02-14 12:00:00")), attractor=Earth, ) fig, ax = plt.subplots() plotter = StaticOrbitPlotter(ax=ax) plotter.set_attractor(Earth) plotter.set_orbit_frame(Orbit.from_ephem(Earth, ephem, epoch)) plotter.plot_ephem(ephem, label="2020 CD3 Minimoon", color="k") return fig
def transit(date, date_arrival, planet1, planet2): #czas trwania misji tof = date_arrival - date N = 50 tof.to(u.h) times_vector = time_range(date, end=date_arrival, periods=N) # okreslenie pozycji planet w przedziale czasu: od startu do końca misji rr_planet1, vv_planet1 = get_body_barycentric_posvel(planet1, times_vector) rr_planet2, vv_planet2 = get_body_barycentric_posvel(planet2, times_vector) r0 = rr_planet1[0].xyz #wektor pozycji Ziemi w momencie startu v0 = vv_planet1[0].xyz #wektor predkosci Ziemi w momencie startu rf = rr_planet2[ -1].xyz #wektor pozycji planety docelowej w momencie końca misji vf = vv_planet2[ -1].xyz #wektor predkosci planety docelowej w momencie końca misji # rozwiazanie problemu Lamberta (va, vb), = iod.lambert(Sun.k, r0, rf, tof, numiter=1000) return (r0, v0, rf, vf, va, vb, rr_planet1, rr_planet2, times_vector)
def build_ephem_interpolant(body, period, t_span, rtol=1e-5, attractor=Earth): """Interpolates ephemerides data Parameters ---------- body : Body Source body. period : ~astropy.units.Quantity Orbital period. t_span : list(~astropy.units.Quantity) Initial and final epochs. rtol : float, optional Relative tolerance. Controls the number of sampled data points, defaults to 1e-5. attractor : ~poliastro.bodies.Body, optional Attractor, default to Earth. Returns ------- interpolant : callable Interpolant function that receives time increment in seconds since the initial epoch. """ epochs = time_range( Time(t_span[0], format="jd", scale="tdb"), end=Time(t_span[1], format="jd", scale="tdb"), periods=int(((t_span[1] - t_span[0]) / (period * rtol)).to_value(u.one)), ) ephem = Ephem.from_body(body, epochs, attractor=attractor) interpolant = interp1d( (epochs - epochs[0]).to_value(u.s), ephem._coordinates.xyz.to_value(u.km), ) return interpolant
# The initial data was gathered from Wikipedia: the date of the launch was on **November 26, 2011 at 15:02 UTC** and landing was on **August 6, 2012 at 05:17 UTC**. We compute then the time of flight, which is exactly what it sounds. # In[4]: # Initial data date_launch = time.Time("2011-11-26 15:02", scale="utc").tdb date_arrival = time.Time("2012-08-06 05:17", scale="utc").tdb # To compute the transfer orbit, we have the useful function `lambert` : according to a theorem with the same name, *the transfer orbit between two points in space only depends on those two points and the time it takes to go from one to the other*. We could make use of the raw algorithms available in `poliastro.iod` for solving this but working with the `poliastro.maneuvers` is even easier! # # We just need to create the orbits for each one of the planets at the specific departure and arrival dates. # In[5]: earth = Ephem.from_body(Earth, time_range(date_launch, end=date_arrival)) mars = Ephem.from_body(Mars, time_range(date_launch, end=date_arrival)) # In[6]: # Solve for departure and target orbits ss_earth = Orbit.from_ephem(Sun, earth, date_launch) ss_mars = Orbit.from_ephem(Sun, mars, date_arrival) # We can now solve for the maneuver that will take us from Earth to Mars. After solving it, we just need to apply it to the departure orbit to solve for the transfer one. # In[7]: # Solve for the transfer maneuver man_lambert = Maneuver.lambert(ss_earth, ss_mars)
import matplotlib.pyplot as plt from poliastro.bodies import Sun, Earth, Mars from poliastro.ephem import Ephem from poliastro.twobody import Orbit from poliastro.maneuver import Maneuver from poliastro.plotting.static import StaticOrbitPlotter from poliastro.util import time_range # In[2]: # Departure and time of flight for the mission EPOCH_DPT = Time("2018-12-01", scale="tdb") EPOCH_ARR = EPOCH_DPT + 2 * u.year epochs = time_range(EPOCH_DPT, end=EPOCH_ARR) # Origin and target orbits earth = Ephem.from_body(Earth, epochs=epochs) mars = Ephem.from_body(Mars, epochs=epochs) earth_departure = Orbit.from_ephem(Sun, earth, EPOCH_DPT) mars_arrival = Orbit.from_ephem(Sun, mars, EPOCH_ARR) # In[3]: def lambert_transfer(ss_dpt, ss_arr, revs): """ Returns the short and long transfer orbits when solving Lambert's problem. Parameters
# In[7]: florence_osc.epoch.iso # Therefore, if we `propagate` this orbit to `EPOCH`, the results will be a bit different from the reality. Therefore, we need to find some other means. # # Let's use the `Ephem.from_horizons` method as an alternative, sampling over a period of 6 months: # In[8]: from poliastro.ephem import Ephem # In[9]: epochs = time_range(EPOCH - TimeDelta(3 * 30 * u.day), end=EPOCH + TimeDelta(3 * 30 * u.day)) # In[10]: florence = Ephem.from_horizons("Florence", epochs, plane=Planes.EARTH_ECLIPTIC) florence # In[11]: florence.plane # And now, let's compute the distance between Florence and the Earth at that epoch: # In[12]: earth = Ephem.from_body(Earth, epochs, plane=Planes.EARTH_ECLIPTIC)
def __init__(self, bodies: SystemScope = SystemScope.PLANETS, start_body: SolarSystemPlanet = None, target_bodies: List[SolarSystemPlanet] = None, start_time: Time = None, action_step: u.s = 3600 * u.s, simulation_ratio: int = 60, number_of_steps: int = 50000, spaceship_name: SpaceShipName = SpaceShipName.LOW_THRUST, spaceship_initial_altitude: u.km = 400 * u.km, spaceship_mass: u.kg = None, spaceship_propellant_mass: u.kg = None, spaceship_isp: u.s = None, spaceship_engine_thrust: u.N = None): super(SolarSystemFull, self).__init__() if start_body is None: start_body = Earth if target_bodies is None: target_bodies = [Mars] if start_time is None: start_time = Time(datetime.now()).tdb # enforce action_step/simulation_step is an integer if action_step.value % simulation_ratio != 0: raise ValueError( "Action step must be evenly divisible by simulation_ratio") self.start_body = start_body self.target_bodies = [] for target in target_bodies: self.target_bodies.append(target.name) self.spaceship_initial_altitude = spaceship_initial_altitude self.start_time = start_time self.current_time = None self.action_step = action_step self.simulation_ratio = simulation_ratio self.number_of_steps = number_of_steps self.done = False self.reward = 0 self.initial_reset = False self.r_normalization = 5e9 * u.km # factor to divide position observations by to normalize them to +- 1. # slightly more than neptune's orbit in km? self.v_normalization = 100 * u.km / u.s # factor to divide velocity observations by to normalize them to +- 1. # approx 2x mercury's orbital velocity in km? self.spaceship_name = spaceship_name self.spaceship_mass = spaceship_mass self.spaceship_propellant_mass = spaceship_propellant_mass self.spaceship_isp = spaceship_isp self.spaceship_engine_thrust = spaceship_engine_thrust # set up solar system solar_system_ephemeris.set("jpl") # Download & use JPL Ephem body_dict = { SystemScope.EARTH: [Earth, Moon], SystemScope.PLANETS: [ Sun, Earth, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune ] } # define bodies to model # poliastro.bodies.SolarSystemPlanet = # Sun, Earth, Moon, Mercury, Venus, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto # could also add versions for: only inner solar system, only 'major' bodies jovan moons, saturn's moons? try: self.body_list = body_dict[bodies] except KeyError: raise KeyError(f"bodies must be one of {body_dict.keys()}") self.soi_radii = self._calculate_system_laplace_radii() self.current_soi = self.start_body.name self.visited_times = {self.start_body.name: Time(datetime.now()).tdb} # set up spacecraft self.spaceship = self._init_spaceship() self.observation_space = spaces.Box(low=-1.0, high=1.0, shape=(10, 9)) # action: # tuple [[x,y,z], burn duration] self.action_space = spaces.Box( low=-1.0, high=1.0, shape=(4, )) # x,y,z direction vector, burn duration as # percent of time_step epochs = time_range(self.start_time, periods=self.number_of_steps, spacing=self.action_step) self.ephems = self._get_ephem_from_list_of_bodies( self.body_list, epochs) self.finish_time = self.start_time + self.action_step * self.number_of_steps
# # For the moment, poliastro is only capable of creating these mission plots between `poliastro.bodies` objects. However, it is intended for future versions to make it able for plotting porkchops between NEOs also. # # ### Basic modules # For creating a porkchop plot with poliastro, we need to import the `porkchop` function from the `poliastro.plotting.porkchop` module. Also, two `poliastro.bodies` are necessary for computing the targetting problem associated. Finally by making use of `time_range`, a very useful function available at `poliastro.utils` it is possible to define a span of launching and arrival dates for the problem. # In[1]: import astropy.units as u from poliastro.plotting.porkchop import porkchop from poliastro.bodies import Earth, Mars from poliastro.util import time_range launch_span = time_range("2005-04-30", end="2005-10-07") arrival_span = time_range("2005-11-16", end="2006-12-21") # ### Plot that porkchop! # # All that we must do is pass the two bodies, the two time spans and some extra plotting parameters realted to different information along the figure such us: # # * If we want poliastro to plot time of flight lines: `tfl=True/False` # * If we want poliastro to plot arrival velocity: `vhp=True/False` # * The maximum value for C3 to be ploted: `max_c3=45 * u.km**2 / u.s**2` (by default) # In[2]: dv_dpt, dv_arr, c3dpt, c3arr, tof = porkchop(Earth, Mars, launch_span, arrival_span)
# Atlas V supplied a launch energy C_3 = 31.1 * u.km**2 / u.s**2 # With previous dates we can create the different orbits that define the position of the Earth along the mission. # In[3]: # Plot initial Earth's position Earth.plot(date_launch, label="Initial Earth's position") # Since both Earth states have been obtained (initial and flyby) we can now solve for Juno's maneuvers. The first one sets Juno into an elliptical orbit around the Sun so it apply a gravity assist around the Earth. # In[4]: earth = Ephem.from_body(Earth, time_range(date_launch, end=date_arrival, periods=500)) r_e0, v_e0 = earth.rv(date_launch) # In[5]: # Assume that the insertion velocity is tangential to that of the Earth dv = C_3**0.5 * v_e0 / norm(v_e0) # We create the maneuver from impulse constructor man = Maneuver.impulse(dv) # If we now apply previous maneuver to the Junos's initial orbit (assume it is the Earth's one for simplicity), we will obtain the orbit around the Sun for Juno. The first inner cruise maneuver is defined just till the ahelion orbit. While Juno is traveling around its new orbit, Earth is also moving. After Juno reaches the aphelion it will be necessary to apply a second maneuver so the flyby is performed around Earth. Once that is achieved a final maneuver will be made in order to benefit from the gravity assist. Let us first propagate Juno's orbit till the aphelion. # In[6]:
frame.plot_body_orbit(Earth, J2000) frame.plot(eros, label="eros") # In[7]: from poliastro.ephem import Ephem from poliastro.util import time_range # In[8]: date_launch = time.Time("2011-11-26 15:02", scale="utc").tdb date_arrival = time.Time("2012-08-06 05:17", scale="utc").tdb earth = Ephem.from_body(Earth, time_range(date_launch, end=date_arrival, periods=50)) # In[9]: frame = OrbitPlotter3D() frame.set_attractor(Sun) frame.plot_body_orbit(Earth, J2000, label=Earth) frame.plot_ephem(earth, label=Earth) # In[10]: frame = OrbitPlotter3D() frame.plot(eros, label="eros") frame.plot_trajectory(earth.sample(), label=Earth)
def transfer_vel(body1, body2, attractor): """Returns transfer parameters for body1 (e.g. Earth) to body2 (e.g. Mars). Optionally, the main attractor can be specified. If omitted, Sun is assumed. Returns: helio1 - heliocentric velocity at departure (before Hohmann) (body1) helio2 - heliocentric velocity at arrival (body2) v1 - heliocentric velocity at departure (after Hohmann burn) v2 - heliocentric velocity at arrival (before Hohmann burn) tof - time of flight (in days) """ # How to obtain the orbit. The from_horisons method seems to be no longer supported # as of perylune 0.16.3. method = "ephem" # allowed values are ephem, horizons_orbit if attractor is None: attractor = Sun # Let's assume the calculations are done for 2020. date_start = time.Time("2020-01-01 00:00", scale="utc").tdb date_end = time.Time("2021-12-31 23:59", scale="utc").tdb name1, id_type1 = name_to_horizons_id(body1) name2, id_type2 = name_to_horizons_id(body2) if method == "ephem": # Get the ephemerides first and then contruct orbit based on them. This is the recommended # way. See warning in Orbit.from_horizons about deprecation. ephem1 = Ephem.from_horizons(name=name1, epochs=time_range(date_start, end=date_end), plane=Planes.EARTH_ECLIPTIC, id_type=id_type1) ephem2 = Ephem.from_horizons(name=name2, epochs=time_range(date_start, end=date_end), plane=Planes.EARTH_ECLIPTIC, id_type=id_type2) # Solve for departure and target orbits orb1 = Orbit.from_ephem(Sun, ephem1, date_start + 180 * u.day) orb2 = Orbit.from_ephem(Sun, ephem2, date_end) elif method == "horizons_orbit": # This is the old way. Sadly, it produces way better values. orb1 = Orbit.from_horizons(name=name1, attractor=attractor, plane=Planes.EARTH_ECLIPTIC, id_type=id_type1) orb2 = Orbit.from_horizons(name=name2, attractor=attractor, plane=Planes.EARTH_ECLIPTIC, id_type=id_type2) else: raise "Invalid method set." # The escape_delta_v returns a tuple of escape velocity at current, periapsis, apoapsis. helio1 = heliocentric_velocity(orb1) helio2 = heliocentric_velocity(orb2) #vesc1 = escape_vel(orb1, False)[1] #vesc2 = escape_vel(orb2, False)[1] hoh1, hoh2, tof = hohmann_velocity(orb1, orb2) return helio1, helio2, hoh1, hoh2, tof
body_from_name(values["dpt"]), body_from_name(values["arr"]), ) # Collect dates data min_dpt, max_dpt = ( values["min_dpt"].replace("/", "-"), values["max_dpt"].replace("/", "-", 2), ) min_arr, max_arr = ( values["min_arr"].replace("/", "-"), values["max_arr"].replace("/", "-", 2), ) # Collect plotting options max_c3 = values["max_c3"] # Get CANVAS elements canvas = window["porkchop_canvas"].TKCanvas # Generate porkchop launch_span = time_range(min_dpt, end=max_dpt) arrival_span = time_range(min_arr, end=max_arr) dv_launch, dev_dpt, c3dpt, c3arr, tof = porkchop( body_dpt, body_arr, launch_span, arrival_span) # Draw the canvas figure fig = plt.gcf() fig.set_size_inches(5, 5) fig_porkchop = draw_figure(canvas, fig)
def test_time_range_requires_keyword_arguments(): with pytest.raises(TypeError) as excinfo: util.time_range(0, 0) assert ( "TypeError: time_range() takes 1 positional argument but" in excinfo.exconly() )
def test_time_range_requires_keyword_arguments(): with pytest.raises(TypeError) as excinfo: util.time_range(0, 0) assert ("TypeError: time_range() takes 1 positional argument but" in excinfo.exconly())
from poliastro.util import time_range from astropy.coordinates import solar_system_ephemeris, get_body_barycentric_posvel solar_system_ephemeris.set("jpl") # Initial data N = 50 date_launch = time.Time('2011-11-26 15:02', scale='utc') date_arrival = time.Time('2012-08-06 05:17', scale='utc') tof = (date_arrival - date_launch) tof.to(u.h) #Array of positions of Earth and Mars times_vector = time_range(date_launch, end=date_arrival, periods=N) times_vector[:5] rr_earth, vv_earth = get_body_barycentric_posvel("earth", times_vector) rr_earth[:3] vv_earth[:3] rr_mars, vv_mars = get_body_barycentric_posvel("mars", times_vector) rr_mars[:3] vv_mars[:3] # Compute the transfer orbit! r0 = rr_earth[0].xyz rf = rr_mars[-1].xyz (va, vb), = iod.lambert(Sun.k, r0, rf, tof)
from poliastro.plotting import OrbitPlotter3D from poliastro.util import time_range EPOCH = Time("2018-02-18 12:00:00", scale="tdb") # In[2]: import plotly.io as pio pio.renderers.default = "notebook_connected" # In[3]: roadster = Ephem.from_horizons( "SpaceX Roadster", epochs=time_range(EPOCH, end=EPOCH + 360 * u.day), attractor=Sun, plane=Planes.EARTH_ECLIPTIC, id_type="majorbody", ) roadster # In[4]: from poliastro.plotting.misc import plot_solar_system # In[5]: frame = plot_solar_system(outer=False, epoch=EPOCH) frame.plot_ephem(roadster, EPOCH, label="SpaceX Roadster", color="black")
arrival_span = time_range(launch_time_plus_one_day, end=arrival_time) dv_dpt, dv_arr, c3dpt, c3arr, tof = porkchop(Earth, Mars, launch_span, arrival_span) opt_arr_time_idx, opt_dpt_time_idx = np.unravel_index( np.nanargmin(c3arr, axis=None), c3arr.shape) opt_dpt_time = launch_span[opt_dpt_time_idx].value opt_arr_time = arrival_span[opt_arr_time_idx].value return { 'launch_time': opt_dpt_time.split(' ')[0], 'arrivial_time': opt_arr_time.split(' ')[0] } if __name__ == "__main__": launch_span = time_range("2020-01-01", end="2020-12-30") arrival_span = time_range("2020-07-01", end="2021-12-30") easy_orbit = get_easy_orbit("2020-01-01", "2021-07-01") print( easy_orbit, total_delta_v(easy_orbit.get('launch_time'), easy_orbit.get('arrivial_time'))) print('End of Game')