Inter-Satellite Link Computation

Hello community. I have set out to build an intersatellite detector for a constellation. For now this is a small constellation, but ideally there would be no limit. I am aware of the existance of a Parallizer to run simultaneous propagators. However I have 2 issues regarding my task.

  1. I am not entirely sure how to set up the parallizer. I have read on the forum some but couldnt find anything explicit about setting up the methods.
  2. I have read also that there are issues setting up event detectors in the classical way (i.e. with a single propagator). I have detectors for areas and locations, and I need to include satellite detectors. My thought was to create a point and FOV for the inter-sat visibility on the other satellite’s coordinates, but I am not sure if it is possible to access the coordinates at every time-step within the propagator.

Any help would be appreciated. Thank you!

Hi @francoC,

Although i have not used the propagator parallelizer yet, i can give you a piece of advice. As your intersatellite detector will need the PV of mulitple satellites simultaneously, i suggest that you run generate Ephemeris for each satellite of your constellation beforehand and then use your custom detector on these ephemeris. This way, you’ll avoid giving one propagator to the other causing a sort of propagation recursivity that can go wrong in so many ways, especially when parallelizing.

So this is how i would do this but this may not be the optimal way :

  1. Use PropagatorsParallelizer a first time to generate Ephemeris for each satellite using multi-thread (no event detector)
  2. For each satellite (i.e. ephemeris), add your interSatellite detector with every other ephemeris
  3. Use PropagatorsParallelizer again with your list of Ephemeris to get your events

I’m afraid i can’t give you a more detailed description than this right now but i hope this will help you a little bit.


1 Like

Thank you for the response and I apologize for the delayed reply. I had a little but of time off so I havent worked on this just yet. I will start soon and will update you on my results. Thank you!


Hello Vincent, your reply was very helpful and I was able to find all the methods I believe I need, however I am a bit rusty on Orekit’s logic flow. I use the “TLEPropagator” class for propagation. Below is what I think the pseudocode looks like for the flow.

  1. propagators = PropagatorsParallelizer(List of TLEPropagator, MultiStepHandler)
  2. states = propagators.propagate(start_date, end_date)
  3. ephemer = ephemeris(states, # of points, extrapolationThreshold, nadirPointing)
  4. add all events to ephemer

Is this correct, or am I wrong in some aspects/missing steps? I am in doubt whether or not I can replace the “Propagator” object with a “TLEPropagator” object in line 1. I am also not entirely sure about the second input in line 1 “MultiStepHandler”. My current logic is as follows, so I’m not sure if the above steps are correct (currently without inter-sat links of course).

  1. define TLEPropagator
  2. add attitude provider to TLEPropagator
  3. add events to TLEPropagator
  4. extrapolate to final_date every stepT time steps inside a for loop
  5. repeat for next satellite

Most importantly, currently I add the events prior to propagation (unless the fact of adding an event is what triggers the propagation and step 4 is redundant), while in the new method I would be adding the events prior to propagation. Any thoughts?

Hi @francoC,

You were close the solution, i think it would be best to do the following (in general, not necessarily for TLE which, in fact simplify the process):

  1. propagatorParallelizer = PropagatorsParallelizer(List of TLEPropagator, MultiStepHandler)
  2. propagators = propagatorParallelizer.getPropagators()
  3. generators = “get list of ephemeris generators from each of your propagator”
  4. propagatorParallelizer.propagate(start_date, end_date)
  5. ephemer = “get generated ephemeris from each of your ephemeris generator”
  6. add all events to ephemer (created with your initial TLEPropagator and not the propagators extracted from the parallizer )
  7. either recreate a new propagator parallelizer to reduce computing time or simply propagate all you ephemeris one by one
  8. Enjoy your awesome results !

Now regarding your case with TLEs, it is an analytical propagator so you don’t need to create an ephemeris beforehand as you would when using an integrated propagator. In your case you can do :

  1. propagatorParallelizer = PropagatorsParallelizer(List of TLEPropagator, MultiStepHandler)
  2. propagators = propagatorParallelizer.getPropagators()
  3. add all events to each propagator (created with your initial TLEPropagator and not the propagators extracted from the parallizer )
  4. propagatorParallelizer.propagate(start_date, end_date)
  5. Enjoy your awesome results !

TLEPropagator is considered a Propagator so you don’t need to worry about that.

The MultiStepHandler is simply something you can use to print things during the process or store data to be output later. You can create your own MultiSatStepHandler class that does nothing. This is not really needed in your case.

Feel free to ask any questions if this is not clear enough.

BEWARE : Don’t expect high precision coming from TLEs when propagating them for more than ~1 day


1 Like


Thank you again for your help. A couple questions.

  1. In step 3, do I add the events as normal, as if I was running a single propagator without a a parallelizer? If I include the intersatellite links, would I still simply add it in to each TLEPropagator?

  2. In the first algorithm you made, I am curious about the logic. So I declare the propagators, add them to the parallelizer, and then I use the getPropagators() method to retrieve them. Step 3-5 are a bit confusing. It seems in step 3 I obtain an ephemeris generator, then I propagate, and finally retrieve the ephemeris. But wouldn’t this mean that the ephemeris generator would be unpropagated? Similarly in step 6 the events are added after the propagation, so would they not be unchecked after the propagation?

PS: I am aware of the TLE uncertainty beyond a day or two. I am working on adding an unscented kalman filter, however its a bit tricky. I haven’t given it much thought about integrating the UKF to the process discussed in this thread, however thinking about it now I probably should before I continue developing. Especially since this algorithm is considering an initial TLE when in reality the UKF would spit out a list of PV states.

Thank you so much for the help,

Hi @francoC and sorry for the wait,

Indeed, you would add all the inter-satellite links with all other needed satellites to each propagator as if you were running one propagator at a time.

Thanks to your question and after re-reading my answer and the documentation, i have realized that i was mistaken, sorry about that :sweat:.

This is what you want to do, you are going to stick to the first, more generic algorithm and i will explain why later on :

  1. generators = “get list of ephemeris generators from each of your TLE propagator”
  2. propagatorParallelizer1 = PropagatorsParallelizer(List of TLEPropagator, MultiStepHandler)
  3. propagatorParallelizer1.propagate(start_date, end_date)
  4. ephemer = “get generated ephemeris from each of your ephemeris generator”
  5. add all events to each ephemeris (created from your initial TLEPropagators and not from the other ephemeris)
  6. propagatorParallelizer2 = PropagatorsParallelizer(ephemer, MultiStepHandler)
  7. propagatorParallelizer2.propagate(start_date, end_date)
  8. retrieve and output data thanks to the handler that you will have added with each of your interSatellite detector at step 6

So why bother creating ephemeris when you have analytical propagator you may ask ? This is because of the following part of the PropagatorsParallelizer documentation :
This class does not provide multi-satellite events. As events may truncate steps and even reset state, all events (including multi-satellite events) are handled at a very low level within each propagators and cannot be managed from outside by the parallelizer. For accurate handling of multi-satellite events, the event detector should be registered within the propagator of one satellite and have access to an independent propagator (typically an analytical propagator or an ephemeris) of the other satellite. As the embedded propagator will be called by the detector which itself is called by the first propagator, it should really be a dedicated propagator and should not also appear as one of the parallelized propagators, otherwise conflicts will appear here.

In short, you do not want to use a propagator in your inter satellite detector that is also used inside you propagatorParallelizer

Hence in the newly presented algorithm, you will first generate ephemeris that will then use the TLEPropagators in their events detectors. Hence, when the ephemeris will be “propagated” with the new propagatorParallelizer2, they will use the analytical TLEPropagators in their events computations.

Once the propagator from which the generator has been extracted from has been propagated, the generator will have generated the ephemeris. They are sort of linked with each other even though we have save the generator before propagating.

So you may already have understood this from my answer but let me rephrase it. The first time we are propagating is simply to create an ephemeris so we do not want any detector at this point. Then, you can use either the generated ephemeris OR the TLEPropagators to create your events.

What i want to emphasize here is that you cannot create events detectors based on your TLEPropagators and add them to your TLEPropagators, or similarly, create events detectors based on your ephemeris and add them to your ephemeris. You need to do a mix so that, when the ```PropagatorParallelizer```` starts the propagation, your parallelized propagators and the propagators used inside your inter satellites detectors are different.

I hope that i have answered your questions clearly this time and once again, i apologize for being misleading in my previous answers.



Sorry for the late reply, I have been working on other things. Everything you said makes total sense, thank you so much. I am working on the code right now. Although the ephemeris object seems to be a BoundedPropagator object, the propagator parallelizer was not liking this. So I made it a Propagator object instead and no errors popped up. Two more comments (for now lol):

  1. given that I am working with TLEs generated at different epochs, when I propagate the parallelizers, what dates should I use?

  2. Below is a code I obtained from someone who posted in the forum for a step handler. Would this serve my purposes. I am not entirely sure what it does, and if it is even useful.

Thank you,

public class DistanceHandler implements MultiSatStepHandler {

private final double displayStep;
private AbsoluteDate next;

public DistanceHandler(final double displayStep) {
    this.displayStep = displayStep;

public void init(List<SpacecraftState> states0, final AbsoluteDate t) {
    // ensure that the first time handleStep is called, it will display something
    next = null;

public void handleStep(List<OrekitStepInterpolator> list) {


public void handleStep(List<OrekitStepInterpolator> interpolators, boolean isLast) {

    final OrekitStepInterpolator i0 = interpolators.get(0);
    final OrekitStepInterpolator i1 = interpolators.get(1);

    // we arbitrarily use the frame from first propagator to compute geometry
    final Frame frame = i0.getPreviousState().getFrame();

    if (next == null) {
        // this is the first time we are called
        next = i0.getPreviousState().getDate();

    // we check date only on interpolator 0 because we know all interpolators are already synchronized
    while (next.isBetweenOrEqualTo(i0.getPreviousState().getDate(),
            i0.getCurrentState().getDate())) {

        // we now know next is a date that is in the current common part of all interpolators,
        // so we can use this date to retrieve positions of all spacecrafts
        final Vector3D p0 = i0.getInterpolatedState(next).getPVCoordinates(frame).getPosition();
        final Vector3D p1 = i1.getInterpolatedState(next).getPVCoordinates(frame).getPosition();
        System.out.format(Locale.US, "%s %12.3f%n", next, Vector3D.distance(p0, p1));

        // prepare date of next display
        next = next.shiftedBy(i0.isForward() ? +displayStep : -displayStep);



Hi Franco,

No worries about that. You are necessarily going to run the simulation between a start and a final date. Of course, the results will not be perfect the further in time you are with respect to your TLEs but this is an approximate analytical model anyway so I believe it will not matter much.

You talk about the step handler in order to output of the results, right ? If this is indeed the case, i suggest you to create an EventHandler specific to your inter satellite detector. This way, you will associate each inter satellite detector with a custom instantiation of your specific handler.

Below is a very quick example of what you could do:

public class InterSatDirectViewHandler implements EventHandler<InterSatDirectViewDetector> {

    private StringBuilder dataToOutput;
    private boolean       inVisibility = false;
    private AbsoluteDate  enteringVisibilityDate;

    public void init(SpacecraftState initialState, AbsoluteDate target, InterSatDirectViewDetector detector) {
        dataToOutput           = new StringBuilder();
        enteringVisibilityDate = initialState.getDate();

    public Action eventOccurred(SpacecraftState spacecraftState,
                                InterSatDirectViewDetector interSatDirectViewDetector,
                                boolean increasing) {

        // The Earth is not eclipsing both satellites anymore and so, we enter visibility
        if (increasing) {
            enteringVisibilityDate = spacecraftState.getDate();
            inVisibility           = true;
        // The Earth is now occulting the other satellite and so, we end visibility -> we save this interval
        else {
            inVisibility = false;

        return Action.CONTINUE;

     * In case we stopped the propagation during a visibility. We add this truncated visibility to the output.
     * @param finalDate final propagation date
    public void finalize(final AbsoluteDate finalDate) {
        if (inVisibility) {
            inVisibility = false;
    public void outputData(final File outputFile) {
        // Output the data however you want

Hope it helps you.



Thank you again for your support. I will take a look at that code snippet. I have used event handlers before for my other location-based event detectors, but since inter-sats are a bit different I’ll take a look. However, I was referring to the propagator parallelizer MultiSatStepHandler object.

Regarding the epoch, my question was related to the fact that for example TLE_1 has an epoch 09/23/23, while TLE_2 might have epoch 09/24/23. So given this difference, will the parallelizer propagate TLE_2 from its starting date, or how would it manage this difference in epoch?

Thank you,

Hi Franco,

Yes i had understood but i am not sure i understand why you want to use this MultiSatStepHandler in particular. From what i understand, it will not serve any previously mentioned purpose so i may be lacking information. If the goal is to store inter sat visibility event, then you should use an EventHandler for each of you detector.

The parallelizer will propagate all TLEs to the starting date and then forward to the final date. It does not matter that the TLEs are not expressed at the same date. However, as mentioned previously, the precision of the results will depend on each TLE.



The purpose of this project is to take in TLEs for multiple satellites, and compute visibiity events to several target locations, as well as inter-satellite links. Then, I upload the events to a database that stores the dates related to tags, which in my case would be “sat_x” or “location_x”, etc. The event handler I have for the ground station detector is the following:

withHandler((s, detector, increasing) → {
if (increasing) {
//System.out.println(s.getDate() + " " + s.getPVCoordinates(inertialFrame).getPosition());
} else {
//System.out.println(s.getDate() + " " + s.getPVCoordinates(inertialFrame).getPosition());
return increasing ? Action.CONTINUE : Action.STOP;

Regarding inter-sat links, I need to retrieve the start and end date for the events of the satellite, and then I want to check the distance between satellites throughout the duration of the event. Since I’ll have the ephemeris, all I need would be the dates. From what I understand, the handler I attached above would do this too right? Is the MultiStepHandler in the parallelizer definition this same handler?

If you want to monitor all events, you should always return Action.CONTINUE, otherwise the propagation will be stopped at end of visibility.

The MultiStepHandler is a different handler. This one is for event handling on one propagator, whereas the MultiStepHandler is for step handling on several propagators at once.

Hi Franco and sorry for the delay,

As Luc said, you’ll need to return Action.CONTINUE.

Indeed, the detector will be basically the same (with Action.CONTINUE of course ). The major difference here is that you don’t need to add too many detectors. Consider 3 satellites , sat1, sat2 and sat3 : you will only need to add the following detectors:

Inter sat visibility detector between 1 and 2
Inter sat visibility detector between 1 and 3
Inter sat visibility detector between 2 and 3

I think you ger the idea.



Yes I get what you are saying, that will definitely make my propagation more efficient. I have the return Action.CONTINUE line as show in the code snippet I included above, or is this something else I haven’t included?

However, the question remains about the step handler for the parallelizer. I am not sure what I should make this, and if the code I provided a few comments before is enough for my purposes, as I can’t exactly understand what it is doing.

I have run into a bigger problem. My code runs smoothly through accesing ephemeris and setting event detectorrs, however as soon as I run the parallelizer with the ephemeris propagators, I run into a StackOverflowError.

Thank you,


I have been playing around with it to try to figure out why I am getting the recursive condition. I’ve narrowed down the issue to the line where I propagate the ephemeris of all the satellites. However, I am not entirely sure what the issue is. I’ll attach my code below, however the project is fairly complex with a lot of objects and classes, so it might not be runnable. I’ve polished it up a bit so its easy to understand. From what I read in the propagatorsParallelizer documentation, I should avoid having objects in common between the propagators, and I think I’ve done this properly, but maybe not. My only doubt is when I add the event detector. Any thoughts on what the issue could be?

        for (TLE tle : TLE_list) {
            TLEPropagator Prop = TLEPropagator.selectExtrapolator(tle);
            Prop.setAttitudeProvider(new NadirPointing(FramesFactory.getEME2000(),
                    new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING,
                            FramesFactory.getITRF(IERSConventions.IERS_2010, true))));

        AbsoluteDate begin_date = TLE_list.get(1).getDate();
        AbsoluteDate final_date = begin_date.shiftedBy(simTime); // End date of propagation(Orekit)

        // propagate parallelizer to generate all ephemeris for all satellites
        PropagatorsParallelizer propagatorParallelizer1 = new PropagatorsParallelizer(propagators, new DistanceHandler(60));
        propagatorParallelizer1.propagate(begin_date, final_date);

        // retrieve ephemeris data from all generators
        for (EphemerisGenerator ephemerisGenerator : ephemerisGenerators) {

         // create lists containing lists of events for all satellites
        List<List<EventDetector_Constructor.Event_Manager>> ISL_events = new ArrayList<>(); // list of ground station detections

        for (int i = 0; i < allEphemeris.size(); i++) {


            // list of all events for each satellite
            List<EventDetector_Constructor.Event_Manager> ISL_events_current = new ArrayList<>(); 

            // Common parameters for GS/UE/TA event detector
            final double maxcheck = 60;  // Checking every x second
            final double threshold = 0.001; //Convergence to 0.001 seconds

            for (int j = 0; j<TLE_list.size(); j++) {
                if (TLE_list.get(i).getSatelliteNumber() == TLE_list.get(j).getSatelliteNumber()) {
                EventDetector_Constructor.Event_Manager add_event_detector = EventDetector_Constructor.getEvent_ISL(maxcheck, threshold
                        , String.valueOf(TLE_list.get(j).getSatelliteNumber()), allEphemeris.get(j)); // constructs event
                allEphemeris.get(i).addEventDetector(add_event_detector.event_detector); // propagates event
                ISL_events_current.add(add_event_detector); // adds event to list
            System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ISL Windows Computed for sat " + (i + 1) + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");

        PropagatorsParallelizer propagatorsParallelizer2 = new PropagatorsParallelizer(allEphemeris, new DistanceHandler(60));
        propagatorsParallelizer2.propagate(begin_date, final_date);

The event dectector constructor is as follows, with start, end, and order being values I use to output data. They are properties of the Event_Manager object.

                final InterSatDirectViewDetector isl = new InterSatDirectViewDetector(earth, secondarySat);

                final EventDetector stationVisibility = isl.withMaxCheck(maxcheck).withThreshold(threshold).
                        withHandler((s, detector, increasing) -> {
                            if (increasing) {
                            } else {
                            return increasing ? Action.CONTINUE : Action.STOP;

Hi Franco,

Sorry for the delay, i’ll try to look into it tomorrow.


Hi Fanco,

the problem is on these lines :

You are adding detectors to your ephemeris which are built using other ephemeris from your “allEphemeris” list, as a result, when one of your ephemeris is “propagated” (in this case it is simply using interpolation), it is “propagating” other ephemeris within the detectors which are themselves propagating other ephemeris in their detectors and so on. Consequently, you have an infinite recursivity which is throwing you an error.

To fix this, use your TLEs to build your detectors instead of your ephemeris.

From my understanding, you don’t need to use a MultiSatStepHandler. Just fix your code as it is and it should meet your needs.

You should remove the Action.STOP, otherwise the propagation will stop prematurely.

Good luck :slight_smile: !



This might be an issue with my understanding of the JAVA logic I think. So you’re saying I should add the detectors to the TLEPropagator object on that first for loop?

In JAVA…say I have a list of objects (allEpehemeris), which themselves are tied to another object (TLEPropagator), if I modify the TLEPropagator after allEphemeris has been declared (as in the case of my code and what you said the fix would be), the objects in that list are modified? My background is Matlab so the whole JAVA logic flow confuses me.

The recursion is gone now, thank you! For some reason no events are being found, which makes no sense for a 10 day propagation. I am looking into it, hopefully I find the reason.

Thank you,

Hi Franco,

It is indeed a bit confusing.

The objects, in this case, are not really tied together as you might think. Let’s look at the “allEpehemeris” you generated, as the “allEpehemeris” has been generated beforehand, it is now completely independant from the “TLEPropagators” so re-running the first propagation with the “TLEPropagators” would not change the “allEpehemeris” list.

Now let’s take the first ephemeris of the “allEpehemeris” list and add inter-satellite detectors to it. We are going to create the detectors like this (if we were to do it detector by detector, this is for explaination purpose) :

detectorWithSecondSat = InterSatDirectViewDetector(earthShape, secondSatTLE);
detectorWithThirdSat = InterSatDirectViewDetector(earthShape, thirdSatTLE);

And so on…

As the ephemeris and the TLEs are completely independant from each other, you will experience no infinite recursivity and it should work properly.

Hence, keep generating the ephemeris the same way you are already doing it and only add the detectors to the ephemeris before the second propagation (with detectors built using the TLEs !)