Get TLE after propagation

Hello,

I have been trying to verify TLE propagation by comparing different entries for a satellite in space-track. I want to input the TLE for the earlier entry, propagate to the time of the second entry, and compare the TLE for the second entry with the propagation. I haven’t been able to find a good resource for getting a TLE after propagation. Should I split up the the propagation into steps and get a series of states to put into a FiniteDifferencePropagatorConverter? Is there a different/more straightforward way to do this comparison?

Hi,

Welcome to Orekit! I’m not sure I understand what you’re trying to do. In general one cannot change the epoch of a TLES and expect it to represent the same trajectory. Solving for a new TLES at a different epoch would generate a TLES that represents a similar but different trajectory, maybe that is what you’re trying to do? Perhaps a better comparison would be to compare position and velocity at a common epoch or over some time span.

Regards,
Evan

1 Like

Hi Evan,

I think I didn’t explain my issue as well as I had originally thought. What I want to do is take a TLE entry for a satellite (from space-track or a similar service), propagate it forward to the time for a later TLE entry for that same satellite and compare the TLE output from propagation with the TLE entry found on space-track. For example, if I have the following TLE entries on space-track:

val tle1 = new TLE(“1 25162U 98008A 20110.56766598 -.00000059 +00000-0 +28881-3 0 9992”,
“2 25162 052.0049 057.9913 0001365 276.3042 256.0128 12.38117809010253”)

val tle2 = new TLE(“1 25162U 98008A 20111.09707350 -.00000053 00000-0 33586-3 0 9994”,
“2 25162 52.0050 56.4507 0001360 277.7211 95.4018 12.38117894 10433”)

which represent the same satellite at different points in time, both taken from space-track. I want to take tle1, propagate it forward to the time in tle2, produce a TLE from that propagation, and compare with the actual tle2. Does that make sense?

Hi,

It make sens if your TLE are not too far in time.
To do what you want you can to follow the following steps (with the explanations):

  1. Build your TLEs

     final TLE tle1 = new TLE("1 25162U 98008A   20110.56766598 -.00000059  00000-0 -28881-3 0  9992",
                              "2 25162 052.0049 057.9913 0001365 276.3042 256.0128 12.38117809010253");
     final TLE tle2 = new TLE("1 25162U 98008A   20111.09707350 -.00000053  00000-0 -33586-3 0  9994",
                              "2 25162  52.0050  56.4507 0001360 277.7211  95.4018 12.38117894 10433");
    
  2. Propagate the first TLE to the date of the second TLE

     final AbsoluteDate date1 = tle1.getDate();
     final AbsoluteDate date2 = tle2.getDate();
     final TLEPropagator propagator = TLEPropagator.selectExtrapolator(tle1);
     final SpacecraftState stateAtDate2 = propagator.propagate(date2);
    
  3. You can convert the output orbit to a keplerian orbit

     final KeplerianOrbit keplerianAtDate2 = (KeplerianOrbit) OrbitType.KEPLERIAN.convertType(stateAtDate2.getOrbit());
    
  4. Build a FinitDifferencePropagatorConvertor. You can initialize meanMotion, e, i, pa, raan, meanAnomaly parameters to the previous Keplerian elements (even if they differ from the mean elements). Moreover, you can set Set meanMotionFirstDerivative, meanMotionSecondDerivative, bStar to zero. bStar will be estimated.

     final FiniteDifferencePropagatorConverter converter =
                     new FiniteDifferencePropagatorConverter(new TLEPropagatorBuilder(
                            new TLE(tle1.getSatelliteNumber(),
                                    tle1.getClassification(),
                                    tle1.getLaunchYear(),
                                    tle1.getLaunchNumber(),
                                    tle1.getLaunchPiece(),
                                    tle1.getEphemerisType(),
                                    tle1.getElementNumber(),
                                    keplerianAtDate2.getDate(),
                                    keplerianAtDate2.getKeplerianMeanMotion(),
                                    0.,
                                    0.,
                                    keplerianAtDate2.getE(),
                                    keplerianAtDate2.getI(),
                                    keplerianAtDate2.getPerigeeArgument(),
                                    keplerianAtDate2.getRightAscensionOfAscendingNode(),
                                    keplerianAtDate2.getMeanAnomaly(),
                                    tle1.getRevolutionNumberAtEpoch(),
                                    0.),
                            PositionAngle.TRUE,
                            1.0),
                                                             1.e-3, 1000);
    
  5. Build a numerical propagator for sample generation.

  6. Build a TLE propagator from the finite difference propagator builder. That’s here where the bStar is declared as an estimated parameter.

     final double timeSpan = 60.0;
     final int nbOrbit = 3;
     final int nbPoints = (int) (keplerianAtDate2.getKeplerianPeriod() * nbOrbit / timeSpan);
     final TLEPropagator propagator2 = (TLEPropagator) converter.convert(numProp, timeSpan,  nbPoints, TLEPropagatorBuilder.B_STAR);
    
  7. Get the TLE a the date of tle2.

     final TLE tle2Built = propagator2.getTLE();
    

I hope my explanations can help you,

Regards,
Bryan

Hi Bryan,

That was very helpful. I seem to be getting an error, which I think is from the numerical propagator so I must be doing it wrong. I followed the tutorial steps to create it from the DormandPrince853Integrator, but that may be incompatible with the converter. Here is what I have at the moment (note I am working in Scala, so the syntax is slightly different):

val minStep = 0.001
val maxstep = 1000.0
val positionTolerance = 10.0
val propagationType = OrbitType.KEPLERIAN
val tolerances = NumericalPropagator.tolerances(positionTolerance, keplerianAtDate2, propagationType)
val integrator = new DormandPrince853Integrator(minStep, maxstep, tolerances(0), tolerances(1))
val numProp = new NumericalPropagator(integrator)


val timeSpan = 60.0
val nbOrbit = 3
val nbPoints = (keplerianAtDate2.getKeplerianPeriod * nbOrbit / timeSpan).asInstanceOf[Int]
val propagator2 = converter.convert(numProp, timeSpan, nbPoints, TLEPropagatorBuilder.B_STAR).asInstanceOf[TLEPropagator]

val tle2Built = propagator2.getTLE()

EDIT: I fixed the problem. The output that I received for the built TLE is:

1 25162U 98008A 20111.09707350 .00000000 00000-0 -17131-0 0 9991
2 25162 52.0047 56.4506 0001126 284.3866 88.7364 12.38107787 10257

And the actual TLE is:

1 25162U 98008A 20111.09707350 -.00000053 00000-0 33586-3 0 9994
2 25162 52.0050 56.4507 0001360 277.7211 95.4018 12.38117894 10433

Are the differences I see to be expected? Or does it indicate that I should have used different parameters?

Hi,

To improve the accuracy, you can add some force models to your numerical propagator (here you are only considering the Newtonian attraction). For instance, you can add:

  1. Perturbing gravity field

     final NormalizedSphericalHarmonicsProvider provider = GravityFieldFactory.getNormalizedProvider(4, 4);
     final ForceModel gravity = new HolmesFeatherstoneAttractionModel(FramesFactory.getITRF(IERSConventions.IERS_2010, true), provider);
    
  2. Atmospheric drag with an Harris-Priester atmosphere model

     final HarrisPriester atmosphere = new HarrisPriester(CelestialBodyFactory.getSun(), new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING,  FramesFactory.getITRF(IERSConventions.IERS_2010, false)));
     final double cd = 1.0;
     final double area = 1.0;
     final IsotropicDrag spacecraft = new IsotropicDrag(area, cd);
     final DragForce drag = new DragForce(atmosphere, spacecraft);