A minor issue that could be improved (I happened to discover while writing tests in #41).
KeplerianElements.from_tle works by using the sgp4 library to parse a TLE string (which produces Keplerian elements in SGP's internal data model), then passes it to sgp4.propagation.sgp4 which converts those elements to a state vector (r, v pair). Then it calls KeplerianElements.from_state_vector to convert the state vector back to elements. This is mathematically correct, but it's lossy as it round-trips through a state vector and can introduce major rounding errors depending on the eccentricity.
It also unnecessarily changes values to equivalent but different values, when there is redundancy involved. See this example, which I made using the sample TLE file on Wikipedia:
from numpy import radians
from scipy.constants import kilo
import orbital
from orbital import earth, earth_sidereal_day, KeplerianElements, Maneuver, plot, plot3d
import matplotlib.pyplot as plt
# Sample TLE from Wikipedia:
# https://en.wikipedia.org/wiki/Two-line_element_set
# ISS (ZARYA)
LINE1 = '1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927'
LINE2 = '2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537'
zarya = KeplerianElements.from_tle(LINE1, LINE2, body=earth)
print(zarya)
# If you want to view it in 3D:
#anim = plot3d(zarya, title='ISS (Zarya)', animate=True)
#plt.show()
This prints:
KeplerianElements:
Semimajor axis (a) = 6725.548 km
Eccentricity (e) = 0.000833
Inclination (i) = 51.6 deg
Right ascension of the ascending node (raan) = 247.5 deg
Argument of perigee (arg_pe) = 112.5 deg
Mean anomaly at reference epoch (M0) = 343.1 deg
Period (T) = 1:31:29.116873
Reference epoch (ref_epoch) = 2008-09-20 12:25:40.104191
Mean anomaly (M) = 343.1 deg
Time (t) = 0:00:00
Epoch (epoch) = 2008-09-20 12:25:40.104191
All of these are "correct" but they're slightly off from the values contained in the source data. For example, a = 6725.548 km, which is off by 5 km. (It's supposed to be 15.721 revolutions / day -- the last number on the second line, which comes to a = 6730.961 km.)
The arg_pe and M0 are both 18° off: arg_pe is 112.5°, should be 130.5° (6th value on line 2). M0 is 343.1°, should be 325.0° (7th value on line 2). Because the eccentricity is so low it's basically circular, the absolute values of arg_pe and M0 don't matter, only the sum of them. So this is still "correct"; the 18° errors are cancelling out. But it is unnecessary to go changing these values from what is supplied.
To fix this, we can simply read the values out of the SGP4 data structure. sgp4/io.py is the code where this string is parsed and written into the data structure, so we can just read those same values out again:
sat = sgp4.io.twoline2rv(line1, line2, wgs72)
i = sat.inclo
raan = sat.nodeo
e = sat.ecco
arg_pe = sat.argpo
M0 = sat.mo
n = sat.no_kozai / 60 # Convert from rad/min.
That gives all the values except a, which can be derived by using KeplerianElements.with_mean_motion.
For now, I am adding a test case in #41 that tests these "slightly off" values, and afterwards, will prepare a PR to replace the implementation to get more accurate values.
A minor issue that could be improved (I happened to discover while writing tests in #41).
KeplerianElements.from_tleworks by using the sgp4 library to parse a TLE string (which produces Keplerian elements in SGP's internal data model), then passes it tosgp4.propagation.sgp4which converts those elements to a state vector (r, v pair). Then it callsKeplerianElements.from_state_vectorto convert the state vector back to elements. This is mathematically correct, but it's lossy as it round-trips through a state vector and can introduce major rounding errors depending on the eccentricity.It also unnecessarily changes values to equivalent but different values, when there is redundancy involved. See this example, which I made using the sample TLE file on Wikipedia:
This prints:
All of these are "correct" but they're slightly off from the values contained in the source data. For example, a = 6725.548 km, which is off by 5 km. (It's supposed to be 15.721 revolutions / day -- the last number on the second line, which comes to a = 6730.961 km.)
The arg_pe and M0 are both 18° off: arg_pe is 112.5°, should be 130.5° (6th value on line 2). M0 is 343.1°, should be 325.0° (7th value on line 2). Because the eccentricity is so low it's basically circular, the absolute values of arg_pe and M0 don't matter, only the sum of them. So this is still "correct"; the 18° errors are cancelling out. But it is unnecessary to go changing these values from what is supplied.
To fix this, we can simply read the values out of the SGP4 data structure. sgp4/io.py is the code where this string is parsed and written into the data structure, so we can just read those same values out again:
That gives all the values except a, which can be derived by using
KeplerianElements.with_mean_motion.For now, I am adding a test case in #41 that tests these "slightly off" values, and afterwards, will prepare a PR to replace the implementation to get more accurate values.