def test_from_horizons_scalar_epoch_uses_reshaped_epochs(horizons_mock): unused_name = "Strange Object" unused_id_type = "id_type" unused_plane = Planes.EARTH_EQUATOR unused_location_str = "500@399" unused_attractor = Earth expected_epochs = Time(["2020-03-01 12:00:00"], scale="tdb") epochs = expected_epochs[0] horizons_mock().vectors.return_value = { "x": [1] * u.au, "y": [0] * u.au, "z": [0] * u.au, "vx": [0] * (u.au / u.day), "vy": [1] * (u.au / u.day), "vz": [0] * (u.au / u.day), } Ephem.from_horizons( unused_name, epochs, attractor=unused_attractor, plane=unused_plane, id_type=unused_id_type, ) horizons_mock.assert_called_with( id=unused_name, location=unused_location_str, epochs=expected_epochs.jd, id_type=unused_id_type, )
def test_from_body_non_tdb_epochs_warning(epochs): unused_body = Earth epochs = Time.now() # This uses UTC scale warning_pattern = "Input time was converted to scale='tdb'" with pytest.warns(TimeScaleWarning, match=warning_pattern): Ephem.from_body(unused_body, epochs)
def test_ephem_sample_same_epochs_returns_same_input(epochs, coordinates, method): unused_plane = Planes.EARTH_EQUATOR ephem = Ephem(coordinates, epochs, unused_plane) result_coordinates = ephem.sample(epochs, method=method) # TODO: Should it return exactly the same? assert_coordinates_allclose(result_coordinates, coordinates, atol_scale=1e-17)
def test_ephem_sample_scalar_epoch_returns_1_dimensional_coordinates( epochs, coordinates, method): unused_plane = Planes.EARTH_EQUATOR ephem = Ephem(coordinates, epochs, unused_plane) result_coordinates = ephem.sample(epochs[0], method=method) # Exactly the same assert result_coordinates.ndim == 1
def test_ephem_sample_no_arguments_returns_exactly_same_input( epochs, coordinates, method): unused_plane = Planes.EARTH_EQUATOR ephem = Ephem(coordinates, epochs, unused_plane) result_coordinates = ephem.sample(method=method) # Exactly the same assert np.all(result_coordinates == coordinates)
def test_ephem_sample_existing_epochs_returns_corresponding_input( epochs, coordinates, method ): unused_plane = Planes.EARTH_EQUATOR ephem = Ephem(coordinates, epochs, unused_plane) result_coordinates = ephem.sample(epochs[::2], method=method) # Exactly the same assert_coordinates_allclose(result_coordinates, coordinates[::2], atol_scale=1e-17)
def test_ephem_sample_scalar_epoch_and_coordinates_returns_exactly_same_input( epochs, coordinates, method): unused_plane = Planes.EARTH_EQUATOR coordinates = coordinates[0].reshape(-1) epochs = epochs[0].reshape(-1) ephem = Ephem(coordinates, epochs, unused_plane) result_coordinates = ephem.sample(epochs[0], method=method) # Exactly the same assert result_coordinates == coordinates
def test_from_body_non_tdb_epochs_warning(epochs): unused_body = Earth epochs = Time.now() # This uses UTC scale with pytest.warns(TimeScaleWarning) as record: Ephem.from_body(unused_body, epochs) assert len(record) == 1 assert "Input time was converted to scale='tdb'" in record[0].message.args[ 0] assert "Use Time(..., scale='tdb') instead" in record[0].message.args[0]
def test_rv_scalar_epoch_returns_scalar_vectors(coordinates, epochs): unused_plane = Planes.EARTH_EQUATOR ephem = Ephem(coordinates, epochs, unused_plane) expected_r = coordinates.get_xyz(xyz_axis=1)[0] expected_v = coordinates.differentials["s"].get_d_xyz(xyz_axis=1)[0] r, v = ephem.rv(epochs[0]) assert_quantity_allclose(r, expected_r) assert_quantity_allclose(v, expected_v)
def test_rv_no_parameters_returns_input_vectors(coordinates, epochs): unused_plane = Planes.EARTH_EQUATOR ephem = Ephem(coordinates, epochs, unused_plane) expected_r = coordinates.get_xyz(xyz_axis=1) expected_v = coordinates.differentials["s"].get_d_xyz(xyz_axis=1) r, v = ephem.rv() assert_quantity_allclose(r, expected_r) assert_quantity_allclose(v, expected_v)
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 _get_ephem(self): for x in self.body_dict["bodies"]: body = DummyBody(moon_dict[x]["index"]) ephem = Ephem.from_body(body, self.current_time, attractor=self.body_dict["attractor"]) self.ephems[x] = ephem
def test_ephem_from_body_has_expected_properties(method, plane, FrameClass, rtol): epochs = Time( [ "2020-03-01 12:00:00", "2020-03-17 00:00:00.000", "2020-04-01 12:00:00.000" ], scale="tdb", ) equatorial_coordinates = CartesianRepresentation( [ (-1.40892271e08, 45067626.83900666, 19543510.68386639), (-1.4925067e08, 9130104.71634121, 3964948.59999307), (-1.46952333e08, -27413113.24215863, -11875983.21773582), ] * u.km, xyz_axis=1, differentials=CartesianDifferential( [ (-10.14262131, -25.96929533, -11.25810932), (-2.28639444, -27.3906416, -11.87218591), (5.67814544, -26.84316701, -11.63720607), ] * (u.km / u.s), xyz_axis=1, ), ) expected_coordinates = ( ICRS(equatorial_coordinates).transform_to(FrameClass).represent_as( CartesianRepresentation, CartesianDifferential)) earth = Ephem.from_body(Earth, epochs, plane=plane) coordinates = earth.sample(method=method) assert earth.epochs is epochs assert_coordinates_allclose(coordinates, expected_coordinates, rtol=rtol)
def test_ephem_from_horizons_calls_horizons_with_correct_parameters( horizons_mock, attractor, location_str, plane, refplane_str ): unused_name = "Strange Object" unused_id_type = "id_type" epochs = Time(["2020-03-01 12:00:00"], scale="tdb") horizons_mock().vectors.return_value = { "x": [1] * u.au, "y": [0] * u.au, "z": [0] * u.au, "vx": [0] * (u.au / u.day), "vy": [1] * (u.au / u.day), "vz": [0] * (u.au / u.day), } expected_coordinates = CartesianRepresentation( [(1, 0, 0)] * u.au, xyz_axis=1, differentials=CartesianDifferential([(0, 1, 0)] * (u.au / u.day), xyz_axis=1), ) ephem = Ephem.from_horizons( unused_name, epochs, attractor=attractor, plane=plane, id_type=unused_id_type ) horizons_mock.assert_called_with( id=unused_name, location=location_str, epochs=epochs.jd, id_type=unused_id_type ) horizons_mock().vectors.assert_called_once_with(refplane=refplane_str) coordinates = ephem.sample() assert_coordinates_allclose(coordinates, expected_coordinates)
def _get_ephem_from_list_of_bodies( bodies, epochs) -> Dict[str, Tuple[SolarSystemPlanet, Ephem]]: list_of_bodies = {} for i in bodies: ephem = Ephem.from_body(i, epochs) list_of_bodies[i.name] = (i, ephem) return list_of_bodies
def test_ephem_fails_if_dimensions_are_not_correct(epochs, coordinates): unused_plane = Planes.EARTH_EQUATOR with pytest.raises(ValueError) as excinfo: Ephem(epochs[0], coordinates, unused_plane) assert ( "Coordinates and epochs must have dimension 1, got 0 and 1" in excinfo.exconly() )
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_from_body_scalar_epoch_uses_reshaped_epochs(): expected_epochs = Time(["2020-03-01 12:00:00"], scale="tdb") epochs = expected_epochs[0] unused_plane = Planes.EARTH_EQUATOR ephem = Ephem.from_body(Earth, epochs, plane=unused_plane) assert ephem.epochs == expected_epochs
def test_ephem_str_matches_expected_representation(epochs, coordinates): plane = Planes.EARTH_EQUATOR ephem = Ephem(coordinates, epochs, plane) expected_str = ( "Ephemerides at 4 epochs " "from 2020-03-01 12:00:00.000 (TDB) to 2020-03-04 12:00:00.000 (TDB)") assert repr(ephem) == str(ephem) == expected_str
def test_can_set_iss_attractor_to_earth(): # See https://github.com/poliastro/poliastro/issues/798 epoch = Time("2019-11-10 12:00:00") ephem = Ephem.from_horizons( "International Space Station", epochs=epoch, attractor=Sun, id_type="majorbody" ) iss = Orbit.from_ephem(Sun, ephem, epoch) iss = iss.change_attractor(Earth) assert iss.attractor == Earth
def get_earth_ephem(epoch=None): if epoch is None: today = date.today() epoch = time.Time(today.strftime("%Y-%m-%d") + " 12:00") # UTC by default earth_eph = Ephem.from_body(Earth, epoch.tdb) return earth_eph
def test_ephem_without_frame_raises_error(): epochs = time.Time("2020-04-29 10:43", scale="tdb") earth = Ephem.from_body(Earth, epochs) plotter = OrbitPlotter2D() with pytest.raises(ValueError) as excinfo: plotter.set_attractor(Sun) plotter.plot_ephem(earth) assert ("A frame must be set up first, please use " "set_orbit_frame(orbit) or plot(orbit)" in excinfo.exconly())
def test_from_ephem_has_expected_properties(): epoch = J2000_TDB ephem = Ephem.from_body(Earth, epoch, attractor=Sun) expected_r, expected_v = ephem.rv(epoch) ss = Orbit.from_ephem(Sun, ephem, epoch) assert ss.plane is ephem.plane assert ss.epoch == epoch assert_quantity_allclose(ss.r, expected_r) assert_quantity_allclose(ss.v, expected_v)
def test_from_orbit_scalar_epoch_uses_reshaped_epochs(): r = [-6045, -3490, 2500] * u.km v = [-3.457, 6.618, 2.533] * u.km / u.s orb = Orbit.from_vectors(Earth, r, v) expected_epochs = Time(["2020-01-02 12:00:00"]) epochs = expected_epochs[0] unused_plane = Planes.EARTH_EQUATOR ephem = Ephem.from_orbit(orbit=orb, epochs=epochs, plane=unused_plane) assert ephem.epochs == expected_epochs
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 bodies_vector(T, dt, body1, body2, epoch=J2000, timedelta = None): """Returns vector for every dt in [0,T] from bodie1 to bodie2 Parameters ---------- T : float time of propagation dt : float time interval body1 : ~poliastro.bodies.Body first body body2 : ~poliastro.bodies.Body second body epoch : ~astropy.time.Time, optional Epoch offset timedelta : ~astropy.time.Time, optional desired time of propagation Returns ------- diff : ~astropy.units.quantity.Quantity vectors for timedelta propagation from body1 to body2 """ DT_TIME = 500 if not timedelta: time_delta = Time([epoch + j*u.s for j in np.linspace(0,3600*24*1.01*T, int(3600*24*T/DT_TIME)) ] ) else: time_delta = timedelta ephem_b1 = Ephem.from_body(body1, time_delta.tdb) ephem_b2 = Ephem.from_body(body2, time_delta.tdb) tofs = Time([epoch + j*dt*u.s for j in range(10,int(3600*24*T/dt)+10)]) r_b1, _ = ephem_b1.rv(tofs) r_b2, _ = ephem_b2.rv(tofs) diff = (r_b2-r_b1).to(u.km) return diff
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 test_plot_ephem_different_plane_raises_error(): unused_epochs = Time.now().reshape(-1) unused_coordinates = CartesianRepresentation( [(1, 0, 0)] * u.au, xyz_axis=1, differentials=CartesianDifferential([(0, 1, 0)] * (u.au / u.day), xyz_axis=1), ) op = StaticOrbitPlotter(plane=Planes.EARTH_ECLIPTIC) op.set_attractor(Sun) op.set_body_frame(Earth) with pytest.raises(ValueError) as excinfo: op.plot_ephem(Ephem(unused_epochs, unused_coordinates, Planes.EARTH_EQUATOR)) assert ( "sample the ephemerides using a different plane or create a new plotter" in excinfo.exconly() )
def reset(self): # get planet and spaceship positions at start_time, reset spaceship fuel, self.current_time = self.start_time self.current_ephem = self._get_ephem_from_list_of_bodies( self.body_list, self.start_time) # set up spacecraft self.spaceship = self._init_spaceship() start_body_ephem = Ephem.from_body(self.start_body, self.start_time) self.spaceship.rv = (self.spaceship.rv[0] + start_body_ephem.rv()[0], self.spaceship.rv[1] + start_body_ephem.rv()[1]) # convert spaceship rv to system-relative rather than earth observation = self._get_observation() return observation
def set_body_frame(self, body, epoch=None): """Sets perifocal frame based on the orbit of a body at a particular epoch if given. Parameters ---------- body : poliastro.bodies.SolarSystemPlanet Body. epoch : astropy.time.Time, optional Epoch of current position. """ from warnings import warn from astropy import time from poliastro.bodies import Sun from poliastro.twobody import Orbit from ..warnings import TimeScaleWarning if not epoch: epoch = time.Time.now().tdb elif epoch.scale != "tdb": epoch = epoch.tdb warn( "Input time was converted to scale='tdb' with value " f"{epoch.tdb.value}. Use Time(..., scale='tdb') instead.", TimeScaleWarning, stacklevel=2, ) with warnings.catch_warnings(): ephem = Ephem.from_body(body, epoch, attractor=Sun, plane=self.plane) # type: ignore orbit = Orbit.from_ephem(Sun, ephem, epoch).change_plane( self.plane) # type: ignore self.set_orbit_frame(orbit)