Computing TLE orbit count using TimeSpanMap

Newbie question: I need to compute future (or past) orbit count (satellite crossing ascending node) using a TLE (which contains an initial orbit count). It seems that TLEPropagator and TimeSpanMap will feature in the solution. Can anyone offer an outline for piecing this together? Thanks!

Hello @DarinKing and welcome to the Orekit forum !

You are indeed right about the classes needed in your case :+1: .

I think you are not the only one needing this so you will find an example below :

package org.example;

import org.hipparchus.ode.events.Action;
import org.orekit.data.DataContext;
import org.orekit.data.DataProvider;
import org.orekit.data.DirectoryCrawler;
import org.orekit.propagation.Propagator;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.analytical.tle.TLE;
import org.orekit.propagation.analytical.tle.TLEPropagator;
import org.orekit.propagation.events.EventDetector;
import org.orekit.propagation.events.EventSlopeFilter;
import org.orekit.propagation.events.FilterType;
import org.orekit.propagation.events.NodeDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.utils.Constants;
import org.orekit.utils.TimeSpanMap;

import java.io.File;

public class Main {
    public static void main(String[] args) {
        // Load orekit data
        loadOrekitData();

        // TLE to work with
        final TLE tle = new TLE("1 25544U 98067A   24122.22496221  .00014946  00000-0  26195-3 0  9999",
                                "2 25544  51.6364 188.2590 0003600 126.3014 233.8308 15.50791225451268");

        // Create associated propagator
        final Propagator propagator = TLEPropagator.selectExtrapolator(tle);

        // Node crossing event detector
        final EventDetector rawDetector = new NodeDetector(propagator.getFrame());

        // Filter only ascending node crossing and add handler
        final OrbitCounterHandler orbitCounterHandler = new OrbitCounterHandler(tle);
        final EventDetector ascendingNodeCrossingDetector =
                new EventSlopeFilter<>(rawDetector, FilterType.TRIGGER_ONLY_INCREASING_EVENTS).withHandler(
                        orbitCounterHandler);

        // Add detector to propagator
        propagator.addEventDetector(ascendingNodeCrossingDetector);

        // Propagate
        final SpacecraftState propagatedState = propagator.propagate(tle.getDate().shiftedBy(Constants.JULIAN_DAY));

        // Display registered orbits
        final TimeSpanMap<Integer> orbitCountMap = orbitCounterHandler.getOrbitCountMap();
        TimeSpanMap.Span<Integer>  span          = orbitCountMap.getFirstSpan();
        for (int i = 0; i < orbitCountMap.getSpansNumber(); i++) {
            System.out.format("Span number %d \n", i);
            System.out.format("Span starting date : %s\n", span.getStart());
            System.out.format("Span ending date : %s\n", span.getEnd());
            System.out.format("Span duration : %8.2f s\n", span.getEnd().durationFrom(span.getStart()));
            System.out.format("Orbit count : %d\n", span.getData());
            System.out.println();
            span = span.next();
        }

    }

    private static class OrbitCounterHandler implements EventHandler<EventDetector> {

        /**
         * Time span map for orbit count.
         */
        private final TimeSpanMap<Integer> orbitCountMap = new TimeSpanMap<>(-1);
        /**
         * Previous orbit count.
         */
        private       int                  previousCount;

        /**
         * Constructor
         *
         * @param tle initial TLE
         */
        public OrbitCounterHandler(final TLE tle) {
            this.previousCount = tle.getRevolutionNumberAtEpoch();
            this.orbitCountMap.addValidAfter(previousCount, tle.getDate(), true);
        }

        @Override
        public Action eventOccurred(final SpacecraftState spacecraftState, final EventDetector eventDetector,
                                    final boolean b) {

            // Add new orbit count to time span map
            previousCount += 1;
            orbitCountMap.addValidAfter(previousCount, spacecraftState.getDate(), true);

            // Continue propagation
            return Action.CONTINUE;
        }

        /**
         * Get the orbit count map.
         *
         * @return orbit count maps
         */
        public TimeSpanMap<Integer> getOrbitCountMap() {
            return orbitCountMap;
        }
    }

    /**
     * Load the orekit data from defaut file path.
     */
    public static void loadOrekitData() {

        //Loading Data
        final File rootFile   = new File(System.getProperty("user.home"));
        final File orekitData = new File(rootFile, "orekit-data");
        if (!orekitData.exists()) {
            System.out.format("File %s was not found.%n", orekitData.getAbsolutePath());
        }
        final DataProvider dirCrawler = new DirectoryCrawler(orekitData);
        DataContext.getDefault().getDataProvidersManager().addProvider(dirCrawler);
    }
}

Some side notes for this example :

  • I’m using the TLEPropagator but in doing this, my solution is overkill because we could simply use the nodal period to compute the orbit number at any given date right away. However with this sample, you can use any kind of propagator.

  • The first span between -Infinity and first span date will contain -1 to show that it does not contain any interesting data.

  • After propagation, the last orbit number will be the one shown when requesting the orbit number between last node crossing event and + infinity so be careful. (I was not going to do all the work :slight_smile: )

Hope this help !

Cheers,
Vincent

UPDATE 1:
As @DarinKing mentioned below, this solution is not necessarily overkill for TLE because of the mean motion derivatives and Bstar coefficient implied by drag. Thank you !

UPDATE 2:
I forgotten to mention that this works for forward propagation only. We could add a boolean in the handler to specify if we are propagating forward or backward.

1 Like

Many thanks, @Vincent ! I look forward to giving this a try. Re: your comment: “my solution is overkill because we could simply use the nodal period to compute the orbit number at any given date right away.”
I have found this not to be the case due to drag implied by the mean motion derivatives, etc. specified by the TLE.

1 Like

Woopsy you are right. ! I had forgotten about this :sweat_smile:.

Thank you :+1:

This is working great for me. Thanks! Since you’ve been so kind, care to offer an approach for “Update 2”? :wink:

I’ll try but got a lot of things going on so worst case might be next week :sweat:. I’ll do something clean that we may be able to implement later on.

Cheers,
Vincent

1 Like