def check_orbit(p, e, i, Om, w, v, ts): """Checks that the given set of elements are calculated properly by elementslib.py Converts the given elements to state vectors using ele_to_vec, then uses those state vectors to create an OsculatingElements object, and then checks that the data in the OsculatingElements object matches the input elements. """ length = 1 for item in p, e, i, Om, w, v: if isinstance(item, (list, ndarray)): length = len(item) mu = 403503.2355022598 pos_vec, vel_vec = ele_to_vec(p, e, i, Om, w, v, mu) time_tt = ts.utc(2018).tt time = ts.tt(jd=repeat(time_tt, pos_vec[0].size)) elements = OsculatingElements(Distance(km=pos_vec), Velocity(km_per_s=vel_vec), time, mu) check_types(elements, length) compare(time, elements.time, 1e-9) compare(elements.semi_latus_rectum.km, p, 1e-9) compare(elements.eccentricity, e, 1e-14) compare(elements.inclination.radians, i, 1e-14, mod=True) compare(elements.longitude_of_ascending_node.radians, Om, 1e-14, mod=True) compare(elements.argument_of_periapsis.radians, w, 1e-14, mod=True) compare(elements.true_anomaly.radians, v, 1e-14, mod=True)
def _from_periapsis( cls, semilatus_rectum_au, eccentricity, inclination_degrees, longitude_of_ascending_node_degrees, argument_of_perihelion_degrees, t_periapsis, gm_km3_s2, center=None, target=None, ): """Build a `KeplerOrbit` given its parameters and date of periapsis.""" gm_au3_d2 = gm_km3_s2 * _CONVERT_GM pos, vel = ele_to_vec( semilatus_rectum_au, eccentricity, DEG2RAD * inclination_degrees, DEG2RAD * longitude_of_ascending_node_degrees, DEG2RAD * argument_of_perihelion_degrees, 0.0, gm_au3_d2, ) return cls( Distance(pos), Velocity(vel), t_periapsis, gm_au3_d2, center, target, )
def check_orbit(p, e, i, Om, w, v, p_eps=None, e_eps=None, i_eps=None, Om_eps=None, w_eps=None, v_eps=None): pos0, vel0 = ele_to_vec(p, e, i, Om, w, v, mu) pos1, vel1 = propagate(pos0, vel0, 0, times, mu) ele = OsculatingElements(Distance(km=pos1), Velocity(km_per_s=vel1), dummy_time, mu) if p_eps: compare(p, ele.semi_latus_rectum.km, p_eps) if e_eps: compare(e, ele.eccentricity, e_eps) if i_eps: compare(i, ele.inclination.radians, i_eps, mod=True) if Om_eps: compare(Om, ele.longitude_of_ascending_node.radians, Om_eps, mod=True) if w_eps: compare(w, ele.argument_of_periapsis.radians, w_eps, mod=True) if v_eps: compare(v, ele.true_anomaly.radians, v_eps, mod=True)
def from_true_anomaly(cls, p, e, i, Om, w, v, epoch, mu_km_s=None, mu_au_d=None, center=None, target=None, center_name=None, target_name=None, ): """ Creates a `KeplerOrbit` object from elements using true anomaly Parameters ---------- p : Distance Semi-Latus Rectum e : float Eccentricity i : Angle Inclination Om : Angle Longitude of Ascending Node w : Angle Argument of periapsis v : Angle True anomaly epoch : Time Time corresponding to `position` and `velocity` mu_km_s : float Value of mu (G * M) in km^3/s^2 mu_au_d : float Value of mu (G * M) in au^3/d^2 center : int NAIF ID of the primary body, 399 for geocentric orbits, 10 for heliocentric orbits target : int NAIF ID of the secondary body """ if (mu_km_s and mu_au_d) or (not mu_km_s and not mu_au_d): raise ValueError('Either mu_km_s or mu_au_d should be used, but not both') if mu_au_d: mu_km_s = mu_au_d * AU_KM**3 / DAY_S**2 position, velocity = ele_to_vec(p.km, e, i.radians, Om.radians, w.radians, v.radians, mu_km_s, ) return cls(Distance(km=position), Velocity(km_per_s=velocity), epoch, mu_km_s, center=center, target=target, center_name=center_name, target_name=target_name, )
def test_helpful_exceptions(): distance = Distance(1.234) expect = '''\ to use this Distance, ask for its value in a particular unit: distance.au distance.km distance.m''' with assert_raises(UnpackingError) as a: x, y, z = distance assert str(a.exception) == expect with assert_raises(UnpackingError) as a: distance[0] assert str(a.exception) == expect velocity = Velocity(1.234) expect = '''\ to use this Velocity, ask for its value in a particular unit: velocity.au_per_d velocity.km_per_s velocity.m_per_s''' with assert_raises(UnpackingError) as a: x, y, z = velocity assert str(a.exception) == expect with assert_raises(UnpackingError) as a: velocity[0] assert str(a.exception) == expect angle = Angle(radians=1.234) expect = '''\ to use this Angle, ask for its value in a particular unit: angle.degrees angle.hours angle.radians''' with assert_raises(UnpackingError) as a: x, y, z = angle assert str(a.exception) == expect with assert_raises(UnpackingError) as a: angle[0] assert str(a.exception) == expect
def write_PV_to_file(tle_obj_vec: list[dict], t0: datetime, tf: datetime, timestep: float, filepath: str = "sats-TLE-example.json") -> None: import json from collections import OrderedDict import numpy as np from sgp4.api import Satrec, SatrecArray, jday from skyfield.api import load from skyfield.sgp4lib import TEME from skyfield.units import Velocity, Distance from skyfield.framelib import ecliptic_J2000_frame from skyfield.positionlib import ICRF json_posveldata = OrderedDict() # all TLEs (w/ diff epochs) will be propagated to current time. epoch_error_flag = False time_vec = [t0] t = t0 while t < tf: t += datetime.timedelta(seconds=timestep) time_vec.append(t) years, months, days, hours, minutes, seconds = zip(*[(t.year, t.month, t.day, t.hour, t.minute, t.second) for t in time_vec]) years, months, days, hours, minutes, seconds = np.array(years), np.array( months), np.array(days), np.array(hours), np.array(minutes), np.array( seconds) jds, frs = jday(years, months, days, hours, minutes, seconds) satrecs_vec = [] for item in tle_obj_vec: sat_name = item["NORAD_CAT_ID"] epoch = datetime.datetime.fromisoformat(item["EPOCH"]) if (tf > (epoch + datetime.timedelta(days=5))) or ( tf < (epoch - datetime.timedelta(days=5))): # raise TypeError(f" Final propagation date {tf} is more than 5 days away from latest TLE epoch ({epoch}), for satellite {sat_name}} ") epoch_error_flag = True sat = Satrec.twoline2rv(item["TLE_LINE1"], item["TLE_LINE2"]) satrecs_vec.append(sat) json_posveldata[sat_name] = {"P": [], "V": []} satrecs = SatrecArray(satrecs_vec) errors, rs, vs = satrecs.sgp4(jds, frs) # r,v in TEME frame ts = load.timescale() for idxsat, satdata in enumerate(json_posveldata.values()): for idxtime, (jdi, fri) in enumerate(zip(jds, frs)): # convert to J2000 ttime = ts.ut1_jd(jdi + fri) rvicrf = ICRF.from_time_and_frame_vectors( t=ttime, frame=TEME, distance=Distance(km=rs[idxsat][idxtime]), velocity=Velocity(km_per_s=vs[idxsat][idxtime])) # rvj2000 = rvicrf.frame_xyz_and_velocity(ecliptic_J2000_frame) # pos_timestep_j2000, vel_timestep_j2000 = rvj2000[0].m, rvj2000[1].m_per_s pos_timestep_j2000, vel_timestep_j2000 = rvicrf.position.m, rvicrf.velocity.m_per_s satdata["P"].append(pos_timestep_j2000.tolist()) satdata["V"].append(vel_timestep_j2000.tolist()) # satdata["P"] = rs[idxsat].tolist() #TEME # satdata["V"] = vs[idxsat].tolist() #TEME if errors.any(): print("SGP4 errors found.") # check errror type. if epoch_error_flag: print( f"Warning: Results obtained might be inaccurate. Final propagation date {tf} is more than 5 days away from latest TLE epoch ({epoch}), for satellite {sat_name} " ) with open(filepath, "w") as file: json.dump(json_posveldata, file, indent=6) print(f"PV File written successfully at {filepath}.")
def test_converting_velocity_with_astropy(): velocity = Velocity(au_per_d=1.234) value1 = velocity.km_per_s value2 = velocity.to(u.km / u.s) epsilon = 1e-6 assert abs(value1 - value2.value) < epsilon
def test_constructors_accept_plain_lists(): Distance(au=[1, 2, 3]) Distance(km=[1, 2, 3]) Distance(m=[1, 2, 3]) Velocity(au_per_d=[1, 2, 3]) Velocity(km_per_s=[1, 2, 3])
def test_velocity_input_units(): v1 = Velocity(au_per_d=2.0) v2 = Velocity(km_per_s=3462.9137) assert abs(v1.au_per_d - v2.au_per_d) < 1e-7
def _from_mean_anomaly( cls, semilatus_rectum_au, eccentricity, inclination_degrees, longitude_of_ascending_node_degrees, argument_of_perihelion_degrees, mean_anomaly_degrees, epoch, gm_km3_s2, center=None, target=None, ): """ Creates a `KeplerOrbit` object from elements using mean anomaly Parameters ---------- p : Distance Semi-Latus Rectum e : float Eccentricity i : Angle Inclination Om : Angle Longitude of Ascending Node w : Angle Argument of periapsis M : Angle Mean anomaly epoch : Time Time corresponding to `position` and `velocity` mu_km_s : float Value of mu (G * M) in km^3/s^2 mu_au3_d2 : float Value of mu (G * M) in au^3/d^2 center : int NAIF ID of the primary body, 399 for geocentric orbits, 10 for heliocentric orbits target : int NAIF ID of the secondary body """ M = DEG2RAD * mean_anomaly_degrees gm_au3_d2 = gm_km3_s2 * _CONVERT_GM if eccentricity < 1.0: E = eccentric_anomaly(eccentricity, M) v = true_anomaly_closed(eccentricity, E) elif eccentricity > 1.0: E = eccentric_anomaly(eccentricity, M) v = true_anomaly_hyperbolic(eccentricity, E) else: v = true_anomaly_parabolic(semilatus_rectum_au, gm_au3_d2, M) pos, vel = ele_to_vec( semilatus_rectum_au, eccentricity, DEG2RAD * inclination_degrees, DEG2RAD * longitude_of_ascending_node_degrees, DEG2RAD * argument_of_perihelion_degrees, v, gm_au3_d2, ) return cls( Distance(pos), Velocity(vel), epoch, gm_au3_d2, center, target, )
"Jupiter", "Saturn", "Uranus", "Neptune", "Pluto", ): planet = planet_name + " Barycenter" position = (planets[planet] - sun).at(t) # This is broken, see # https://github.com/skyfielders/python-skyfield/issues/655#issuecomment-960377889 # osculating_elements_of(position, ecliptic_frame) # So we have to do things manually elems = OsculatingElements( Distance(np.einsum("mnr,nr->mr", ecliptic_frame, position.position.au)), # type: ignore Velocity( np.einsum("mnr,nr->mr", ecliptic_frame, position.velocity.au_per_d)), # type: ignore position.t, GM_dict[position.center] + GM_dict[position.target], ) c_object = CelestialObject(position.target, planet_name, elems) c_object.glue() position = (planets["Moon"] - planets["Earth"]).at(t) elems = osculating_elements_of(position) c_object = CelestialObject(position.target, "Moon", elems) c_object.glue()
def test_iterating_over_raw_velocity(): velocity = Velocity(au_per_d=1.234) with assert_raises(UnpackingError) as a: x, y, z = velocity assert str(a.exception) == '''\