Custom Handler not Being Used after Instantiating a PythonAbstractDetector

I have a custom detector that extends PythonAbstractDetector:

class RangeDetector(PythonAbstractDetector):
    def __init__(self,topo: TopocentricFrame, maxCheck: float=PythonAbstractDetector.DEFAULT_MAXCHECK, threshold: float=PythonAbstractDetector.DEFAULT_THRESHOLD,
                 maxIter: int=PythonAbstractDetector.DEFAULT_MAX_ITER, handler: PythonEventHandler=StopOnDecreasing(),
                 minRange: float=0.0,
                 refractionModel: AtmosphericRefractionModel=None):
        """
        Creates an instance of Range detector based on passed in topocentric frame
        and overrides of default maximal checking interval and convergence threshold values.
        """
        self._MAXRANGE = minRange
        self._REFRACTIONMODEL = refractionModel
        self._TOPO = topo
        super(RangeDetector,self).__init__(maxCheck,threshold,maxIter,handler)

    def create(self,newMaxCheck: float, newThreshold: float,
               newMaxIter: int, newHandler: PythonEventHandler):
        RangeDetector(self._TOPO,maxCheck=newMaxCheck,threshold=newThreshold,maxIter=newMaxIter,handler=newHandler,
                      minRange=self._MAXRANGE,refractionModel=self._REFRACTIONMODEL)
        
    def getMinRange(self) -> float:
        return self._MAXRANGE
    
    def getRefractionmodel(self) -> float:
        return self._REFRACTIONMODEL
    
    def getTopocentricFrame(self) -> float:
        return self._TOPO
    
    def g(self,s: SpacecraftState) -> float:
        TRUERANGE = self._TOPO.getRange(s.getPVCoordinates().getPosition(),
                                        s.getFrame(), s.getDate())
        
        CALCULATEDRANGE = 0.0
        if self._REFRACTIONMODEL != None:
            CALCULATEDRANGE = TRUERANGE + self._REFRACTIONMODEL.getRefraction(TRUERANGE)
        else:
            CALCULATEDRANGE = TRUERANGE
        
        return CALCULATEDRANGE - self._MAXRANGE
    
    def withConstantRange(self, newMaxRange: float):
        return RangeDetector(self._TOPO,maxCheck=self.getMaxCheckInterval(),threshold=self.getThreshold(),
                             maxIter=self.getMaxIterationCount(),handler=self.getHandler(),
                             minRange=newMaxRange,refractionModel=self._REFRACTIONMODEL)
    
    def withRefraction(self, newRefractionModel: AtmosphericRefractionModel):
        return RangeDetector(self._TOPO,maxCheck=self.getMaxCheckInterval(),threshold=self.getThreshold(),
                             maxIter=self.getMaxIterationCount(),handler=self.getHandler(),
                             minRange=self._MAXRANGE,refractionModel=newRefractionModel)

I am instatiating it using:

tempHandler2 = RangeVisibilityHandler(index)
station1RangeVisible = RangeDetector(station._topocentricFrame,Common.maxStep,ElevationDetector.DEFAULT_THRESHOLD,handler=tempHandler2)\
                                    .withConstantRange(station._maxRange)\
                                    .withHandler(tempHandler2) # Range restrictions

And the RangeVisibilityHandler is:

class RangeVisibilityHandler(PythonEventHandler):
    """
    Handler for range visibility event. Implements PythonEventHandler<RangeDetector>.
    """
    index = 0
    def __init__(self, index):
        self.index = index
        super(RangeVisibilityHandler, self).__init__()

    def init(self, initialState, target, detector):
        print(initialState,target,detector)

    def eventOccurred(self, state: SpacecraftState, detector: RangeDetector, increasing: bool) -> Action:
        if increasing:
            SimulateObservation.endRangeObserveDate[self.index] = state.getDate()
            print(f" Range visibility on station {self.index} {detector.getTopocentricFrame().getName()} end at {state.getDate()}")
        else:
            SimulateObservation.startRangeObserveDate[self.index] = state.getDate()
            SimulateObservation.endRangeObserveDate[self.index] = SimulateObservation.finalDate
            print(f" Range visibility on station {self.index} {detector.getTopocentricFrame().getName()} begins at {state.getDate()}")

        return Action.CONTINUE
    
    def finish(self, spacecraftState: SpacecraftState, eventDetector: EventDetector) -> None:
        pass
    
    def resetState(self, eventDetector: EventDetector, oldState: SpacecraftState) -> SpacecraftState:
        return oldState

When I use .withHandler(tempHandler2) it returns station1RangeVisible as None. I commented out .withHandler(tempHandler2) and called station1RangeVisible.getHandler() and it shows <EventHandler: org.orekit.propagation.events.handlers.PythonEventHandler@bfc14b9> which is not the handler that I input and I do not have access to the custom methods I put into the RangeVisibilityHandler class.

I believe I did the subclassing correctly, implementing all the methods that I saw, and I do not know what is causing it to seemingly ignore the custom handler.

All help is very much appreciated.

Thank you!

I was able to get my handler to work by using the AbstractDetectorTest.py at orekit_python_artifacts/test at version-12.2 · petrushy/orekit_python_artifacts · GitHub which makes it really easy to copy your handler into the script and ensure that all your custom functions are being handled correctly.

It turns out that my handler didn’t have all of the functions required for Java to accept it as an extension of PythonEventHandler and my final version of the handler was this:

class RangeVisibilityHandler(PythonEventHandler):
    """
    Handler for range visibility event. Implements PythonEventHandler<RangeDetector>.
    """
    index = 0
    topoName = ""
    simulateObservation = None

    def init(self, initialState, target, detector):
        print(initialState,target,detector)

    def eventOccurred(self, state:SpacecraftState, detector, increasing):
        # if self.index == 1:
        #     print()
        if increasing:
            self.simulateObservation.endRangeObserveDate[self.index] = state.getDate()
            print(f" Range visibility on station {self.index} {self.topoName} ends at {state.getDate()}")
        else:
            self.simulateObservation.startRangeObserveDate[self.index] = state.getDate()
            self.simulateObservation.endRangeObserveDate[self.index] = self.simulateObservation.finalDate
            print(f" Range visibility on station {self.index} {self.topoName} begins at {state.getDate()}")

        return Action.CONTINUE
    
    def resetState(self, detector, oldState):
        return oldState
    
    def finish(self, finalState, detector):
        pass
    
    def setIndex(self, index: int):
        self.index = index

    def setTopoName(self, name):
        self.topoName = name

    def setObservation(self, simulateObservation: SimulateObservation):
        self.simulateObservation = simulateObservation

The .withHandler(tempHandler2) method still doesn’t work, because it seems to just return the generic AbstractDetector class instead of the custom one or the PythonAbstractDetector class. Since AbstractDetector doesn’t have a g() method, it throws a AttributeError. This wasn’t an issue, because instantiating the detector as RangeDetector(station._topocentricFrame,Common.maxStep,ElevationDetector.DEFAULT_THRESHOLD,handler=tempHandler2) still used my custom handler. If you want to use .withHandler() overloading the method in RangeDetector worked for me:

def withHandler(self, newHandler: PythonEventHandler):
        return RangeDetector(self._TOPO,maxCheck=self.getMaxCheckInterval(),threshold=self.getThreshold(),
                             maxIter=self.getMaxIterationCount(),handler=newHandler,
                             maxRange=self._MAXRANGE,refractionModel=self._REFRACTIONMODEL)
2 Likes

Hi,

Not sure at all, but you could try to cast the object from .withHandler back to a PythonAbstractDetector (PythonAbstractDetector.cast_(…)). Sometimes that is needed unforationately.