Event detection errors: Longitude and Eclipse (using 10.2)

Orekit 10.2 on Linux: creating an Ephemeris with an existing list of SpacecraftState, I successfully employed the LatitudeExtremumDetector and the LatitudeCrossingDetector. However, when attempting to employ the LongitudeCrossingDetector, the Ephemeris.propagate(AbsoluteDate end) call never returns, and when attempting to employ the EclipseDetector, the Ephemeris.propagate() call results in a StackOverflowError, with three of the error iterations shown below.

Advice? Thanks!

Denis

Caused by: java.lang.StackOverflowError
at org.orekit.utils.AngularCoordinates.getModifiedRodrigues(AngularCoordinates.java:674)
at org.orekit.utils.TimeStampedAngularCoordinates.interpolate(TimeStampedAngularCoordinates.java:314)
at org.orekit.attitudes.Attitude.interpolate(Attitude.java:214)
at org.orekit.attitudes.Attitude.interpolate(Attitude.java:55)
at org.orekit.time.TimeInterpolable.interpolate(TimeInterpolable.java:51)
at org.orekit.propagation.SpacecraftState.interpolate(SpacecraftState.java:520)
at org.orekit.propagation.SpacecraftState.interpolate(SpacecraftState.java:76)
at org.orekit.time.TimeInterpolable.interpolate(TimeInterpolable.java:51)
at org.orekit.propagation.analytical.Ephemeris.basicPropagate(Ephemeris.java:212)
at org.orekit.propagation.analytical.AbstractAnalyticalPropagator.propagate(AbstractAnalyticalPropagator.java:125)
at org.orekit.propagation.AbstractPropagator.propagate(AbstractPropagator.java:260)
at org.orekit.propagation.analytical.Ephemeris.getPVCoordinates(Ephemeris.java:238)
at org.orekit.propagation.events.EclipseDetector.g(EclipseDetector.java:180)
at org.orekit.propagation.events.EventState.g(EventState.java:153)
at org.orekit.propagation.events.EventState.reinitializeBegin(EventState.java:165)
at org.orekit.propagation.analytical.AbstractAnalyticalPropagator.acceptStep(AbstractAnalyticalPropagator.java:208)
at org.orekit.propagation.analytical.AbstractAnalyticalPropagator.propagate(AbstractAnalyticalPropagator.java:168)
at org.orekit.propagation.AbstractPropagator.propagate(AbstractPropagator.java:260)
at org.orekit.propagation.analytical.Ephemeris.getPVCoordinates(Ephemeris.java:238)
at org.orekit.propagation.events.EclipseDetector.g(EclipseDetector.java:180)
at org.orekit.propagation.events.EventState.g(EventState.java:153)
at org.orekit.propagation.events.EventState.reinitializeBegin(EventState.java:165)
at org.orekit.propagation.analytical.AbstractAnalyticalPropagator.acceptStep(AbstractAnalyticalPropagator.java:208)
at org.orekit.propagation.analytical.AbstractAnalyticalPropagator.propagate(AbstractAnalyticalPropagator.java:168)
at org.orekit.propagation.AbstractPropagator.propagate(AbstractPropagator.java:260)
at org.orekit.propagation.analytical.Ephemeris.getPVCoordinates(Ephemeris.java:238)
at org.orekit.propagation.events.EclipseDetector.g(EclipseDetector.java:180)
at org.orekit.propagation.events.EventState.g(EventState.java:153)

Hi @DenisDurand

Welcome to the Orekit forum!

Could you provide us with a working example showing your issue, like a failing JUnit test?

Thank you,
Bryan

Thanks for your reply! I’m an astrodynamicist with enough software skills to be dangerous, so no unit testing employed (at this time)! I have a Java class that is instantiated with a List of SpacecraftState which is then used to create an Ephemeris. The class has methods for adding event detections, with a final method to process the events by propagating the ephemeris. There is also a mapping of the employed EventDetectors to be used in subsequent processing of the event logs. The event detection methods are given below. LatitudeExtremum and LatitudeCrossing work well. If LongitudeCrossing is added (either alone or with others, excluding eclipse detection), the propagation in processEvents() nerver returns. If Eclipse Detection is employed, the stack is overflowed with the trace given above…

Denis

public OrekitEphemerisHandler addLatitudeExtremumDetection() throws IOException {
LatitudeExtremumDetector detector = new LatitudeExtremumDetector(Geo.getEllipsoid(FramesAndModels.getAstroGeoModel())).withHandler(new RecordAndContinue());
if (detectorNameMap.containsKey(detector)) {
throw new IOException(“LatitudeExtremumDetection already present!”);
}
detectorNameMap.put(detector, “LatituteExtremumDetection”);
return this;
}

public OrekitEphemerisHandler addLatitudeCrossingDetection(double latitude_deg) throws IOException {
LatitudeCrossingDetector detector = new LatitudeCrossingDetector(Geo.getEllipsoid(FramesAndModels.getAstroGeoModel()), FastMath.toRadians(latitude_deg)).withHandler(new RecordAndContinue());
if (detectorNameMap.containsKey(detector)) {
throw new IOException(“LatitudeCrossingDetection already present!”);
}
detectorNameMap.put(detector, “LatitudeCrossingDetection”);
return this;
}

public OrekitEphemerisHandler addLongitudeCrossingDetection(double longitude_deg) throws IOException {
LongitudeCrossingDetector detector = new LongitudeCrossingDetector(Geo.getEllipsoid(FramesAndModels.getAstroGeoModel()), FastMath.toRadians(longitude_deg)).withHandler(new RecordAndContinue());
if (detectorNameMap.containsKey(detector)) {
throw new IOException(“LongitudeCrossingDetection already present!”);
}
detectorNameMap.put(detector, “LongitudeCrossingDetection”);
return this;
}

public OrekitEphemerisHandler addEclipseDetection() throws IOException {
EclipseDetector detector = new EclipseDetector(ephemeris, OCCULTED_RADIUS, Geo.getEllipsoid(FramesAndModels.getAstroGeoModel())).withHandler(new RecordAndContinue());
if (detectorNameMap.containsKey(detector)) {
throw new IOException(“ElipseDetector already present!”);
}
detectorNameMap.put(detector, “EclipseDetector”);
return this;
}

public void processEvents() {
for (EventDetector detector : detectorNameMap.keySet()) {
ephemeris.addEventDetector(detector);
ephemeris.addEventDetector(eventsLogger.monitorDetector(detector));
}
ephemeris.propagate(ephemeris.getMaxDate());
List events = eventsLogger.getLoggedEvents();
System.out.println(events.size() + " events");
for (LoggedEvent event : events) {
System.out.println(String.format("%s: %s %s", detectorNameMap.get(event.getEventDetector()), event.getState().getDate(), event.isIncreasing() ));
}
}

Additional info: Ephemeris instantiation uses List of SpacecraftState(AbsolutePVCoordinates) where different frames produce the same result; OCCULTED_RADIUS = 1.0 (meters).

You EclipseDetector configuration seems strange to me. It looks as if the occulted body is set to the spacecraft being propagated and not some other body. In EclipseDetector you have three main bodies : the spacecraft, the occulted body (typically Sun) and the occulting body (typically Earth). The spacecraft is managed as the “thing” the the propagator handles and the two other bodies should therefore be provided by other providers, hence they are specified in the EclipseDetector constructor.

When the g function of the detector is called for one current spacecraft state, it calls the two other providers to get the position of the two other bodies and then computes the relative geometry. Here, as the occulted seems to be the ephemeris itself, you end up with infinite recursion.

Yes! That was it indeed! EclipseDetector now working. Thank you!!

The longitude detection problem remains: longitude crossing never returns from the propagate call. I’m using the ISS propagated for 24 hours…

Denis

Hi Denis,

Can you provide a minimal failing test case that shows the issue with the longitude crossing detector? If so I can take a look at it. A hang during event detection would be a bug, but I need to be able to run the code locally to debug it.

Regards,
Evan

I generated a one-day ephemeris at 1-minute intervals on the ISS using SGP4. Each ephemeris point was converted to a SpacecraftState, and the List of SpacecraftState instantiated an Orekit Ephemeris which was then used to detect various events, including eclipse, latitude extremum, latitude crossing, longitude extremum, and longitude crossing. They all succeeded except longitude crossing, which hangs (and for this example longitude extremum returns 0 events). I’m using Orekit 10.1 (I may have said 10.2 somewhere else). What stage and form of test data would you like? Thanks!

Hi Dennis,

I need something I can run on my machine. The preferred form is a failing JUnit test case that I can incorporate directly into the Orekit test suite. Or you can just provide a class with a main method that demonstrates the issue. Minimal examples are preferred so there aren’t other things going on that obscure the issue.

Regarding the longitude extremum detector what result do you expect? I would expect zero events since the time derivative of longitude is always greater than zero for the ISS.

Regards,
Evan

Hi Evan,
Thanks for your reply. I did indeed expect 0 for the longitude extremum–I was just noting that it worked. Here is the code for event detection on a short ISS ephemeris. I’ve included latitude crossing detection to be sure it works. The longitude crossing detection hangs…

package applib.test;

import applib.orekit.OrekitUtilities;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.hipparchus.geometry.euclidean.threed.Vector3D;
import org.orekit.bodies.OneAxisEllipsoid;
import org.orekit.data.DataProvidersManager;
import org.orekit.data.DirectoryCrawler;
import org.orekit.errors.OrekitException;
import org.orekit.frames.Frame;
import org.orekit.frames.FramesFactory;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.analytical.Ephemeris;
import org.orekit.propagation.events.EventDetector;
import org.orekit.propagation.events.EventsLogger;
import org.orekit.propagation.events.LatitudeCrossingDetector;
import org.orekit.propagation.events.LongitudeCrossingDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.propagation.events.handlers.RecordAndContinue;
import org.orekit.time.AbsoluteDate;
import org.orekit.time.DateComponents;
import org.orekit.time.DateTimeComponents;
import org.orekit.time.TimeComponents;
import org.orekit.time.TimeScalesFactory;
import org.orekit.utils.AbsolutePVCoordinates;
import org.orekit.utils.Constants;
import org.orekit.utils.IERSConventions;
import org.orekit.utils.PVCoordinates;
import org.orekit.utils.TimeStampedPVCoordinates;

public class LongCrossingTest {

  double[][] ephemData =   

  {{2020168.00000000, +3.11645372646856E+06, +3.56303974760210E+06, +4.86585087591950E+06, -6.78600943954931E+03, +2.60527025104741E+03, +2.43268735723718E+03},
   {2020168.00694444, -1.33788954572545E+06, +4.22521068792356E+06, +5.14208226327810E+06, -7.49130239339198E+03, -4.82620951928272E+02, -1.54711536580586E+03},
   {2020168.01388889, -5.20302491052448E+06, +3.02687870520295E+06, +3.14785702257933E+06, -4.89707456825247E+03, -3.35865666023309E+03, -4.84451336174061E+03},
   {2020168.02083333, -6.77614214603955E+06, +4.95199524380026E+05, -2.36817019670809E+05, -1.45826203103024E+02, -4.75577529669370E+03, -6.00276146228711E+03},
   {2020168.02777778, -5.36718001167701E+06, -2.25488447276047E+06, -3.51759010857660E+06, +4.65952505072566E+03, -4.06010992466957E+03, -4.51349883925724E+03},
   {2020168.03472222, -1.60262722780846E+06, -4.01625181910066E+06, -5.25145024343810E+06, +7.40819958381310E+03, -1.58790935112168E+03, -1.04613902727167E+03},
   {2020168.04166667, +2.86371248000347E+06, -4.01967221558883E+06, -4.68032459287575E+06, +6.91104996598993E+03, +1.57762760504089E+03, +2.87783461225264E+03},
   {2020168.04861111, +6.07423894273251E+06, -2.26182768972817E+06, -2.05294286111847E+06, +3.37858576520745E+03, +4.05915010507624E+03, +5.54555781651491E+03},
   {2020168.05555556, +6.61326189805755E+06, +4.89969312558430E+05, +1.47878747143030E+06, -1.65289604846995E+03, +4.76136461855069E+03, +5.77335858396059E+03},
   {2020168.06250000, +4.23790377664995E+06, +3.02566242955372E+06, +4.35680967738801E+06, -5.96008321899841E+03, +3.36600978316733E+03, +3.45026177517780E+03},
   {2020168.06944444, -4.90507648645092E+03, +4.22839521464820E+06, +5.31009043591639E+06, -7.63893201932203E+03, +4.89436333497626E+02, -3.94583656473561E+02},
   {2020168.07638889, -4.24558550678168E+06, +3.56926022411138E+06, +3.91876309857785E+06, -5.95338290563186E+03, -2.60243319809405E+03, -4.06482957923683E+03},
   {2020168.08333333, -6.61606694142538E+06, +1.33801135966337E+06, +7.96651732857410E+05, -1.64461058439188E+03, -4.54915483531668E+03, -5.94102136490552E+03}};

  public LongCrossingTest() {
    
    List<SpacecraftState> stateList = new ArrayList<>(ephemData.length);
    
    
    int numPoints = ephemData.length;
    
    for (int ephPointIndex = 0; ephPointIndex<numPoints; ephPointIndex++) {
      double             timeVal        = ephemData[ephPointIndex][0];
      int                year           = (int) Math.floor(timeVal/1000.0);
      int                dayOfYear      = (int) Math.floor(timeVal-year*1000.0);
      DateComponents     dateComponents = new DateComponents(year, dayOfYear);
      int                secInDay       = (int) (86400 * (timeVal - Math.floor(timeVal)));
      TimeComponents     timeComponents = new TimeComponents(secInDay);
      DateTimeComponents dtc            = new DateTimeComponents(dateComponents, timeComponents);
      AbsoluteDate       absoluteDate   = new AbsoluteDate(dtc, TimeScalesFactory.getUTC());
      
      Frame              frame          = FramesFactory.getTEME();
      Vector3D           pos            = new Vector3D(ephemData[ephPointIndex][1], ephemData[ephPointIndex][2], ephemData[ephPointIndex][3]);          
      Vector3D           vel            = new Vector3D(ephemData[ephPointIndex][4], ephemData[ephPointIndex][5], ephemData[ephPointIndex][6]);          
              
      TimeStampedPVCoordinates tpv      = new TimeStampedPVCoordinates(absoluteDate, pos, vel);
      AbsolutePVCoordinates    apv      = new AbsolutePVCoordinates(frame, tpv);
      
      stateList.add(new SpacecraftState(apv));
    }
    
    Ephemeris    ephemeris         = new Ephemeris(stateList, 3);
    EventsLogger eventsLogger      = new EventsLogger();
    EventHandler recordAndContinue = new RecordAndContinue();
    
    Frame earthFrame = FramesFactory.getTIRF(IERSConventions.IERS_1996);
    OneAxisEllipsoid ellipsoid = new OneAxisEllipsoid(Constants.GRS80_EARTH_EQUATORIAL_RADIUS, Constants.GRS80_EARTH_FLATTENING, earthFrame);

    EventDetector latCrossingDetector = new LatitudeCrossingDetector(ellipsoid, 0.0).withHandler(recordAndContinue);
    EventDetector lonCrossingDetector = new LongitudeCrossingDetector(ellipsoid, 0.0).withHandler(recordAndContinue);
    
    ephemeris.addEventDetector(latCrossingDetector);
    ephemeris.addEventDetector(eventsLogger.monitorDetector(latCrossingDetector));  
    
    ephemeris.addEventDetector(lonCrossingDetector);
    ephemeris.addEventDetector(eventsLogger.monitorDetector(lonCrossingDetector));    

    ephemeris.propagate(ephemeris.getMaxDate());
    
    List<EventsLogger.LoggedEvent> events = eventsLogger.getLoggedEvents();
    for (EventsLogger.LoggedEvent event : events) {
      System.out.println("Event Date = " + event.getState().getDate());
    }    

  }
  
  public static void main(String[] args) {
    
    File orekitData = new File("data/orekit-data");
    DataProvidersManager manager = DataProvidersManager.getInstance();
    try {
      manager.addProvider(new DirectoryCrawler(orekitData));
    } catch (OrekitException ex) {
      ex.printStackTrace(System.err);
    }

    new LongCrossingTest();
  }
  
}

HI Denis,

Thanks for the code. I’ll take a look.

Regards,
Evan

Hi Denis,

I finally got a chance to look at the example you posted. It runs fine on my machine. I used the latest development version of Orekit and the “regular-data” for the auxiliary data. (I didn’t have your data/orekit-data and it probably won’t make a difference.) Here is the output I get:

Event Date = 2020-06-16T00:29:19.744
Event Date = 2020-06-16T00:55:00.572
Event Date = 2020-06-16T00:55:00.572
Event Date = 2020-06-16T00:55:00.572
Event Date = 2020-06-16T00:55:00.572
Event Date = 2020-06-16T01:15:55.877

Seems like there are several simultaneous events, but they’re handled.

Some notes on implementation. EventsLogger and RecordAndContinue serve essentially the same purpose using different interfaces, so you probably don’t need both. Also you’ve added to copies of the same event detector to the propagator which is duplicative and causes the simultaneous events.

Regards,
Evan

Evan,
I updated to Orekit 10.2 recently but forgot to check–it is working fine now!
Thanks!
Denis

Also ran with more detailed output to print time to double precision and increasing flag. Looks like several close events are detected possibly due to numerical noise in the g function and/or propagation/interpolation. A good candidate for the numerical noise is the iterative algorithm in OneAxisEllipsoid.transform(...) which uses a hard coded tolerance to decide when to stop iterating.

  1759.7438410582567 false org.orekit.propagation.events.LatitudeCrossingDetector@725bef66
  1759.7438410582567 false org.orekit.propagation.events.LatitudeCrossingDetector@725bef66
  3300.5723410934343 false org.orekit.propagation.events.LongitudeCrossingDetector@2aaf7cc2
  3300.5723410934343 false org.orekit.propagation.events.LongitudeCrossingDetector@2aaf7cc2
  3300.5723410934343  true org.orekit.propagation.events.LongitudeCrossingDetector@2aaf7cc2
   3300.572341094967 false org.orekit.propagation.events.LongitudeCrossingDetector@2aaf7cc2
   3300.572341094967  true org.orekit.propagation.events.LongitudeCrossingDetector@2aaf7cc2
  3300.5723410965006 false org.orekit.propagation.events.LongitudeCrossingDetector@2aaf7cc2
  3300.5723410965006  true org.orekit.propagation.events.LongitudeCrossingDetector@2aaf7cc2
  3300.5723410965006  true org.orekit.propagation.events.LongitudeCrossingDetector@2aaf7cc2
   4555.876583584446  true org.orekit.propagation.events.LatitudeCrossingDetector@725bef66
   4555.876583584446  true org.orekit.propagation.events.LatitudeCrossingDetector@725bef66

Great, glad it is working now.

Regards,
Evan

Now that it’s working, I haven’t investigated it to the detail you’ve presented above. I’ll have to take a look…
Thanks!
Denis