def test_orbit_wrong_dimensions_fails(): bad_r = [[1000, 0, 0]] * u.km bad_v = [[[0, 10, 0]]] * u.km / u.s with pytest.raises(ValueError) as excinfo: Orbit.from_vectors(Earth, bad_r, bad_v) assert "ValueError: Vectors must have dimension 1, got 2 and 3" in excinfo.exconly()
def test_from_horizons_raise_valueerror(): with pytest.raises(ValueError) as exep: Orbit.from_horizons(name="Dummy") assert ( "ValueError: Unknown target (Dummy). Maybe try different id_type?" in exep.exconly() )
def test_frozen_orbit_altitude(): with pytest.raises(ValueError) as excinfo: Orbit.frozen(Earth, -1 * u.m) assert excinfo.type == ValueError assert ( str(excinfo.value) == "The semimajor axis may not be smaller that Earth's radius" )
def test_frozen_orbit_non_spherical_arguments(): with pytest.raises(AttributeError) as excinfo: Orbit.frozen(Jupiter, 1 * u.m) assert excinfo.type == AttributeError assert ( str(excinfo.value) == "Attractor Jupiter has not spherical harmonics implemented" )
def test_geostationary_input(attractor): with pytest.raises(ValueError) as excinfo: Orbit.geostationary(attractor=attractor) assert ( "ValueError: At least one among angular_velocity or period must be passed" in excinfo.exconly() )
def test_geostationary_non_existence_condition(attractor, period, hill_radius): with pytest.raises(ValueError) as excinfo: Orbit.geostationary(attractor=attractor, period=period, hill_radius=hill_radius) assert ( "Geostationary orbit for the given parameters doesn't exist" in excinfo.exconly() )
def test_from_sbdb_raise_valueerror(): with pytest.raises(ValueError) as excinfo: Orbit.from_sbdb(name="Halley") assert ( str(excinfo.value) == "2 different objects found: \n2688 Halley (1982 HG1)\n1P/Halley\n" )
def test_from_ephem_raises_warning_if_time_is_not_tdb_with_proper_time(recwarn): body = Earth epoch = Time("2017-09-29 07:31:26", scale="utc") expected_epoch_string = "2017-09-29 07:32:35.182" # epoch.tdb.value Orbit.from_body_ephem(body, epoch) w = recwarn.pop(TimeScaleWarning) assert expected_epoch_string in str(w.message)
def test_bad_hyperbolic_raises_exception(): bad_a = 1.0 * u.AU ecc = 1.5 * u.one _inc = 100 * u.deg # Unused inclination _a = 1.0 * u.deg # Unused angle _body = Sun # Unused body with pytest.raises(ValueError) as excinfo: Orbit.from_classical(_body, bad_a, ecc, _inc, _a, _a, _a) assert "Hyperbolic orbits have negative semimajor axis" in excinfo.exconly()
def test_state_raises_unitserror_if_rv_units_are_wrong(): _d = [1.0, 0.0, 0.0] * u.AU wrong_v = [0.0, 1.0e-6, 0.0] * u.AU with pytest.raises(u.UnitsError) as excinfo: Orbit.from_vectors(Sun, _d, wrong_v) assert ( "UnitsError: Argument 'v' to function 'from_vectors' must be in units convertible to 'm / s'." in excinfo.exconly() )
def test_plot_trajectory_sets_label(): op = OrbitPlotter() earth = Orbit.from_body_ephem(Earth) mars = Orbit.from_body_ephem(Mars) trajectory = earth.sample() op.plot(mars, label="Mars") op.plot_trajectory(trajectory, label="Earth") legend = plt.gca().get_legend() assert legend.get_texts()[1].get_text() == "Earth"
def test_bad_inclination_raises_exception(): _d = 1.0 * u.AU # Unused distance _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle _body = Sun # Unused body bad_inc = 200 * u.deg with pytest.raises(ValueError) as excinfo: Orbit.from_classical(_body, _d, _, bad_inc, _a, _a, _a) assert ( "ValueError: Inclination must be between 0 and 180 degrees" in excinfo.exconly() )
def test_state_raises_unitserror_if_elements_units_are_wrong(): _d = 1.0 * u.AU # Unused distance _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle wrong_angle = 1.0 * u.AU with pytest.raises(u.UnitsError) as excinfo: Orbit.from_classical(Sun, _d, _, _a, _a, _a, wrong_angle) assert ( "UnitsError: Argument 'nu' to function 'from_classical' must be in units convertible to 'rad'." in excinfo.exconly() )
def test_parabolic_elements_fail_early(): attractor = Earth ecc = 1.0 * u.one _d = 1.0 * u.AU # Unused distance _a = 1.0 * u.deg # Unused angle with pytest.raises(ValueError) as excinfo: Orbit.from_classical(attractor, _d, ecc, _a, _a, _a, _a) assert ( "ValueError: For parabolic orbits use Orbit.parabolic instead" in excinfo.exconly() )
def test_from_coord_fails_if_no_time_differential(): pos = [30000, 0, 0] * u.km cartrep = CartesianRepresentation(*pos) # Method fails if coordinate instance doesn't contain a differential with # respect to time with pytest.raises(ValueError) as excinfo: Orbit.from_coords(Earth, SkyCoord(cartrep)) assert ( "ValueError: Coordinate instance doesn't have a differential with respect to time" in excinfo.exconly() )
def test_from_coord_fails_for_multiple_positions(obstime): cartdiff = CartesianDifferential( [[0, 1, 0], [-0.1, 0.9, 0]] * u.km / u.s, xyz_axis=1 ) cartrep = CartesianRepresentation( [[1, 0, 0], [0.9, 0.1, 0]] * u.km, differentials=cartdiff, xyz_axis=1 ) coords = GCRS(cartrep, representation_type=CartesianRepresentation, obstime=obstime) with pytest.raises(ValueError) as excinfo: Orbit.from_coords(Earth, coords) assert ( "ValueError: Coordinate instance must represents exactly 1 position, found: 2" in excinfo.exconly() )
def orbit_from_record(record): """Return :py:class:`~poliastro.twobody.orbit.Orbit` given a record. Retrieve info from JPL DASTCOM5 database. Parameters ---------- record : int Object record. Returns ------- orbit : ~poliastro.twobody.orbit.Orbit NEO orbit. """ body_data = read_record(record) a = body_data['A'].item() * u.au ecc = body_data['EC'].item() * u.one inc = body_data['IN'].item() * u.deg raan = body_data['OM'].item() * u.deg argp = body_data['W'].item() * u.deg m = body_data['MA'].item() * u.deg nu = M_to_nu(m, ecc) epoch = Time(body_data['EPOCH'].item(), format='jd', scale='tdb') orbit = Orbit.from_classical(Sun, a, ecc, inc, raan, argp, nu, epoch) orbit._frame = HeliocentricEclipticJ2000(obstime=epoch) return orbit
def test_inertial_body_centered_to_pqw(): molniya_r_peri, molniya_v_peri = coordinates.inertial_body_centered_to_pqw(molniya.r, molniya.v, bodies.Earth) molniya_peri = Orbit.from_vectors(bodies.Earth, molniya_r_peri, molniya_v_peri, molniya.epoch) assert_quantity_allclose(molniya_peri.e_vec[-2:], [0, 0], atol=1e-12) assert_quantity_allclose(norm(molniya_peri.e_vec), norm(molniya.e_vec))
def test_perifocal_points_to_perigee(): _d = 1.0 * u.AU # Unused distance _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle ss = Orbit.from_classical(Sun, _d, _, _a, _a, _a, _a) p, _, _ = ss.pqw() assert_allclose(p, ss.e_vec / ss.ecc)
def transform(orbit, frame_orig, frame_dest): """Transforms Orbit from one frame to another. Parameters ---------- orbit : ~poliastro.bodies.Orbit Orbit to transform frame_orig : ~astropy.coordinates.BaseCoordinateFrame Initial frame frame_dest : ~astropy.coordinates.BaseCoordinateFrame Final frame Returns ------- orbit: ~poliastro.bodies.Orbit Orbit in the new frame """ orbit_orig = frame_orig(x=orbit.r[0], y=orbit.r[1], z=orbit.r[2], v_x=orbit.v[0], v_y=orbit.v[1], v_z=orbit.v[2], representation=CartesianRepresentation, differential_type=CartesianDifferential) orbit_dest = orbit_orig.transform_to(frame_dest(obstime=orbit.epoch)) orbit_dest.representation = CartesianRepresentation return Orbit.from_vectors(orbit.attractor, orbit_dest.data.xyz, orbit_dest.data.differentials['s'].d_xyz, epoch=orbit.epoch)
def plot_solar_system(outer=True, epoch=None): """ Plots the whole solar system in one single call. .. versionadded:: 0.9.0 Parameters ------------ outer : bool, optional Whether to print the outer Solar System, default to True. epoch: ~astropy.time.Time, optional Epoch value of the plot, default to J2000. """ bodies = [Mercury, Venus, Earth, Mars] if outer: bodies.extend([Jupiter, Saturn, Uranus, Neptune]) op = OrbitPlotter() for body in bodies: orb = Orbit.from_body_ephem(body, epoch) op.plot(orb, label=str(body)) # Sets frame to the orbit of the Earth by default # TODO: Wait until https://github.com/poliastro/poliastro/issues/316 # op.set_frame(*Orbit.from_body_ephem(Earth, epoch).pqw()) return op
def test_parabolic_has_proper_eccentricity(): attractor = Earth _d = 1.0 * u.AU # Unused distance _a = 1.0 * u.deg # Unused angle expected_ecc = 1.0 * u.one ss = Orbit.parabolic(attractor, _d, _a, _a, _a, _a) assert_allclose(ss.ecc, expected_ecc)
def test_orbit_representation(): ss = Orbit.circular( Earth, 600 * u.km, 20 * u.deg, epoch=Time("2018-09-08 09:04:00", scale="tdb") ) expected_str = "6978 x 6978 km x 20.0 deg (GCRS) orbit around Earth (\u2641) at epoch 2018-09-08 09:04:00.000 (TDB)" assert str(ss) == repr(ss) == expected_str
def test_orbit_accepts_ecliptic_plane(): r = [1e09, -4e09, -1e09] * u.km v = [5e00, -1e01, -4e00] * u.km / u.s ss = Orbit.from_vectors(Sun, r, v, plane=Planes.EARTH_ECLIPTIC) assert ss.frame.is_equivalent_frame(HeliocentricEclipticJ2000(obstime=J2000))
def test_orbit_plot_static_3d(): # Data from Curtis, example 4.3 r = [-6045, -3490, 2500] * u.km v = [-3.457, 6.618, 2.533] * u.km / u.s ss = Orbit.from_vectors(Earth, r, v) with pytest.raises(ValueError, match="static and use_3d cannot be true"): ss.plot(static=True, use_3d=True)
def test_sample_numpoints(): _d = 1.0 * u.AU # Unused distance _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle _body = Sun # Unused body ss = Orbit.from_classical(_body, _d, _, _a, _a, _a, _a) positions = ss.sample(values=50) assert len(positions) == 50
def test_geostationary_creation_with_Hill_radius( attractor, period, hill_radius, expected_a ): ss = Orbit.geostationary( attractor=attractor, period=period, hill_radius=hill_radius ) assert_quantity_allclose(ss.a, expected_a, rtol=1.0e-7) assert_quantity_allclose(ss.period, period, rtol=1.0e-7)
def test_default_time_for_new_state(): _d = 1.0 * u.AU # Unused distance _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle _body = Sun # Unused body expected_epoch = J2000 ss = Orbit.from_classical(_body, _d, _, _a, _a, _a, _a) assert ss.epoch == expected_epoch
def test_show_calls_prepare_plot(mock_prepare_plot, mock_iplot): m = OrbitPlotter2D() earth = Orbit.from_body_ephem(Earth) m.plot(orbit=earth, label="Obj") m.show() assert mock_iplot.call_count == 1 mock_prepare_plot.assert_called_once_with()
def test_orbit_no_frame_representation(): date_launch = Time("2011-11-26 15:02", scale="utc") r = [61445.76498656, 24827.93010168, 0.0] * u.km v = [-0.42581645, -0.18867869, 0.0] * u.km / u.s ss = Orbit.from_vectors(Moon, r, v, date_launch) expected_str = "106 x -142299 km x 180.0 deg orbit around Moon (\u263E) at epoch 2011-11-26 15:02:00.000 (UTC)" assert str(ss) == repr(ss) == expected_str
def orbit_from_orbit(init_orbit, part_of_period, alfa_init): # alfa in degrees period_init = init_orbit.period t_maneuver = part_of_period * period_init mean_motion_init = init_orbit.n anomaly_init = (alfa_init / 180 * np.pi + mean_motion_init * t_maneuver) * u.rad orbit_dprt = init_orbit.propagate_to_anomaly(anomaly_init) r = orbit_dprt.r v = orbit_dprt.v date_arrival = time.Time((DATA_LAUNCH.unix + t_maneuver), format='unix') orbit = Orbit.from_vectors(Earth, r, v, epoch=date_arrival) return orbit
def test_orbit_represent_as_produces_correct_data(): r = [1e09, -4e09, -1e09] * u.km v = [5e00, -1e01, -4e00] * u.km / u.s ss = Orbit.from_vectors(Sun, r, v) expected_result = CartesianRepresentation( *r, differentials=CartesianDifferential(*v)) result = ss.represent_as(CartesianRepresentation) # We can't directly compare the objects, see # https://github.com/astropy/astropy/issues/7793 assert (result.xyz == expected_result.xyz).all() assert (result.differentials["s"].d_xyz == expected_result.differentials["s"].d_xyz).all()
def test_expected_angular_momentum(): # Example from Curtis expected_ang_mag = 72472 * u.km**2 attractor = Earth _a = 1.0 * u.deg # Unused angle a = 15_300 * u.km ecc = 0.37255 * u.one nu = 120 * u.deg orbit = Orbit.from_classical(attractor, a, ecc, _a, _a, _a, nu) orbit_h_mag = orbit.h_mag assert_quantity_allclose(orbit_h_mag.value, expected_ang_mag.value, rtol=1e-2)
def test_convert_from_rv_to_coe(): # Data from Vallado, example 2.6 attractor = Earth p = 11067.790 * u.km ecc = 0.83285 * u.one inc = 87.87 * u.deg raan = 227.89 * u.deg argp = 53.38 * u.deg nu = 92.335 * u.deg expected_r = [6525.344, 6861.535, 6449.125] * u.km expected_v = [4.902276, 5.533124, -1.975709] * u.km / u.s r, v = Orbit.from_classical(attractor, p / (1 - ecc**2), ecc, inc, raan, argp, nu).rv() assert_quantity_allclose(r, expected_r, rtol=1e-5) assert_quantity_allclose(v, expected_v, rtol=1e-5)
def test_expected_mean_anomaly(): # Example from Curtis expected_mean_anomaly = 77.93 * u.deg attractor = Earth _a = 1.0 * u.deg # Unused angle a = 15_300 * u.km ecc = 0.37255 * u.one nu = 120 * u.deg orbit = Orbit.from_classical(attractor, a, ecc, _a, _a, _a, nu) orbit_M = orbit.M assert_quantity_allclose(orbit_M.value, expected_mean_anomaly.value, rtol=1e-2)
def test_orbit_creation_using_skycoord(attractor): vel = [0, 2, 0] * u.km / u.s cartdiff = CartesianDifferential(*vel) pos = [30_000, 0, 0] * u.km cartrep = CartesianRepresentation(*pos, differentials=cartdiff) coord = SkyCoord(cartrep, frame="icrs") o = Orbit.from_coords(attractor, coord) inertial_frame_at_body_centre = get_frame( attractor, Planes.EARTH_EQUATOR, obstime=coord.obstime ) coord_transformed_to_irf = coord.transform_to(inertial_frame_at_body_centre) pos_transformed_to_irf = coord_transformed_to_irf.cartesian.xyz vel_transformed_to_irf = coord_transformed_to_irf.cartesian.differentials["s"].d_xyz assert (o.r == pos_transformed_to_irf).all() assert (o.v == vel_transformed_to_irf).all()
def test_from_sbdb(): # Dictionary with structure: 'Object': [a, e, i, raan, argp, nu, epoch] # Notice JPL provides Mean anomaly, a conversion is needed to obtain nu SBDB_DATA = { "Ceres": ( 2.76916515450648 * u.AU, 0.07600902910070946 * u.one, 10.59406704424526 * u.deg, 80.30553156826473 * u.deg, 73.597694115971 * u.deg, 86.01851107780747 * u.deg, ) } for target_name in SBDB_DATA.keys(): ss_target = Orbit.from_sbdb(target_name) ss_classical = ss_target.classical() assert ss_classical == SBDB_DATA[target_name]
def from_vectors(cls, r, v): """Return EarthSatellite with the `Orbit` from position and velocity vectors. Parameters ---------- r : ~astropy.units.Quantity Position vector wrt attractor center. v : ~astropy.units.Quantity Velocity vector. Returns ------- EarthSatellite New EarthSatellite with the'Orbit' position and velocity vectors. """ orbit = Orbit.from_vectors(Earth, r, v) return cls(orbit)
def test_apply_maneuver_returns_intermediate_states_if_true(): _d = 1.0 * u.AU # Unused distance _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle ss = Orbit.from_classical(attractor=Sun, a=_d, ecc=_, inc=_a, raan=_a, argp=_a, nu=_a) dt1 = 0.5 * u.h dv1 = [5, 0, 10] * u.km / u.s dt2 = 0.5 * u.h dv2 = [0, 5, 10] * u.km / u.s states = ss.apply_maneuver([(dt1, dv1), (dt2, dv2)], intermediate=True) assert len(states) == 2 assert states[-1].epoch == ss.epoch + dt1 + dt2
def test_orbit_creation_using_frame_obj(attractor, frame, obstime): vel = [0, 2, 0] * u.km / u.s cartdiff = CartesianDifferential(*vel) pos = [30_000, 0, 0] * u.km cartrep = CartesianRepresentation(*pos, differentials=cartdiff) coord = frame(cartrep, obstime=obstime) o = Orbit.from_coords(attractor, coord) inertial_frame_at_body_centre = get_frame( attractor, Planes.EARTH_EQUATOR, obstime=coord.obstime ) coord_transformed_to_irf = coord.transform_to(inertial_frame_at_body_centre) pos_transformed_to_irf = coord_transformed_to_irf.cartesian.xyz vel_transformed_to_irf = coord_transformed_to_irf.cartesian.differentials["s"].d_xyz assert_quantity_allclose(o.r, pos_transformed_to_irf, atol=1e-5 * u.km) assert_quantity_allclose(o.v, vel_transformed_to_irf, atol=1e-5 * u.km / u.s)
def test_expected_last_perifocal_passage(): # Example from Curtis expected_t_p = 4077 * u.s attractor = Earth _a = 1.0 * u.deg # Unused angle a = 15_300 * u.km ecc = 0.37255 * u.one nu = 120 * u.deg orbit = Orbit.from_classical(attractor=attractor, a=a, ecc=ecc, inc=_a, raan=_a, argp=_a, nu=nu) orbit_t_p = orbit.t_p assert_quantity_allclose(orbit_t_p, expected_t_p, rtol=1e-2)
def test_expected_mean_anomaly(): # Example from Curtis expected_mean_anomaly = 77.93 * u.deg attractor = Earth _a = 1.0 * u.deg # Unused angle a = 15_300 * u.km ecc = 0.37255 * u.one nu = 120 * u.deg orbit = Orbit.from_classical(attractor=attractor, a=a, ecc=ecc, inc=_a, raan=_a, argp=_a, nu=nu) orbit_M = E_to_M(nu_to_E(orbit.nu, orbit.ecc), orbit.ecc) assert_quantity_allclose(orbit_M, expected_mean_anomaly, rtol=1e-2)
def test_from_orbit_has_desired_properties(method, rtol): epochs = Time( [ "2020-02-01 12:00:00", "2020-02-13 12:00:00", "2020-03-04 12:00:00", "2020-03-17 12:00:00", ] ) expected_coordinates = CartesianRepresentation( [ (336.77109079, -1447.38211842, -743.72094119), (-1133.43957703, 449.41297342, 3129.10416554), (201.42480053, -1978.64139325, -287.25776291), (-1084.94556884, -1713.5357774, 3298.72284309), ] * u.km, xyz_axis=1, differentials=CartesianDifferential( [ (-2.68502706, -14.85798508, 9.66683585), (1.36841306, 7.30080155, -4.88822441), (-3.46999908, -10.04899184, 11.19715233), (-1.46069855, 5.88696886, 3.28112281), ] * (u.km / u.s), xyz_axis=1, ), ) r = [-1000, -2000, 3100] * u.km v = [-1.836, 5.218, 4.433] * u.km / u.s orb = Orbit.from_vectors(Earth, r, v) unused_plane = Planes.EARTH_EQUATOR ephem = Ephem.from_orbit(orbit=orb, epochs=epochs, plane=unused_plane) coordinates = ephem.sample() assert ephem.epochs is epochs assert_coordinates_allclose(coordinates, expected_coordinates, rtol=rtol)
def orbit_from_spk_id(spk_id, api_key='DEMO_KEY'): """Return :py:class:`~poliastro.twobody.orbit.Orbit` given a SPK-ID. Retrieve info from NASA NeoWS API, and therefore it only works with NEAs (Near Earth Asteroids). Parameters ---------- spk_id : str SPK-ID number, which is given to each body by JPL. api_key : str NASA OPEN APIs key (default: `DEMO_KEY`) Returns ------- orbit : ~poliastro.twobody.orbit.Orbit NEA orbit. """ payload = {'api_key': api_key} response = requests.get(NEOWS_URL + spk_id, params=payload) response.raise_for_status() orbital_data = response.json()['orbital_data'] attractor = Sun a = float(orbital_data['semi_major_axis']) * u.AU ecc = float(orbital_data['eccentricity']) * u.one inc = float(orbital_data['inclination']) * u.deg raan = float(orbital_data['ascending_node_longitude']) * u.deg argp = float(orbital_data['perihelion_argument']) * u.deg m = float(orbital_data['mean_anomaly']) * u.deg nu = M_to_nu(m.to(u.rad), ecc) epoch = Time(float(orbital_data['epoch_osculation']), format='jd', scale='tdb') return Orbit.from_classical(attractor, a, ecc, inc, raan, argp, nu, epoch)
def transform(orbit, frame_orig, frame_dest): """Transforms Orbit from one frame to another. Parameters ---------- orbit : ~poliastro.bodies.Orbit Orbit to transform frame_orig : ~astropy.coordinates.BaseCoordinateFrame Initial frame frame_dest : ~astropy.coordinates.BaseCoordinateFrame Final frame Returns ------- orbit: ~poliastro.bodies.Orbit Orbit in the new frame """ orbit_orig = frame_orig( x=orbit.r[0], y=orbit.r[1], z=orbit.r[2], v_x=orbit.v[0], v_y=orbit.v[1], v_z=orbit.v[2], representation=CartesianRepresentation, differential_type=CartesianDifferential, ) orbit_dest = orbit_orig.transform_to(frame_dest(obstime=orbit.epoch)) orbit_dest.representation = CartesianRepresentation return Orbit.from_vectors( orbit.attractor, orbit_dest.data.xyz, orbit_dest.data.differentials["s"].d_xyz, epoch=orbit.epoch, )
def test_from_sbdb(): # Dictionary with structure: 'Object': [a, e, i, raan, argp, nu, epoch] # Notice JPL provides Mean anomaly, a conversion is needed to obtain nu SBDB_DATA = { "Ceres": ( 2.769165146349478 * u.AU, 0.07600902762923671 * u.one, 10.59406732590292 * u.deg, 80.30553084093981 * u.deg, 73.59769486239257 * u.deg, M_to_nu(77.37209773768207 * u.deg, 0.07600902762923671 * u.one), ) } for target_name in SBDB_DATA.keys(): ss_target = Orbit.from_sbdb(target_name) ss_classical = ss_target.classical() assert ss_classical == SBDB_DATA[target_name]
def orbit_from_record(record): """Return :py:class:`~poliastro.twobody.orbit.Orbit` given a record. Retrieve info from JPL DASTCOM5 database. Parameters ---------- record : int Object record. Returns ------- orbit : ~poliastro.twobody.orbit.Orbit NEO orbit. """ body_data = read_record(record) a = body_data["A"].item() * u.au ecc = body_data["EC"].item() * u.one inc = body_data["IN"].item() * u.deg raan = body_data["OM"].item() * u.deg argp = body_data["W"].item() * u.deg M = body_data["MA"].item() * u.deg epoch = Time(body_data["EPOCH"].item(), format="jd", scale="tdb") # NOTE: It is unclear how this conversion should happen, # see https://ssd-api.jpl.nasa.gov/doc/sbdb.html if ecc < 1: M = (M + np.pi * u.rad) % (2 * np.pi * u.rad) - np.pi * u.rad nu = E_to_nu(M_to_E(M, ecc), ecc) elif ecc == 1: nu = D_to_nu(M_to_D(M)) else: nu = F_to_nu(M_to_F(M, ecc), ecc) orbit = Orbit.from_classical(Sun, a, ecc, inc, raan, argp, nu, epoch) orbit._frame = HeliocentricEclipticJ2000(obstime=epoch) return orbit
def test_propagate_instance(): tof = 1.0 * u.min ss0 = Orbit.from_classical( Earth, 1000 * u.km, 0.75 * u.one, 63.4 * u.deg, 0 * u.deg, 270 * u.deg, 80 * u.deg, ) C_D = 2.2 * u.one # dimentionless (any value would do) A = ((np.pi / 4.0) * (u.m**2)).to(u.km**2) m = 100 * u.kg spacecraft = Spacecraft(A, C_D, m) earth_satellite = EarthSatellite(ss0, spacecraft) orbit_with_j2 = earth_satellite.propagate(tof=tof, gravity=EarthGravity.J2) orbit_without_perturbation = earth_satellite.propagate(tof) orbit_with_atmosphere_and_j2 = earth_satellite.propagate( tof=tof, gravity=EarthGravity.J2, atmosphere=COESA76()) assert isinstance(orbit_with_j2, EarthSatellite) assert isinstance(orbit_with_atmosphere_and_j2, EarthSatellite) assert isinstance(orbit_without_perturbation, EarthSatellite)
def test_convert_from_coe_to_rv(): # Data from Vallado, example 2.5 attractor = Earth r = [6524.384, 6862.875, 6448.296] * u.km v = [4.901327, 5.533756, -1.976341] * u.km / u.s expected_p = 11067.79 * u.km expected_ecc = 0.832853 * u.one expected_inc = 87.870 * u.deg expected_raan = 227.89 * u.deg expected_argp = 53.38 * u.deg expected_nu = 92.335 * u.deg ss = Orbit.from_vectors(attractor, r, v) _, ecc, inc, raan, argp, nu = ss.classical() p = ss.p assert_quantity_allclose(p, expected_p, rtol=1e-4) assert_quantity_allclose(ecc, expected_ecc, rtol=1e-4) assert_quantity_allclose(inc, expected_inc, rtol=1e-4) assert_quantity_allclose(raan, expected_raan, rtol=1e-4) assert_quantity_allclose(argp, expected_argp, rtol=1e-4) assert_quantity_allclose(nu, expected_nu, rtol=1e-4)
def test_propagate_instance(): tof = 1.0 * u.min ss0 = Orbit.from_classical( attractor=Earth, a=1000 * u.km, ecc=0.75 * u.one, inc=63.4 * u.deg, raan=0 * u.deg, argp=270 * u.deg, nu=80 * u.deg, ) C_D = 2.2 * u.one # Dimensionless (any value would do) A = ((np.pi / 4.0) * (u.m**2)).to(u.km**2) m = 100 * u.kg spacecraft = Spacecraft(A, C_D, m) earth_satellite = EarthSatellite(ss0, spacecraft) orbit_with_j2 = earth_satellite.propagate(tof=tof, gravity=EarthGravity.J2) orbit_without_perturbation = earth_satellite.propagate(tof) # orbit_with_atmosphere_and_j2 = earth_satellite.propagate( # tof=tof, gravity=EarthGravity.J2, atmosphere=COESA76() # ) assert isinstance(orbit_with_j2, EarthSatellite) # assert isinstance(orbit_with_atmosphere_and_j2, EarthSatellite) assert isinstance(orbit_without_perturbation, EarthSatellite)
def test_from_sbdb_raise_valueerror(): with pytest.raises(ValueError) as excinfo: Orbit.from_sbdb(name="Halley") assert (str(excinfo.value) == "2 different objects found: \n2688 Halley (1982 HG1)\n1P/Halley\n")
def test_arglat_within_range(): r = [3539.08827417, 5310.19903462, 3066.31301457] * u.km v = [-6.49780849, 3.24910291, 1.87521413] * u.km / u.s ss = Orbit.from_vectors(Earth, r, v) assert 0 * u.deg <= ss.arglat <= 360 * u.deg
def test_geostationary_creation_from_period(attractor, period, expected_a): ss = Orbit.geostationary(attractor=attractor, period=period) assert_quantity_allclose(ss.a, expected_a, rtol=1.0e-7) assert_quantity_allclose(ss.period, period, rtol=1.0e-7)
def test_plane_is_set_in_horizons(): plane = Planes.EARTH_ECLIPTIC ss = Orbit.from_horizons(name="Ceres", plane=plane) assert ss.plane == plane
def test_from_horizons_raise_valueerror(): with pytest.raises(ValueError) as exep: Orbit.from_horizons(name="Dummy") assert ("ValueError: Unknown target (Dummy). Maybe try different id_type?" in exep.exconly())
def test_orbit_from_ephem_is_in_icrs_frame(body): ss = Orbit.from_body_ephem(body) assert ss.frame.is_equivalent_frame(ICRS())
def test_frozen_orbit_altitude(): with pytest.raises(ValueError) as excinfo: Orbit.frozen(Earth, -1 * u.m) assert excinfo.type == ValueError assert (str(excinfo.value) == "The semimajor axis may not be smaller that Earth's radius")
def test_frozen_orbit_non_spherical_arguments(): with pytest.raises(AttributeError) as excinfo: Orbit.frozen(Jupiter, 1 * u.m) assert excinfo.type == AttributeError assert (str(excinfo.value) == "Attractor Jupiter has not spherical harmonics implemented")
def test_frozen_orbit_venus_special_case(): with pytest.raises(NotImplementedError) as excinfo: Orbit.frozen(Venus, 1 * u.m) assert excinfo.type == NotImplementedError assert str(excinfo.value) == "This has not been implemented for Venus"
def test_frozen_orbit_non_critical_inclination(): orbit = Orbit.frozen(Earth, 1e3 * u.km, inc=0 * u.deg) # Non-critical value assert orbit.argp in [np.pi / 2, 3 * np.pi / 2] * u.rad