Different results between TLE and KeplerianOrbit

Hi all!
I got the gp data from space-track.com is below:

    "CCSDS_OMM_VERS": "2.0",
    "CREATION_DATE": "2023-03-15T03:17:22",
    "ORIGINATOR": "18 SPCS",
    "OBJECT_ID": "1999-068A",
    "REF_FRAME": "TEME",
    "EPOCH": "2023-03-14T22:01:50.020320",
    "MEAN_MOTION": "14.59101772",
    "ECCENTRICITY": "0.00020310",
    "INCLINATION": "98.1038",
    "RA_OF_ASC_NODE": "144.6045",
    "ARG_OF_PERICENTER": "45.6311",
    "MEAN_ANOMALY": "69.0145",
    "EPHEMERIS_TYPE": "0",
    "NORAD_CAT_ID": "25994",
    "ELEMENT_SET_NO": "999",
    "REV_AT_EPOCH": "23591",
    "BSTAR": "0.00025206000000",
    "MEAN_MOTION_DOT": "0.00001141",
    "MEAN_MOTION_DDOT": "0.0000000000000",
    "SEMIMAJOR_AXIS": "7074.213",
    "PERIOD": "98.691",
    "APOAPSIS": "697.515",
    "PERIAPSIS": "694.641",
    "RCS_SIZE": "LARGE",
    "LAUNCH_DATE": "1999-12-18",
    "SITE": "AFWTR",
    "DECAY_DATE": null,
    "FILE": "3860643",
    "GP_ID": "228092687",
    "TLE_LINE0": "0 TERRA",
    "TLE_LINE1": "1 25994U 99068A   23073.91794005  .00001141  00000-0  25206-3 0  9996",
    "TLE_LINE2": "2 25994  98.1038 144.6045 0002031  45.6311  69.0145 14.59101772235912"

When I use this gp data to compute access, by TLEPropagator and KeplerianPropagator or NumericalPropagator, the access result is different. The TLE result is the same as STK’s result, and using KeplerianPropagator or NumericalPropagator is NOT same. In my submission, the TLE result is correct. But why the result is different, they use same gp data.
This is my script by KeplerianPropagator:

 public static void main(final String[] args) {
        try {

            // configure Orekit
            final File home       = new File(System.getProperty("user.home"));
            final File orekitData = new File(home, "orekit-data");
            if (!orekitData.exists()) {
                System.err.format(Locale.US, "Failed to find %s folder%n",
                System.err.format(Locale.US, "You need to download %s from %s, unzip it in %s and rename it 'orekit-data' for this tutorial to work%n",
                                  "orekit-data-master.zip", "https://gitlab.orekit.org/orekit/orekit-data/-/archive/master/orekit-data-master.zip",
            final DataProvidersManager manager = DataContext.getDefault().getDataProvidersManager();
            manager.addProvider(new DirectoryCrawler(orekitData));

            //  Initial state definition : date, orbit
            final double mu =  3.986004415e+14; // gravitation coefficient

            final double a = 7074213.;
            final double e = 0.00020310;
            final double i = FastMath.toRadians(98.1038);
            final double pa = FastMath.toRadians(45.6311);
            final double raan = FastMath.toRadians(144.6045);
            final double anomaly = FastMath.toRadians(69.0145);
            // second = 50.020320
            AbsoluteDate initialDate = new AbsoluteDate(2023, 3, 14, 22, 1, 50.020320, TimeScalesFactory.getUTC());
            final Orbit initialOrbit = new KeplerianOrbit(a, e, i, pa, raan, anomaly, PositionAngle.MEAN, FramesFactory.getEME2000(), initialDate, mu);

            // Propagator : consider a simple Keplerian motion (could be more elaborate)
            final Propagator kepler = new KeplerianPropagator(initialOrbit);

            // Earth and frame
            final Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
            final BodyShape earth = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS,

            // Station
            final double longitude = FastMath.toRadians(108.8381);
            final double latitude  = FastMath.toRadians(34.1516);
            final double altitude  = 0.;
            final GeodeticPoint station1 = new GeodeticPoint(latitude, longitude, altitude);
            final TopocentricFrame sta1Frame = new TopocentricFrame(earth, station1, "station1");

            // Event definition
            final double maxcheck  = 60.0;
            final double threshold =  0.001;
            final double elevation = FastMath.toRadians(5.0);
            final EventDetector sta1Visi =
                    new ElevationDetector(maxcheck, threshold, sta1Frame).
                    withHandler((s, detector, increasing) -> {
                        System.out.println(" Visibility on " +
                                           detector.getTopocentricFrame().getName() +
                                           (increasing ? " begins at " : " ends at ") +
                        return increasing ? Action.CONTINUE : Action.STOP;

            // Add event to be detected

            // Propagate from the initial date to the first raising or for the fixed duration
            AbsoluteDate startAbsolute = new AbsoluteDate(2023, 3, 15, 6, 21, 0, TimeScalesFactory.getUTC());
            AbsoluteDate endAbsolute = new AbsoluteDate(2023, 3, 16, 6, 21, 0, TimeScalesFactory.getUTC());
            final SpacecraftState finalState = kepler.propagate(startAbsolute, endAbsolute);
            System.out.println(" Final state : " + finalState.getDate().durationFrom(initialDate));

        } catch (OrekitException oe) {

The result by using TLE and TLEPropagator is

 Visibility on station1 begins at 2023-03-15T14:02:08.546282157851Z
 Visibility on station1 ends at 2023-03-15T14:13:15.0753189324598Z

And the KeplerianPropagator’s result is:

 Visibility on station1 begins at 2023-03-15T14:01:33.10323315769451Z
 Visibility on station1 ends at 2023-03-15T14:12:43.04132566023242Z

They differ by more than 30 seconds, and as time goes on, the differ becomes larger.

In addition, I use this orbit data(a, e, i, pa, raan, anomaly) to create a satellite with STK, then compute access. The result is same as KeplerianPropagator. I don’t understand why did this happen.

Thank you.

Hi @chenqiang,

Welcome to the community !!

Keplerian propagation is a two-bodies theory, only the central attraction coefficient is modelled.
TLE propagation uses SGP4 theory, it’s a mean model.
Quote from @bcazabonne answer on a recent post:

SGP4 model is an analytical propagation model taking into account the following perturbations:

  • J2 to J5 zonal harmonics
  • Sun/Moon attraction
  • Drag (based on the B* value)

Numerical propagation propagates osculating elements with a set of perturbing forces that has to be introduced by the user.

In short, these are different propagation models so it’s logical that your access time is different


I think the force model behind the SGP4 model (including its submodels e.g. for deep space) is not that straightforward to pinpoint.

Anyway, to be extra clear, the elements coming from spacetrack’s TLEs or OMMs are in the mean sense of the SGP4 theory. To use them with other propagators, you should first convert them into an osculating state, which in orekit is done for the former via the propagate method obtained with the result of TLE.selectPropagator(). For the latter (OMM), I’m not sure what the most straightforward way is though.


Thank you! @Serrof @MaximeJ
I think the reason for this problem is that, the elements coming from spacetrack’s OMMs can not use directly.

Finally, I got the TLE and OMM from satellite owner at the same epochtime:

"1 00001U 20065A   23045.54473380  .00000000  00000-0  26347-4 0  9990";
"2 00001  97.4149  96.6299 0015162 122.5900 237.9599 15.13992228    11";

I compute access using them with TLEProgator and NumericalPropagator, the results are almost same:

   Contact with revolution 8 from 2023-02-15T00:46:15.145Z to 2023-02-15T00:52:34.235Z
   Contact with revolution 9 from 2023-02-15T02:19:20.154Z to 2023-02-15T02:28:06.763Z
   Contact with revolution 15 from 2023-02-15T12:56:29.838Z to 2023-02-15T13:05:50.021Z
   Contact with revolution 16 from 2023-02-15T14:33:59.555Z to 2023-02-15T14:37:22.177Z
   Contact with revolution 8 from 2023-02-15T00:46:15.164Z to 2023-02-15T00:52:34.253Z
   Contact with revolution 9 from 2023-02-15T02:19:20.188Z to 2023-02-15T02:28:06.745Z
   Contact with revolution 15 from 2023-02-15T12:56:29.896Z to 2023-02-15T13:05:50.008Z
   Contact with revolution 16 from 2023-02-15T14:33:59.691Z to 2023-02-15T14:37:21.923Z

In this OMM, the Frame is EME2000. And the Frame of spacetrack’s OMMs is TEME. So, I think this is why they are different. But I don’t kown how to use spacetrack’s OMMs to compute.


Great !

You’re right. I’m sorry I just realized I completely misunderstood your first question :roll_eyes:
Indeed you cannot use Spacetrack OMM orbital elements directly in a numerical or keplerian propagator. As you and Romain said these elements are given in the mean sense of SGP4 theory.

So what exactly do you want to achieve ? Convert a Spacetrack OMM to an osculating orbit that you will be able to use directly as input of a numerical or keplerian propagator ?

I want to convert a Spacetrack OMM to an osculating orbit, and how can I do? Convert OMM’s orbital frame to EME2000?

Hi @chenqiang,

First as explained by @Serrof and in this post or this one TLE parameters cannot be used directly, even for a frame conversion. They are mean parameters in the sense of the SGP theory.

Once you’ve built a TLEPropagator from a TLE or an OMM, there are 2 methods that I know of:

  1. Use propagator.propagate(date): this will give you the osculating parameters in TEME at date.
    From there you can transform them to EME2000.
    Drawback is that you will get the osculating orbit in the sense of SGP. And if you reuse it in another propagator, the results will rapidly diverge from the TLE propagation.

  2. Convert the TLE propagator to the one you desire.
    This method will fit a propagator to some osculating states of the TLE on a user-defined time span.
    The returned propagator will be the one realizing the fit. The fit is done using a batch least-squares algorithm.
    It is similar to considering the TLE states as PV measurements and doing an orbit determination with these measurements.
    You can either use a PropagatorConvertor (see the tutorials for examples), or a BatchLSEstimator and do an orbit determination on a chosen list of TLE propagated states.

Does that help you ?

You are right! I did it.

I’ll try this. Thank you for your help! :smiley: