Creating a TLE from base code from GorgiAstro

I am attempting to create a TLE from a set of GPS data. I have code with which I generate an Ephemeris using the base from which I edited but my output is roughly the same. I also have a program that accepts data in Earth Centered Earth Fixed (GTOD) and converts is to the Earth Centered Inertia (GCRF) used in the program. It is clear that creating the Ephemeris is simple. However, I am new to Orekit and am unfamiliar with how to create a fit TLE from this data. Note: The base code is in Orekit version 10.2 and mine is updated to 11.2.

Hi @separnell

Welcome to the Orekit forum!

In Orekit, you have two possibilities to generate a TLE:

  1. Generate the exact TLE of a single orbit. It can be done calling the method TLE.stateToTLE(SpacecraftState, TLE)
  2. Generate a TLE that represents and fits an interval of data.

For the second point, which is I think what you want to do, you try something like below. If
your have access to the List<SpacecraftState> used to initialize the Ephemeris object, let sample be the List<SpacecraftState>.

builder = TLEPropagatorBuilder(tleTemplate, PositionAngle.TRUE, 1.0);
fitter = FiniteDifferencePropagatorConverter(builder, 0.001, 1000);
fitter.convert(sample, false);
prop = TLEPropagator.cast_(fitter.getAdaptedPropagator())
fitted_tle = prop.getTLE()

With tleTemplate is a TLE object used to initialize some values like the NORAD ID, the epoch of the TLE, etc. In other words, the keplerian elements of the tleTemplate can be equal to 0.0.

Best regards,
Bryan

Thanks @bcazabonne for your advice,

I’ve been trying to follow your recommendation have had problems creating the tleTemplate.

(Ran out of embedded media objects)

And I’m getting this error. I presumed there was something incorrect with my date formatting, so I did it differently and got a not implemented error .

Traceback (most recent call last):
File “C:\Users\separ\Desktop\New_Orekit_Orbit_Determination.py”, line 245, in
tleTemplate = TLE(11111, “U”, 2020, 222, “A”, 0, 333, AbsoluteDate(2020, 1, 1, TimeScale(utc)), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
NotImplementedError: (‘instantiating java class’, <class ‘org.orekit.time.TimeScale’>)

(Ran out of links… please bear with me as I try to become a level 1 user…)

What formatting should I be using for the date/ is there some other way to create the tleTemplate?

The problem is the conversion from integer to double. You should initialize the tleTemplate like that:

tleTemplate = TLE(11111, "U", 2020, 222, "A", 0, 333, ref_date, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0, 0.0)

Bryan

1 Like

That resolved my problem with creating the tleTemplate, I however then encountered the error message that the Jacobian matrix is singular. I have encountered this several times throughout working on this, but it usually fixes itself by just messing with values which doesn’t seem to be working here. What does this mean (from a code issue perspective, I know what a singular or degenerate matrix is mathematically), and how is it actually supposed to be fixed?

I see. Let “ephemeris” be your Ephemeris object, could you try initializing the tleTemplate like that.

tleTemplate = TLE.stateToTLE(ephemeris.propagate(date_start), tleTemplate)

Bryan

I think that’s because for TLE fitting the keplerian elements of the tleTemplate can’t be equal to 0 (while for the stateToTLE they can). So, that’s why I propose to use the stateToTLE method to initialize the template
Sorry for the wrong information in my first message…

1 Like

I initialized it and end up getting what I think is an error code, though it’s hard to tell as it’s longer than the terminal… here is a clip of it

I have figured out that it happens here in this line:

I used my estimated propagator (creating a spacecraft state alike to that which is required for stateToTLE) in my ephemeris code, could it have changed the nature of it?

I may also have the wrong list of spacecraft states. The one used is generated in the last code block above.

Quick update to anyone who may read and respond to this, I have realized that I did indeed have my spacecraft states incorrect and they are now the list of absolute pv coordinates that it should be, I also seem to need some list of “free parameters” and have otherwise been unable to figure out what this means. I am getting the IllegalArgumentException which makes sense as I am not listing any free parameters.

If it’s possible for you, I’ll need a sample of code in order to run you application and see what’s wrong. Because it’s hard to understand.

By default, all orbital parameters are fitted. freeParameters are additional parameters that you want to fit. For TLE fitting, it can be the B* parameter.

Bryan

I forgot I could do this now that I’m no longer a level 0 user…

New_Orekit_Orbit_Determination.py (22.2 KB)

And this accepts the Earth Centered Earth Fixed Frame, so here is a data set that can be run with it.

SimulatedObservationsOK.csv (8.1 KB)

The TLE fitting is at the bottom, but it needs all the code to run.

I just found this post:

Generation of TLE - Orekit development - Orekit

And it looks like the approach you are using used an Orbit object for the spacecraft states. The only difficulty is I don’t have an orbit as a spacecraft state, but rather an AbsolutePV coordinate. Is there a different approach that would be better to use, or is there some way to turn my PV coordinates into an orbit rather that I am just not seeing?

Hi,

I solved the argument error. The TLESpacecraftStates needs to be converted to a Java list. You can solve the issue like that:

# Convert to a list
states_list = ArrayList()
for state in TLESpacecraftStates:
    states_list.add(state)

tleTemplate = TLE(11111, "U", 2020, 222, "A", 0, 333, date_start, 0.0, -138.8546595251871, 0.0, 0.00029131750532795117, 98.24086439196059, 66.12659133154932, -55.56899656065682, 0.0, 0, 0.0)
tleTemplate = stateToTLE(estimatedPropagator.propagate(date_start), tleTemplate)
builder = TLEPropagatorBuilder(tleTemplate, PositionAngle.TRUE, 1.0)
fitter = FiniteDifferencePropagatorConverter(builder, 0.001, 1000)
fitter.convert(states_list, False, 'BSTAR')

I added the BSTAR parameter as free parameter. Indeed, I think it is interesting to fit it.

The convert method is now used but another exception is raised: MathIllegalStateException for the Q.R decomposition. This must be investigated.

Bryan

Ok, I solved the second issue.

Because GCRF is an inertial frame, you can use orbits instead of absolute coordinates. Indeed, absolute coordinates were implemented to create spacecraft states defined in non inertial states and to perform the computation in non inertial frame (e.g. trajectory around Lagragian points) Therefore, you can change your lines

pv = TimeStampedPVCoordinates(date, vector_gtod, velocity_gtod)
pv_gcrf = gtod.getTransformTo(gcrf, date).transformPVCoordinates(pv)
absolute_pv_gcrf = AbsolutePVCoordinates(gcrf, pv_gcrf)
TLESpacecraftStates.append(SpacecraftState(absolute_pv_gcrf))

by

from org.orekit.utils import Constants
from org.orekit.orbits import CartesianOrbit

pv = TimeStampedPVCoordinates(date, vector_gtod, velocity_gtod)
pv_gcrf = gtod.getTransformTo(gcrf, date).transformPVCoordinates(pv)
TLESpacecraftStates.append(SpacecraftState(CartesianOrbit(pv_gcrf, gcrf, date, Constants.WGS84_EARTH_MU)))

Best regards,
Bryan

Now I have an open question.

Because your initial need is to generate a TLE that fits your GPS data. Maybe you could perform a SGP4 (i.e., TLE-based) orbit determination?

It means that instead of using a numerical model during the orbit determination you can use the SGP4 model. At the end, you will obtain an estimated TLE fitted using the observations.
Also, I saw that your numerical model doesn’t consider any perturbation. Therefore, the accuracy will be very poor… And your fitted TLE will be very inaccurate.

Maybe you can try to replace your propagatorBuilder initialization by

tleTemplate = TLE(11111, "U", 2020, 222, "A", 0, 333, orbit_first_guess.getDate(), 0.0, -138.8546595251871, 0.0, 0.00029131750532795117, 98.24086439196059, 66.12659133154932, -55.56899656065682, 0.0, 0, 0.0)
tleInitialGuess = stateToTLE(SpacecraftState(orbit_first_guess), tleTemplate)
propagatorBuilder = TLEPropagatorBuilder(tleInitialGuess, PositionAngle.TRUE, estimator_position_scale)

And obtain the estimated (fitted) TLE by using

print(TLEPropagator.cast_(estimatedPropagator).getTLE())

and not the FiniteDifferencePropagatorConverter!

Just be careful that the outputs will be in TEME frame.

I didn’t try in your application because my Orekit Python version is 10.3 and this functionality was introduced in 11.0. But I think it can be very useful for your need. However, it’s only an advice, your free to try or not :slight_smile:

If you want to know more about SGP4 orbit determination you can look at this reference:

Bryan

Thank you for your advice!

In response to your open question, I didn’t do that simply because I didn’t know I could. So, I will investigate that. You mentioned that my numerical propagator doesn’t consider any perturbation… is that specifically in reference to the way I was trying to generate my TLE, or will that effect the accuracy of my Ephemeris as well as it uses a numerical propagator?

I was unable to get this line working:
print(TLEPropagator.cast_(estimatedPropagator).getTLE())

But in comparison with another programs output (FreeFlyer) which we are seeking an alternative to, changing the first three lines made a difference of about 10 meters in comparison which is about 50% closer. So, I think everything is now working. Thanks for all your help!