Skip to content

from_tle does a lossy round-trip instead of just parsing the values #43

@mgiuca

Description

@mgiuca

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions