Calculating TCA with two TLE data

Hello everyone. first post here (and new to orekit!). I am trying to calculate TCA and MissDistance with TLE data from CelesTrack, but it calculates the values a bit incorrectly. how can I calculate the exact values without error?

package org.example;

import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.analytical.tle.TLE;
import org.orekit.propagation.analytical.tle.TLEPropagator;
import org.orekit.time.AbsoluteDate;

public class Main {

public static void main(String[] args) {
try {
        // Initialize Orekit
        InitializeOrekit.init();

        // TLE data for Sat1
        String tleLine1Sat1 = "1 45746U 20038S   24135.58388045  .00013219  00000+0  85889-3 0  9991";
        String tleLine2Sat1 = "2 45746  53.0549 337.5672 0001521 101.0908 259.0253 15.08327573216090";
        TLE tleSat1 = new TLE(tleLine1Sat1, tleLine2Sat1);

        // TLE data for SL-12 R/B(AUX MOTOR)
        String tleLine1Sat2 = "1 54760U 22175C   24136.21138098  .00004893  00000+0  32507-3 0  9990";
        String tleLine2Sat2 = "2 54760  53.2150 142.7806 0001482  72.0039 288.1115 15.08864831 77592";
        TLE tleSat2 = new TLE(tleLine1Sat2, tleLine2Sat2);

        // Create TLE propagators
        TLEPropagator propagatorTianmu1 = TLEPropagator.selectExtrapolator(tleSat1);
        TLEPropagator propagatorSl12RB = TLEPropagator.selectExtrapolator(tleSat2);

        // Set start date
        AbsoluteDate startDate = tleSat1.getDate();

        // Set time interval
        AbsoluteDate endDate = startDate.shiftedBy(360 * 3600); // 15 days

        // Find the time and distance of closest approach (TCA)
        double minDistance = Double.MAX_VALUE;

        AbsoluteDate tcaDate = null;

        // Iterate over the time interval
        AbsoluteDate currentDate = startDate;
        while (currentDate.compareTo(endDate) <= 0) {
            // Get the state of both spacecraft
            SpacecraftState stateSat1 = propagatorTianmu1.propagate(currentDate);
            SpacecraftState stateSat2 = propagatorSl12RB.propagate(currentDate);

            // Get the position vectors
            Vector3D positionSat1 = stateSat1.getPVCoordinates().getPosition();
            Vector3D positionSat2 = stateSat2.getPVCoordinates().getPosition();

            // Calculate the distance
            double distance = Vector3D.distance(positionSat2, positionSat1);

            // Check for minimum distance
            if (distance < minDistance) {
                minDistance = distance;
                tcaDate = currentDate;
            }

            // Advance time
            currentDate = currentDate.shiftedBy(0.02); 
        }

        // Print the time and distance of closest approach
        if (tcaDate != null) {
            System.out.println("Closest Approach Time (TCA): " + tcaDate);
            System.out.println("Minimum Distance (km): " + minDistance / 1000); // Distance in meters
        } else {
            System.out.println("TCA not found.");
        }

}       catch (Exception e) {
        e.printStackTrace();
        }

}
}

Hi @blacksmith16

Could you try using the ExtremumApproachDetector

You can add this detector to the propagatorTianmu1 and use propagatorSl12RB as PVCoordinatesProvider of the detector.

You can find example in the test class of the detector.

Best regards,
Bryan

1 Like

Hi @bcazabonne. thank you for your answer.

I get an error, java: type org.orekit.propagation.events.handlers.EventHandler does not take parameters

if you have some free time, I would be very grateful if you could show me how to do it.

package org.example;

import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.ode.events.Action;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.analytical.tle.TLE;
import org.orekit.propagation.analytical.tle.TLEPropagator;
import org.orekit.propagation.events.ExtremumApproachDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.time.AbsoluteDate;

public class Main {

public static void main(String[] args) {
    try {
        
        InitializeOrekit.init();

        // Sat1 için TLE verileri
        String tleLine1Sat1 = "1 45746U 20038S   24135.58388045  .00013219  00000+0  85889-3 0  9991";
        String tleLine2Sat1 = "2 45746  53.0549 337.5672 0001521 101.0908 259.0253 15.08327573216090";
        TLE tleSat1 = new TLE(tleLine1Sat1, tleLine2Sat1);

        
        String tleLine1Sat2 = "1 54760U 22175C   24136.21138098  .00004893  00000+0  32507-3 0  9990";
        String tleLine2Sat2 = "2 54760  53.2150 142.7806 0001482  72.0039 288.1115 15.08864831 77592";
        TLE tleSat2 = new TLE(tleLine1Sat2, tleLine2Sat2);

        
        TLEPropagator propagatorSat1 = TLEPropagator.selectExtrapolator(tleSat1);
        TLEPropagator propagatorSat2 = TLEPropagator.selectExtrapolator(tleSat2);

        
        AbsoluteDate startDate = tleSat1.getDate();

        
        AbsoluteDate endDate = startDate.shiftedBy(360 * 3600); // 15 gün sonrası

        
        ExtremumApproachDetector extremumApproachDetector = new ExtremumApproachDetector(propagatorSat2)
                .withHandler(new EventHandler<ExtremumApproachDetector>() {
                    @Override
                    public Action eventOccurred(SpacecraftState s, ExtremumApproachDetector detector, boolean increasing) {
                        return Action.STOP;
                    }

                    @Override
                    public SpacecraftState resetState(ExtremumApproachDetector detector, SpacecraftState oldState) {
                        return oldState;
                    }
                });

        
        propagatorSat1.addEventDetector(extremumApproachDetector);

        
        double minDistance = Double.MAX_VALUE;
        AbsoluteDate tcaDate = null;

        
        AbsoluteDate currentDate = startDate;
        double timeStep = 60.0; // Başlangıçta 60 saniye
        while (currentDate.compareTo(endDate) <= 0) {
            
            SpacecraftState stateSat1 = propagatorSat1.propagate(currentDate);
            SpacecraftState stateSat2 = propagatorSat2.propagate(currentDate);

            
            Vector3D positionSat1 = stateSat1.getPVCoordinates().getPosition();
            Vector3D positionSat2 = stateSat2.getPVCoordinates().getPosition();

            
            double distance = Vector3D.distance(positionSat2, positionSat1);

            
            if (distance < minDistance) {
                minDistance = distance;
                tcaDate = currentDate;
            }

            
            currentDate = currentDate.shiftedBy(timeStep);

           
            if (distance < 5000) {
                timeStep = 1.0; // Mesafe 5km'den küçükse adımı küçült
            }
            if (distance < 3000) {
                timeStep = 0.1; // Mesafe 3km'den küçükse adımı daha da küçült
            }
            if (distance < 1000) {
                timeStep = 0.01; // Mesafe 1km'den küçükse adımı çok küçült
            }
        }

        
        if (tcaDate != null) {
            System.out.println("En Yakın Yaklaşma Zamanı (TCA): " + tcaDate);
            System.out.println("En Küçük Uzaklık (km): " + minDistance / 1000); // Metre cinsinden uzaklık
        } else {
            System.out.println("TCA bulunamadı.");
        }

    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

Hi,

The handler you’ve implemented already exists. It’s called StopOnEvent.

Cheers,
Romain.

1 Like

Hi @Serrof
Thank you.for your answer.
How exactly do i need to edit it? I do not know much abaout coding and GPT did not do a good job.
Thank you.

Use withHandler(new StopOnEvent())

1 Like

I did not understand where it is

package org.example;

import org.example.InitializeOrekit;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.analytical.tle.TLE;
import org.orekit.propagation.analytical.tle.TLEPropagator;
import org.orekit.propagation.events.ExtremumApproachDetector;
import org.orekit.time.AbsoluteDate;

public class Main {

@Test
public void testFindMinimumDistanceBetweenSatellites() {
    try {
        // Initialize Orekit
        InitializeOrekit.init();

        // TLE data for Sat1
        String tleLine1Sat1 = "1 45746U 20038S   24135.58388045  .00013219  00000+0  85889-3 0  9991";
        String tleLine2Sat1 = "2 45746  53.0549 337.5672 0001521 101.0908 259.0253 15.08327573216090";
        TLE tleSat1 = new TLE(tleLine1Sat1, tleLine2Sat1);

        // TLE data for Sat2
        String tleLine1Sat2 = "1 54760U 22175C   24136.21138098  .00004893  00000+0  32507-3 0  9990";
        String tleLine2Sat2 = "2 54760  53.2150 142.7806 0001482  72.0039 288.1115 15.08864831 77592";
        TLE tleSat2 = new TLE(tleLine1Sat2, tleLine2Sat2);

        // Create TLE propagators for both satellites
        TLEPropagator propagatorTianmu1 = TLEPropagator.selectExtrapolator(tleSat1);
        TLEPropagator propagatorSl12RB = TLEPropagator.selectExtrapolator(tleSat2);

        // Set up the ExtremumApproachDetector with Sat2 as the secondary provider
        ExtremumApproachDetector detector = new ExtremumApproachDetector(propagatorSl12RB);

        // Add the detector to the propagator of Sat1
        propagatorTianmu1.addEventDetector(detector);

        // Set the start date for propagation
        AbsoluteDate startDate = tleSat1.getDate();

        // Propagation time interval (e.g., 15 days)
        AbsoluteDate endDate = startDate.shiftedBy(15 * 24 * 3600); // 15 days

        // Variables to track minimum distance
        double minDistance = Double.MAX_VALUE;
        AbsoluteDate minDistanceDate = null;

        // Propagate and detect closest approach events
        AbsoluteDate currentDate = startDate;
        while (currentDate.compareTo(endDate) < 0) {
            // Propagate both satellites to the current date
            SpacecraftState stateSat1 = propagatorTianmu1.propagate(currentDate);
            SpacecraftState stateSat2 = propagatorSl12RB.propagate(currentDate);

            // Calculate distance between the satellites
            Vector3D positionSat1 = stateSat1.getPVCoordinates().getPosition();
            Vector3D positionSat2 = stateSat2.getPVCoordinates().getPosition();
            double distance = Vector3D.distance(positionSat1, positionSat2);

            // Check for minimum distance
            if (distance < minDistance) {
                minDistance = distance;
                minDistanceDate = currentDate;
            }

            // Advance time for propagation
            currentDate = currentDate.shiftedBy(60); // Propagate every 60 seconds
        }

        // Output the minimum distance and the corresponding date
        System.out.println("Minimum Distance: " + minDistance + " meters");
        System.out.println("At Date: " + minDistanceDate);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

is it correct this way?
@Serrof

I suggest you look at the orekit tutorials, which are documented here. You can in particular look at the ones dealing with propagation.
You will see that instead of performing a loop in time by yourself (you used a 60 s step for this in your code), you can set up a handler and have a single propagation run, that will be automatically stopped when the even occurs. Start with the first tutorials (simple propagation with no events) before diving into the next ones.

1 Like

Hi @blacksmith16 ,

It is highly recommend that you look at the tutorials, mentioned by @luc.
And the orekit is well documented. You should read that too.

Specifically here, what @Serrof said is the implemention of new EventHandler<ExtremumApproachDetector>() in .withHandler() in your code, it is just the StopOnEvent. So the code should be like the followings:

ExtremumApproachDetector extremumApproachDetector = new ExtremumApproachDetector(propagatorSat2)
                .withHandler(new StopOnEvent());

Further more, when we use event, the loop is no longer needed. The event is used instead of the loop, with high performance and high time resolution.

Here is the sample code in python. You can take a look.

    # Initialize the extremum approach detector and event slope filter for detecting
    # and filtering increasing events.
    extremumApproachDetector = ExtremumApproachDetector(secondaryPVProvider)
    closeApproachDetector = EventSlopeFilter(extremumApproachDetector,
                                             FilterType.TRIGGER_ONLY_INCREASING_EVENTS)\
        .withHandler(StopOnEvent())

    # Set up the event logger and add it to the propagator to monitor
    # the close approach event.
    eventsLogger = EventsLogger()
    prop.addEventDetector(
        eventsLogger.monitorDetector(closeApproachDetector))

    # Propagate from `absdateStart` to `absdateEnd` and clear the event detectors.
    prop.propagate(absdateStart, absdateEnd)

    # Get the unique logged event and extract relevant information.
    event: EventsLogger.LoggedEvent = eventsLogger.getLoggedEvents().get(0)
    absdate = event.getDate()
    s: SpacecraftState = event.getState()
    pv = s.getPVCoordinates(_eme2000)
    secdPV = secondaryPVProvider.getPVCoordinates(absdate, _eme2000)
    # Compute the distance, which is the minimum distance between the two bodies.
    minDis = Vector3D.distance(secdPV.getPosition(),
                               pv.getPosition())
prop=propagatorTianmu1
secondaryPVProvider=propagatorSl12RB
1 Like

Hi all,

By using ExtremumApproachDetector with EventSlopeFilter, the event of closest approach can be detected. But for the two satellite, it is just the first one, not the one really minimum distance during the time span. How to solve it?

For the case here, the closest approach is as followings, which is just the first one.

2024-05-14T14:47:10.82068539554401Z 757.364km

1 Like

Hi,

Well with StopOnEvent as handler, you do stop at the first occurence. If there are multiple close approaches and you want to know the closest of all, you need to somehow log all the events and check afterwards. So you could use RecordAndContinue for example.

Cheers,
Romain.

1 Like

Hi @Serrof ,

Thank you so much.
With RecordAndContinue handler and looping the logged events, the Closest-Approach can be found.

    # Loop the logged events to find the Closest-Approach during the time span,
    # and assemble relevant information.
    absdate_closest = None
    info_closest = {}
    dis_closest = None
    for event in eventsLogger.getLoggedEvents():
        absdate: AbsoluteDate = event.getDate()
        s: SpacecraftState = event.getState()
        pv = s.getPVCoordinates(_eme2000)
        secdPV = secondaryPVProvider.getPVCoordinates(absdate, _eme2000)
        # Compute the distance, which is the minimum distance between the two bodies.
        dis = Vector3D.distance(secdPV.getPosition(),
                                pv.getPosition())
        # Compute the relative velocity
        relvel = Vector3D.subtract(secdPV.getVelocity(), pv.getVelocity())
        # print('{} {:8.3f} {}'.format(absdate.toStringWithoutUtcOffset(_utc, 6),
        #                              dis * 1e-3, relvel))

        if (dis_closest is None) or (dis < dis_closest):
            dis_closest = dis
            absdate_closest = absdate
            info_closest = {'dis': dis, 'relvel': relvel,
                            'pv': pv, 'secdPV': secdPV}

The result is as followings:

2024-05-20T17:03:42.44189146462073Z {'dis': 855.1896383023153, 'relvel': <Vector3D: {968.4113496121; -1,378.8216963619; 11,971.6454535693}>, 'spv': <TimeStampedPVCoordinates: {2024-05-20T17:03:42.44189146462073, P(-3799683.4142968054, 5701844.024700588, 954500.4086337961), V(-4308.642532043876, -1862.0144038531332, -5970.1894198128475), A(4.57436435120161, -6.864338156237287, -1.1491043407152146)}>, 'pv': <TimeStampedPVCoordinates: {2024-05-20T17:03:42.44189146462073, P(-3799391.018639073, 5701048.681807317, 954385.1533565962), V(-3340.231182431817, -3240.836100215075, 6001.456033756488), A(4.575663018318098, -6.865857631474727, -1.1493802397005881)}>}

BTW, the StopOnEvent is the default handler of EventSlopeFilter. Is that true?

1 Like

Hi, thank you so much for your answer.

Im trying to write in orekit but i could not convert yours.

Here is what I wrote;

import org.example.InitializeOrekit;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.hipparchus.ode.events.Action;
import org.orekit.bodies.OneAxisEllipsoid;
import org.orekit.data.DataProvidersManager;
import org.orekit.data.ZipJarCrawler;
import org.orekit.frames.FramesFactory;
import org.orekit.orbits.PositionAngleType;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.analytical.tle.TLE;
import org.orekit.propagation.analytical.tle.TLEPropagator;
import org.orekit.propagation.events.ExtremumApproachDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.TimeScalesFactory;
import org.orekit.utils.Constants;

import java.io.File;

public class Main {
public static void main(String args) {
try {
// Initialize Orekit
InitializeOrekit.init();

        // Load TLE data
        String tleLine1Sat1 = "1 25544U 98067A   20029.54791667  .00016717  00000-0  10270-3 0  9000";
        String tleLine2Sat1 = "2 25544  51.6442  25.4028 0007397  39.2633  65.7286 15.50199938209694";
        TLE tleSat1 = new TLE(tleLine1Sat1, tleLine2Sat1);

        String tleLine1Sat2 = "1 33591U 09005A   20029.54791667  .00016717  00000-0  10270-3 0  9000";
        String tleLine2Sat2 = "2 33591  51.6442  25.4028 0007397  39.2633  65.7286 15.50199938209694";
        TLE tleSat2 = new TLE(tleLine1Sat2, tleLine2Sat2);

        // Create propagators
        TLEPropagator propagatorSat1 = TLEPropagator.selectExtrapolator(tleSat1);
        TLEPropagator propagatorSat2 = TLEPropagator.selectExtrapolator(tleSat2);

        // Create detector for extremum approach
        ExtremumApproachDetector detector = new ExtremumApproachDetector(1.0, propagatorSat2)
                .withHandler(new EventHandler<ExtremumApproachDetector>() {
                    @Override
                    public Action eventOccurred(SpacecraftState state, ExtremumApproachDetector detector, boolean increasing) {
                        // Capture the TCA and miss distance
                        AbsoluteDate tca = state.getDate();
                        double missDistance = detector.getCurrentState().getPVCoordinates().getPosition().distance(state.getPVCoordinates().getPosition());
                        System.out.println("Closest Approach Time (TCA): " + tca);
                        System.out.println("Minimum Distance (km): " + missDistance / 1000);
                        return Action.STOP;
                    }

                    @Override
                    public SpacecraftState resetState(ExtremumApproachDetector detector, SpacecraftState oldState) {
                        return oldState;
                    }
                });

        // Set up time range for propagation
        AbsoluteDate startDate = tleSat1.getDate();
        AbsoluteDate endDate = startDate.shiftedBy(360 * 3600); // 15 days

        // Add event detector to the propagator
        propagatorSat1.addEventDetector(detector);

        // Propagate over the time range
        propagatorSat1.propagate(startDate, endDate);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

}

and this is the error i cant fix
java: type org.orekit.propagation.events.handlers.EventHandler does not take parameters

Replace new EventHandler<ExtremumApproachDetector> by new EventHandler. Generics have been removed from event handlers in january 2023, according to issue 1017. The change was later published in 12.0.

I replaced but now İntellij dont even run the code

I think here i have similar error

DateDetector maneuverDate = new DateDetector(tcaDate);
                ImpulseManeuver<DateDetector> maneuver = new ImpulseManeuver<>(
                        maneuverDate,
                        deltaV,
                        isp
                );
                numPropagator.addForceModel(maneuver);

error:
java: type org.orekit.forces.maneuvers.ImpulseManeuver does not take parameters

how do you provide tle?

what is wrong in here?

Hi @blacksmith16,

The first “>” symbol should be deleted. Your IDE should tell you what is the mistake here.

Cheers,
Maxime