Compute contact windows between two satellites


Given two satellites as input (two TLEs), I want to be able to determine all possible time windows in which the satellites will be less than some predetermined distance apart with the caveat that the Earth cannot be in-between the satellites (blocking the line of sight).

I am brand new to orekit, so any help is appreciated. :slight_smile:

Here’s a potentially related thread I’ve looked at.

Hi @jcf

In order to know if the Earth is between your two satellites, I recommend you to use the Orekit event detector InterSatDirectViewDetector.

In addition, in order to detect when the distance between the two satellites is less than a user-defined value, you can create a InterSatDistanceDetector like that:

public class InterSatDistanceDetector extends AbstractDetector<InterSatDistanceDetector> {

    /** Threshold for the distance between the two satellites. */
    private final double distance;

    /** Coordinates provider for the secondary satellite. */
    private final PVCoordinatesProvider secondary;

     * Constructor.
     * @param distance threshold for the distance between the two satellites in meters
     * @param secondary provider for the secondary satellite
    public InterSatDistanceDetector(final double distance, final PVCoordinatesProvider secondary) {
             new ContinueOnEvent<>());

    /** Private constructor.
     * @param distance threshold for the distance between the two satellites in meters
     * @param secondary provider for the secondary satellite
     * @param maxCheck  maximum checking interval (s)
     * @param threshold convergence threshold (s)
     * @param maxIter   maximum number of iterations in the event time search
     * @param handler   event handler to call at event occurrences
    private InterSatDistanceDetector(final double distance,
                                     final PVCoordinatesProvider secondary,
                                     final double maxCheck,
                                     final double threshold,
                                     final int maxIter,
                                     final EventHandler<? super InterSatDistanceDetector> handler) {
        super(maxCheck, threshold, maxIter, handler);
        this.distance  = distance;
        this.secondary = secondary;

    /** {@inheritDoc} */
    public double g(final SpacecraftState s) {

        // Position of the primary
        final Vector3D pPrimary = s.getPVCoordinates().getPosition();

        // Position of the secondary at state epoch and frame
        final Vector3D pSecondary = secondary.getPVCoordinates(s.getDate(), s.getFrame()).getPosition();

        // Return the value of the function
        return Vector3D.distance(pPrimary, pSecondary) - distance;


    /** {@inheritDoc} */
    protected InterSatDistanceDetector create(final double newMaxCheck, final double newThreshold, final int newMaxIter,
                                              final EventHandler<? super InterSatDistanceDetector> newHandler) {
        return new InterSatDistanceDetector(distance, secondary, newMaxCheck, newThreshold, newMaxIter, newHandler);

Finally, you can use the Orekit event detector BooleanDetector#andCombine(event1, event2) (i.e., event1 is the InterSatDirectViewDetector and event2 the InterSatDistanceDetector) to detect when the two satellites are visible and at a distance lower than a user define value.

I hope these comments will help you.

Best regards,

1 Like

An important remark. The PVCoordinatesProvider secondary parameter must be the same for the two detectors (e.g., a TLEPropagator build from the second TLE). In this case, the BooleanDetector must be added to the first TLEPropagator built from the first TLE which perform the orbit propagation.

1 Like

Thank you for your quick reply. This gives me a good roadmap to move forward. :slight_smile:

Follow up question: I want to create a custom event handler that is triggered when BooleanDetector detects an event. What in the detector causes the event handler to be triggered?

You can easily add a custom event handler to the BooleanDetector by calling the withHandler() method of the detector.
The event handler is triggered each time the g function of the corresponding event detector change its sign. It means that during an event detection it can happen twice: at the beginning of the event and at the end of the event.
Therefore, the eventOccurred method of the event handler is called both at the beginning of the event detection and at the end. The boolean parameter increasing help you to know if it corresponds to the beginning or the end.


1 Like

Thanks Bryan. :slight_smile:

It appears that the InterSatDirectViewDetector takes far smaller “time steps” than the InterSatDistanceDetector (1 millisecond vs 10 minutes). How can I increase the time step size?

Oh, that’s a strange behavior. You could try to use the withMaxCheck(final double newMaxCheck) method when initializing the detectors.


Even using withMaxCheck(final double nexMaxCheck), the step size remains 1 nanosecond for the InterSatDirectViewDetector. Any other ideas? :slight_smile:

Did you use something like

  InterSatDirectViewDetector detector = new InterSatDirectViewDetector(...);
  detector.withMaxCheck(...);  // this doesn't work, detector is not modified


  InterSatDirectViewDetector detector = new InterSatDirectViewDetector(...).
                                        withMaxCheck(...); // this works, as a new detector is created and will be used

The withXxx methods use a fluent API and detectors are in many cases immutable, so you must use the return value of the withXxx methods (just for example like the standard java String.substring method).

1 Like

Note that InterSatDirectViewDetector default value for the max check is 60 seconds. I don’t know why you see one millisecond.

1 Like

It turns out there was a dummy test case in which contact windows were being generated for two satellites with the same TLE. This caused the switching function in InterSatDirectViewDetector to return 0 at each step, and for whatever reason advance the state only 1 ms at a time. :slight_smile:

My guess 1 ms is the tolerance you set for event finding. I.e. events times are found to the nearest ms. And when events are dense on the timeline since g(t)=0 for all t the event detection code appears to be handling one event after the other at the tolerance specified.

A different definition of the g function could handle this case.