Hello,
I’m trying to generate TLEs from spacecraft state, hence trying to use the stateToTLE method. But I end up creating TLEs that actually don’t meet my expectations while working on a simple case (as a way to validate my future work).
For instance, while creating a simple Keplerian orbit, generating states, and using the stateToTLE method, the Keplerian elements contained in the resulting TLEs are varying quite a lot, though it would be expected for it to be constant.
The code I’m using for that simple case :
a_in_meters_without_earth_radius = 600000.0
e = 0.001
i_in_degrees = 51.0
aop_in_degrees = 20.0
raan_in_degrees = 10.0
ta_in_degrees = 0.0
ITRF = FramesFactory.getITRF(IERSConventions.IERS_2010, True)
utc = TimeScalesFactory.getUTC()
inertialFrame = FramesFactory.getEME2000()
earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,
Constants.WGS84_EARTH_FLATTENING,
FramesFactory.getITRF(IERSConventions.IERS_2010, True))
a = Constants.WGS84_EARTH_EQUATORIAL_RADIUS + a_in_meters_without_earth_radius # semi-major axis in meters
i = math.radians(i_in_degrees) # inclination
aop = math.radians(aop_in_degrees) # perigee argument
raan = math.radians(raan_in_degrees) # right ascension of ascending node
ta = math.radians(ta_in_degrees) # true anomaly
initialDate = AbsoluteDate(2025, 5, 9, 12, 0, 0.0, utc)
initialOrbit = KeplerianOrbit(a, e, i, aop, raan, ta, PositionAngleType.TRUE,
inertialFrame, initialDate, Constants.WGS84_EARTH_MU)
# Attitude provider: aligned with orbital frame
lof = LOFType.VNC
attitudeLaw = LofOffset(inertialFrame, lof)
# Create propagator
propagator = KeplerianPropagator(initialOrbit, attitudeLaw)
finalDate = initialDate.shiftedBy(4 * 3600.0)
# Propagate and collect states
step = 60.0 # 60 seconds step
spacecraft_states = []
currentDate = initialDate
while currentDate.compareTo(finalDate) <= 0:
state = propagator.propagate(currentDate)
spacecraft_states.append(state)
currentDate = currentDate.shiftedBy(step)
my usage of the stateToTLE method :
def a_without_earth_radius_to_mean_motion(a):
n_rad_s = math.sqrt(Constants.WGS84_EARTH_MU / (a+Constants.WGS84_EARTH_EQUATORIAL_RADIUS)**3)
return n_rad_s * 86400 / (2 * math.pi)
L_TLE = list()
fixedPoint = FixedPointTleGenerationAlgorithm()
norad_id = 99985 # Dummy norad
TLE_template = TLE(
f"1 {norad_id}U {norad_id}A 25129.50000000 .00000000 00000-0 00000-0 0 10",
f"2 {norad_id} {format(i_in_degrees, '0>7.4f')} {format(raan_in_degrees, '0>8.4f')} {format(int(e*1e7),'0>7')} {format(aop_in_degrees, '0>8.4f')} 000.0000 {format(a_without_earth_radius_to_mean_motion(a_in_meters_without_earth_radius), '0>11.8f')}000000"
)
for state in spacecraft_states:
L_TLE.append(TLE.stateToTLE(state, TLE_template, fixedPoint))
And I extract the Keplerian elements from the spacecraft states and from the TLEs in the following ways, to compare them (see the attached excel file) :
# Spacecraft states data
L_a = list()
L_e = list()
L_i_rad = list()
L_pa_rad = list()
L_raan_rad = list()
L_ma_rad = list()
for state in spacecraft_states:
orbit = KeplerianOrbit(state.getOrbit())
L_a.append(orbit.getA())
L_e.append(orbit.getE())
L_i_rad.append(orbit.getI())
L_pa_rad.append(orbit.getPerigeeArgument())
L_raan_rad.append(orbit.getRightAscensionOfAscendingNode())
L_ma_rad.append(orbit.getMeanAnomaly())
L_i = [e * 180/math.pi for e in L_i_rad]
L_pa = [e * 180/math.pi for e in L_pa_rad]
L_raan = [e * 180/math.pi for e in L_raan_rad]
L_ma = [e * 180/math.pi for e in L_ma_rad]
# TLE data
L_a_TLE = [mean_motion_to_a(TLE.getMeanMotion()) for TLE in L_TLE]
L_e_TLE = [TLE.getE() for TLE in L_TLE]
L_i_rad_TLE = [TLE.getI() for TLE in L_TLE]
L_pa_rad_TLE = [TLE.getPerigeeArgument() for TLE in L_TLE]
L_raan_rad_TLE = [TLE.getRaan() for TLE in L_TLE]
L_ma_rad_TLE = [TLE.getMeanAnomaly() for TLE in L_TLE]
L_i_TLE = [e * 180/math.pi for e in L_i_rad_TLE]
L_pa_TLE = [e * 180/math.pi for e in L_pa_rad_TLE]
L_raan_TLE = [e * 180/math.pi for e in L_raan_rad_TLE]
L_ma_TLE = [e * 180/math.pi for e in L_ma_rad_TLE]
compare_elements.xlsx (68.9 KB)
That “noise” I end up with is very bothersome for what I’m trying to achieve as a final result, as it creates a lot of uncertainty - is that a known issue/behaviour and/or am I missing something? Is it possible to fix this, or maybe I’m not working the right way?
Thank you very much for the possible explanations you would give me!