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 .
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 )
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.
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.
Woopsy you are right. ! I had forgotten about this .
Thank you
This is working great for me. Thanks! Since you’ve been so kind, care to offer an approach for “Update 2”?
I’ll try but got a lot of things going on so worst case might be next week . I’ll do something clean that we may be able to implement later on.
Cheers,
Vincent