How to calculate the maximum elevation angle within a specified time interval?

Hi everyone,
I use the ElevationExtremumDetector to calculate the maximum elevation. The key code is as follows.
My question is that when my calculation start time and end time do not include the maximum elevation, there is no event returned. I expect to compute the maximum elevation angle within the time interval. What should I do?

Cheers,
Anjin

    public List<MaxElevationResp> calMaxElevation(Coordinate3D location, Long startMillis, Long endMillis) {
        GeodeticPoint point = new GeodeticPoint(FastMath.toRadians(location.getLat()), FastMath.toRadians(location.getLng()), location.getAlt());
        final TopocentricFrame tcf = new TopocentricFrame(OrekitUtil.EARTH, point, "point");
        
        ElevationExtremumDetector extremumDetector = new ElevationExtremumDetector(tcf)
                .withHandler(new RecordAndContinue<>());
        propagator.addEventDetector(extremumDetector);
        
        AbsoluteDate start = new AbsoluteDate(new Date(startMillis), TimeScalesFactory.getUTC());
        AbsoluteDate end = new AbsoluteDate(new Date(endMillis), TimeScalesFactory.getUTC());
        propagator.propagate(start, end);
        
        Iterator<EventDetector> itr = propagator.getEventsDetectors().iterator();
        ElevationExtremumDetector respDetector = (ElevationExtremumDetector) itr.next();
        RecordAndContinue handler = (RecordAndContinue) respDetector.getHandler();
        List<RecordAndContinue.Event> events = handler.getEvents();
        List<MaxElevationResp> respList = new ArrayList<>();
        for (RecordAndContinue.Event event : events) {
            SpacecraftState state = event.getState();
            ElevationExtremumDetector detector = (ElevationExtremumDetector) event.getDetector();
            double elevationDegrees = FastMath.toDegrees(detector.getElevation(state));
            if (elevationDegrees > 0) {
                MaxElevationResp maxElevationResp = new MaxElevationResp();
                Date date = state.getDate().toDate(TimeScalesFactory.getUTC());
                maxElevationResp.setDate(date);
                maxElevationResp.setMaxElevation(elevationDegrees);
                respList.add(maxElevationResp);
            }
        }
    }

Hi and welcome here,

If you’re using Orekit 12.2, you could create your own detector, that would basically wrap
ElevationExtremumDetector (possibly with AdapterDetector) and add in the init and finish methods (respectively called at propagation start and end) the computation and storage of the elevation.

Cheers,
Romain.

1 Like

Hi,

Serrof’s approach is probably cleaner, but another approach is that propagators have a method that allows you to get the initial spacecraft state, and the propagate method itself returns the final spacecraft state. One of those two must be the maximum over the interval if there was no sign change in the g function, i.e. no extremum event detected.

SpacecraftState satState = propagator.getInitialState();

double initialElevationRad = stationFrame.getElevation(satState.getPVCoordinates().getPosition(), satState.getFrame(), satState.getDate());

Likewise get the final elevation by the spacecraft state that was returned from the propagator by your propagator.propagate(start, end) call and compare the two.

Yeah that’s essentially what you would achieve with the init/finish method. And actually it’s even better to do it at EventHandler level. This way you could collect all local extrema as well, each time eventOccurred is called.

Totally untested, but if you want to use a handler approach for global min/max, give this a try:

import org.hipparchus.ode.events.Action;
import org.hipparchus.util.FastMath;
import org.orekit.propagation.SpacecraftState;
import org.orekit.propagation.events.EventDetector;
import org.orekit.propagation.events.ElevationExtremumDetector;
import org.orekit.propagation.events.handlers.EventHandler;
import org.orekit.time.AbsoluteDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class GlobalMinMaxHandler implements EventHandler 
{

    // Elevation values stored in degrees.
    private final List<Double> elevationValuesInDegrees = new ArrayList<>();

    /**
     * Returns the maximum elevation in degrees.
     *
     * @return maximum elevation (degrees)
     */
    public double getMaxInDegrees() 
    {
        if (elevationValuesInDegrees.isEmpty()) 
        {
            return Double.NaN;
        }
        else 
        {
            return Collections.max(elevationValuesInDegrees);
        }
    }

    /**
     * Returns the minimum elevation in degrees.
     *
     * @return minimum elevation (degrees)
     */
    public double getMinInDegrees() 
    {
        if (elevationValuesInDegrees.isEmpty()) 
        {
            return Double.NaN;
        }
        else 
        {
            return Collections.min(elevationValuesInDegrees);
        }
    }

       /**
     * Adds an elevation value (degrees) to the list.
     *
     * @param elevation elevation in degrees
     */
    public void addElevation(double elevation) 
    {
        elevationValuesInDegrees.add(elevation);
    }

    @Override
    public Action eventOccurred(SpacecraftState s, EventDetector detector, boolean increasing) {
        
        // Assuming the detector is an ElevationExtremumDetector,
        // use its getElevation method to get the current elevation.
        if (detector instanceof ElevationExtremumDetector) 
        {
            double elevationRad = ((ElevationExtremumDetector) detector).getElevation(s);
            addElevation(FastMath.toDegrees(elevationRad));
        }

        return Action.CONTINUE;
    }

    @Override
    public void init(SpacecraftState initialState, AbsoluteDate target, EventDetector detector)
    {
        // Initialize the list of elevation values.
        elevationValuesInDegrees.clear();
        
        // Add the initial elevation value.
        if (detector instanceof ElevationExtremumDetector) 
        {
            double elevationRad = ((ElevationExtremumDetector) detector).getElevation(initialState);
            addElevation(FastMath.toDegrees(elevationRad));
        }
    }

    @Override
    public void finish(SpacecraftState finalState, EventDetector detector)
    {
        // Add the final elevation value.
        if (detector instanceof ElevationExtremumDetector) 
        {
            double elevationRad = ((ElevationExtremumDetector) detector).getElevation(finalState);
            addElevation(FastMath.toDegrees(elevationRad));
        }
    }

    @Override
    public SpacecraftState resetState(EventDetector detector, SpacecraftState oldState) 
    {
        return oldState;
    }
}
2 Likes

Thanks for your reply! GlobalMinMaxHandler works great.

1 Like

Thanks for your reply! My Orekit version was 11.3, and I upgraded to 12.2.1, which fixed it.