def test_orbit_from_classical_wraps_out_of_range_anomaly_and_warns(): _d = 1.0 * u.AU # Unused distance _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle out_angle = np.pi * u.rad with pytest.warns(UserWarning, match="Wrapping true anomaly to -π <= nu < π"): Orbit.from_classical(Sun, _d, _, _a, _a, _a, out_angle)
def test_from_classical_wrong_dimensions_fails(): bad_a = [1.0] * u.AU _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle with pytest.raises(ValueError) as excinfo: Orbit.from_classical(Earth, bad_a, _, _a, _a, _a, _a) assert "ValueError: Elements must be scalar, got [1.] AU" in excinfo.exconly()
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_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_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_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_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_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_orbit_from_classical_wraps_out_of_range_anomaly_and_warns(): _d = 1.0 * u.AU # Unused distance _ = 0.5 * u.one # Unused dimensionless value _a = 1.0 * u.deg # Unused angle out_angle = np.pi * u.rad with pytest.warns(UserWarning, match="Wrapping true anomaly to -π <= nu < π"): Orbit.from_classical(attractor=Sun, a=_d, ecc=_, inc=_a, raan=_a, argp=_a, nu=out_angle)
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 from_classical( cls, a, ecc, inc, raan, argp, nu, ): """Return EarthSatellite with the 'Orbit' from classical orbital elements. Parameters ---------- a : ~astropy.units.Quantity Semi-major axis. ecc : ~astropy.units.Quantity Eccentricity. inc : ~astropy.units.Quantity Inclination raan : ~astropy.units.Quantity Right ascension of the ascending node. argp : ~astropy.units.Quantity Argument of the pericenter. nu : ~astropy.units.Quantity True anomaly. Returns ------- EarthSatellite New EarthSatellite with the 'Orbit' from classical orbital elements. """ orbit = Orbit.from_classical(Earth, a, ecc, inc, raan, argp, nu) return cls(orbit)
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_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 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') orbit = Orbit.from_classical(Sun, a, ecc, inc, raan, argp, nu, epoch) return orbit
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_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_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_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_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_apply_maneuver_changes_epoch(): _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) dt = 1 * u.h dv = [0, 0, 0] * u.km / u.s orbit_new = ss.apply_maneuver([(dt, dv)]) assert orbit_new.epoch == ss.epoch + dt
def test_perigee_and_apogee(): expected_r_a = 500 * u.km expected_r_p = 300 * u.km a = (expected_r_a + expected_r_p) / 2 ecc = expected_r_a / a - 1 _a = 1.0 * u.deg # Unused angle ss = Orbit.from_classical(Earth, a, ecc, _a, _a, _a, _a) assert_allclose(ss.r_a.to(u.km).value, expected_r_a.to(u.km).value) assert_allclose(ss.r_p.to(u.km).value, expected_r_p.to(u.km).value)
def test_apply_maneuver_changes_epoch(): _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) dt = 1 * u.h dv = [0, 0, 0] * u.km / u.s orbit_new = ss.apply_maneuver([(dt, dv)]) assert orbit_new.epoch == ss.epoch + dt
def test_perigee_and_apogee(): expected_r_a = 500 * u.km expected_r_p = 300 * u.km a = (expected_r_a + expected_r_p) / 2 ecc = expected_r_a / a - 1 _a = 1.0 * u.deg # Unused angle ss = Orbit.from_classical(Earth, a, ecc, _a, _a, _a, _a) assert_allclose(ss.r_a.to(u.km).value, expected_r_a.to(u.km).value) assert_allclose(ss.r_p.to(u.km).value, expected_r_p.to(u.km).value)
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(attractor=Sun, a=_d, ecc=_, inc=_a, raan=_a, argp=_a, nu=_a) p, _, _ = ss.pqw() assert_allclose(p, ss.e_vec / ss.ecc)
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(attractor=_body, a=_d, ecc=_, inc=_a, raan=_a, argp=_a, nu=_a) positions = ss.sample(values=50) assert len(positions) == 50
def orbit_from_sbdb(name, **kwargs): obj = SBDB.query(name, full_precision=True, **kwargs) if "count" in obj: # No error till now ---> more than one object has been found # Contains all the name of the objects objects_name = obj["list"]["name"] objects_name_in_str = "" # Used to store them in string form each in new line for i in objects_name: objects_name_in_str += i + "\n" raise ValueError( str(obj["count"]) + " different objects found: \n" + objects_name_in_str) if "object" not in obj.keys(): raise ValueError(f"Object {name} not found") a = obj["orbit"]["elements"]["a"] ecc = float(obj["orbit"]["elements"]["e"]) * u.one inc = obj["orbit"]["elements"]["i"] raan = obj["orbit"]["elements"]["om"] argp = obj["orbit"]["elements"]["w"] # Since JPL provides Mean Anomaly (M) we need to make # the conversion to the true anomaly (nu) M = obj["orbit"]["elements"]["ma"].to(u.rad) # 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) epoch = Time(obj["orbit"]["epoch"].to(u.d), format="jd") return Orbit.from_classical( attractor=Sun, a=a, ecc=ecc, inc=inc, raan=raan, argp=argp, nu=nu, epoch=epoch.tdb, plane=Planes.EARTH_ECLIPTIC, )
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(attractor=_body, a=_d, ecc=_, inc=_a, raan=_a, argp=_a, nu=_a) assert ss.epoch == expected_epoch
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(Sun, _d, _, _a, _a, _a, _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_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 = 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_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, a, ecc, _a, _a, _a, nu) orbit_t_p = orbit.t_p assert_quantity_allclose(orbit_t_p, expected_t_p, rtol=1e-2)
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_apply_maneuver_changes_epoch(): _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) dt = 1 * u.h dv = [0, 0, 0] * u.km / u.s orbit_new = ss.apply_maneuver([(dt, dv)]) assert orbit_new.epoch == ss.epoch + dt
def test_perigee_and_apogee(): expected_r_a = 500 * u.km expected_r_p = 300 * u.km a = (expected_r_a + expected_r_p) / 2 ecc = expected_r_a / a - 1 _a = 1.0 * u.deg # Unused angle ss = Orbit.from_classical(attractor=Earth, a=a, ecc=ecc, inc=_a, raan=_a, argp=_a, nu=_a) assert_allclose(ss.r_a.to(u.km).value, expected_r_a.to(u.km).value) assert_allclose(ss.r_p.to(u.km).value, expected_r_p.to(u.km).value)
def orbit_from_spk_id(spk_id, api_key=None): """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 or DEFAULT_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") ss = Orbit.from_classical(attractor, a, ecc, inc, raan, argp, nu, epoch, plane=Planes.EARTH_ECLIPTIC) return ss
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_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_orbit_from_horizons_has_expected_elements(): epoch = Time("2018-07-23", scale="tdb") # Orbit Parameters of Ceres # Taken from https://ssd.jpl.nasa.gov/horizons.cgi ss = Orbit.from_classical( Sun, 2.767107592216510 * u.au, 7.554803091400027e-2 * u.one, 2.718502494739172e1 * u.deg, 2.336913218336299e1 * u.deg, 1.322919809219236e2 * u.deg, 2.128957916690369e1 * u.deg, epoch, ) ss1 = Orbit.from_horizons(name="Ceres", epoch=epoch) assert ss.pqw()[0].value.all() == ss1.pqw()[0].value.all() assert ss.r_a == ss1.r_a assert ss.a == ss1.a
def test_orbits_are_same(): epoch = Time("2018-07-23") # Orbit Parameters of Ceres # Taken from https://ssd.jpl.nasa.gov/horizons.cgi ss = Orbit.from_classical( Sun, 2.767107584017257 * u.au, 0.07554802949294502 * u.one, 27.18502520750381 * u.deg, 23.36913256044832 * u.deg, 132.2919806192451 * u.deg, 21.28958091587153 * u.deg, epoch, ) ss1 = Orbit.from_horizons(name="Ceres", epoch=epoch) assert ss.pqw()[0].value.all() == ss1.pqw()[0].value.all() assert ss.r_a == ss1.r_a assert ss.a == ss1.a
def test_orbit_from_horizons_has_expected_elements(): epoch = Time("2018-07-23", scale="tdb") # Orbit Parameters of Ceres # Taken from https://ssd.jpl.nasa.gov/horizons.cgi ss = Orbit.from_classical( Sun, 2.76710759221651 * u.au, 0.07554803091400027 * u.one, 27.18502494739172 * u.deg, 23.36913218336299 * u.deg, 132.2919809219236 * u.deg, 21.28957916690369 * u.deg, epoch, ) ss1 = Orbit.from_horizons(name="Ceres", attractor=Sun, epoch=epoch) assert ss.pqw()[0].value.all() == ss1.pqw()[0].value.all() assert_quantity_allclose(ss.r_a, ss1.r_a, rtol=1.0e-4) assert_quantity_allclose(ss.a, ss1.a, rtol=1.0e-4)
def test_orbits_are_same(): epoch = Time("2018-07-23") # Orbit Parameters of Ceres # Taken from https://ssd.jpl.nasa.gov/horizons.cgi ss = Orbit.from_classical( Sun, 2.767107584017257 * u.au, 0.07554802949294502 * u.one, 27.18502520750381 * u.deg, 23.36913256044832 * u.deg, 132.2919806192451 * u.deg, 21.28958091587153 * u.deg, epoch, ) ss1 = Orbit.from_horizons(name="Ceres", epoch=epoch) assert ss.pqw()[0].value.all() == ss1.pqw()[0].value.all() assert ss.r_a == ss1.r_a assert ss.a == ss1.a
def orbit_from_spk_id(spk_id, api_key=None): """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 or DEFAULT_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') ss = Orbit.from_classical(attractor, a, ecc, inc, raan, argp, nu, epoch, plane=Planes.EARTH_ECLIPTIC) return ss