Event after a certain time in eclipse

Hi @evan.ward thanks a lot for your kind help. I’ve changed the return value of my custom PassCounter to Action.RESET_EVENTS from Action.CONTINUE - is this what you’ve had in mind? Even after the change, I still get different results depending on the order in which I create handlers. The only difference is that now eclipseDetectorWithPassCounter has to be after eclipseDetectorWithLogger. I’m not sure if this is an indication of a larger issue?

Also, I see where you’re going with this, thanks for the idea. It didn’t cross my mind to communicate between detectors and handlers like this:

  if (increasing) {
    durationDetector.stop(s.getDate()); // Let the durationDetector know when eclipse starts/ends so that the duration event can be found by the propagator
  } else {
    durationDetector.start(s.getDate());
  }
  return Action.RESET_EVENTS;
}));

I’ve implemented this but with a custom handler (I don’t think Python supports lambda detectors, see this post). Unfortunately, it falls over when I try to attach this ElapsedTimeHandler to the EclipseDetector with the following error:

InvalidArgsError: (<class 'org.orekit.propagation.events.EclipseDetector'>, 'withHandler', <ElapsedTimeHandler: java.lang.Object@25f7391e>)

I’m not sure why this is the case because the custom handler is nearly identical to my PassCounter, which works. Any ideas what might be wrong? I’ve put my changed code here below.

class PassCounter(PythonEventHandler):
    """ Counts events. Will stop propagation after first n events."""

    def __init__(self, limit):
        self.limit = limit
        self.passesInc = 0
        self.passesDec = 0
        super(PassCounter, self).__init__()

    def init(self, initialState, target):
        # Not used, just to try and port functionality from ElapsedTimeCounter.
        self.startEpoch = initialState.getDate() #TODO 'PassCounter' object has no attribute 'startEpoch'?
        self.elapsedSec = 0.0 #TODO: 'PassCounter' object has no attribute 'elapsedSec'?
        super(PassCounter, self).init() # Doesn't affect the odd attribute errors.
        #TODO for whatever reason, init is never called. This is why I had cryptic Java errors in ElapsedTimeCounter - because attributes were undefined!
    
    def eventOccurred(self, s, T, increasing):
        if increasing:
            self.passesInc = self.passesInc + 1
            print(f'Increasing pass at {s.getDate()}, passes = {self.passesInc}')
        else:
            self.passesDec = self.passesDec + 1
            print(f'Decreasing pass at {s.getDate()}, passes = {self.passesDec}')
        
        # Just as an example, stop propagation after some no. events.
        if self.passesInc >= self.limit and self.passesDec >= self.limit:
            return Action.STOP
        else:
            return Action.RESET_EVENTS # Check other event detectors, not simply continue.

    def resetState(self, detector, oldState):
        return oldState

class ElapsedTimeDetector(PythonAbstractDetector):
    """ Counts the elapsed time since a decreasing event, and triggers and event
    when the elapsed  time reaches a predefined limit. Inspired by this:
    https://forum.orekit.org/t/eventdetection-based-on-time-in/293
    """
    
    def __init__(self, target, handler=None):
        self.target = target
        self.startEpoch = None
        self.elapsedSec = 0.0
        
        dmax = float(PythonAbstractDetector.DEFAULT_MAXCHECK)
        dthresh = float(PythonAbstractDetector.DEFAULT_THRESHOLD)
        dmaxiter = PythonAbstractDetector.DEFAULT_MAX_ITER
        
        if handler is None:
            handler = ContinueOnEvent().of_(ElapsedTimeDetector)
        
        super(ElapsedTimeDetector, self).__init__(dmax, dthresh, dmaxiter, handler) #super(maxCheck, threshold, maxIter, handler);

    def init(self, initialState, target):
        pass
    
    def g(self, s):
        if self.startEpoch is None:
            add = 0.0
        else:
            add = s.getDate().durationFrom(self.startEpoch)
            
        return self.elapsedSec - self.target + add
    
    def start(self, d):
        self.startEpoch = d
    
    def stop(self, d):
        self.elapsedSec += d.durationFrom(self.startEpoch)
        self.startEpoch = None

class ElapsedTimeHandler(PythonEventHandler):
    """ Handler for EclipseDetector that also marks start/stop times in ElapsedTimeDetector. """

    def __init__(self):
        super(PythonEventHandler, self).__init__()
    
    def init(self, initialState, target):
        pass
    
    def setDetector(self, elapsedDetector):
        self.elapsedDetector = elapsedDetector
        
    def eventOccurred(self, s, T, increasing):
        if increasing:
            self.elapsedDetector.start(s.getDate())
            print(f'Increasing pass at {self.elapsedDetector.startEpoch}')
        else:
            self.elapsedDetector.stop(s.getDate())
            print(f'Increasing pass at {self.elapsedDetector.startEpoch}, duration = {self.elapsedHandler.elapsedSec}')
        
        return Action.RESET_EVENTS # Check other event detectors, not simply continue.

    def resetState(self, detector, oldState):
        return oldState

(…)

#%% Configure the propagator events
passCounter = PassCounter(limit=MAX_ECLIPSES)
elapsedTimeDetector = ElapsedTimeDetector(100.0)
elapsedTimeHandler = ElapsedTimeHandler()
elapsedTimeHandler.setDetector(elapsedTimeDetector)
logger = EventsLogger()

propagator.addEventDetector(elapsedTimeDetector)
#TODO eclipseDetectorWithElapsedHandler fails to instantiate due to InvalidArgsError in withHandler???
eclipseDetectorWithElapsedHandler = EclipseDetector(sun, Constants.SUN_RADIUS, earth).withUmbra().withHandler(elapsedTimeHandler)

#TODO eclipseDetectorWithPassCounter has to be here for eclipseDetectorWithLogger to work properly. Otherwise, we get 9 logged events.

eclipseDetectorWithLogger = EclipseDetector(sun, Constants.SUN_RADIUS, earth).withUmbra().withHandler(ContinueOnEvent())
loggedDetector = logger.monitorDetector(eclipseDetectorWithLogger)
propagator.addEventDetector(loggedDetector)

#TODO After changing from Action.CONTINUE to RESET_EVENTS had to move the eclipseDetectorWithPassCounter to the end. Why?
eclipseDetectorWithPassCounter = EclipseDetector(sun, Constants.SUN_RADIUS, earth).withUmbra().withHandler(passCounter)
propagator.addEventDetector(eclipseDetectorWithPassCounter)