StartStopEventsTrigger.java

  1. /* Copyright 2002-2022 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.forces.maneuvers.trigger;

  18. import java.util.HashMap;
  19. import java.util.Map;
  20. import java.util.stream.Stream;

  21. import org.hipparchus.CalculusFieldElement;
  22. import org.hipparchus.Field;
  23. import org.hipparchus.ode.events.Action;
  24. import org.orekit.propagation.FieldSpacecraftState;
  25. import org.orekit.propagation.SpacecraftState;
  26. import org.orekit.propagation.events.AbstractDetector;
  27. import org.orekit.propagation.events.EventDetector;
  28. import org.orekit.propagation.events.FieldAbstractDetector;
  29. import org.orekit.propagation.events.FieldEventDetector;
  30. import org.orekit.propagation.events.handlers.EventHandler;
  31. import org.orekit.propagation.events.handlers.FieldEventHandler;
  32. import org.orekit.time.AbsoluteDate;
  33. import org.orekit.time.FieldAbsoluteDate;

  34. /**
  35.  * Maneuver triggers based on a pair of event detectors that defines firing start and stop.
  36.  * <p>
  37.  * The thruster starts firing when the start detector becomes
  38.  * positive. The thruster stops firing when the stop detector becomes positive.
  39.  * The 2 detectors should not be positive at the same time. A date detector is
  40.  * not suited as it does not delimit an interval. They can be both negative at
  41.  * the same time.
  42.  * </p>
  43.  * @param <A> type of the start detector
  44.  * @param <O> type of the stop detector
  45.  * @see IntervalEventTrigger
  46.  * @author Luc Maisonobe
  47.  * @since 11.1
  48.  */
  49. public abstract class StartStopEventsTrigger<A extends AbstractDetector<A>, O extends AbstractDetector<O>> extends AbstractManeuverTriggers {

  50.     /** Start detector. */
  51.     private final A startDetector;

  52.     /** Stop detector. */
  53.     private final O stopDetector;

  54.     /** Cached field-based start detectors. */
  55.     private final transient Map<Field<? extends CalculusFieldElement<?>>, FieldEventDetector<? extends CalculusFieldElement<?>>> cachedStart;

  56.     /** Cached field-based stop detectors. */
  57.     private final transient Map<Field<? extends CalculusFieldElement<?>>, FieldEventDetector<? extends CalculusFieldElement<?>>> cachedStop;

  58.     /** Simple constructor.
  59.      * <p>
  60.      * Note that the {@code startDetector} and {@code stopDetector} passed as an argument are used only
  61.      * as a <em>prototypes</em> from which new detectors will be built using their
  62.      * {@link AbstractDetector#withHandler(EventHandler) withHandler} methods to
  63.      * set up internal handlers. The original event handlers from the prototype
  64.      * will be <em>ignored</em> and never called.
  65.      * </p>
  66.      * <p>
  67.      * If the trigger is used in a {@link org.orekit.propagation.FieldPropagator field-based propagation},
  68.      * the detector will be automatically converted to a field equivalent. Beware however that the
  69.      * {@link FieldEventHandler#eventOccurred(FieldSpacecraftState, FieldEventDetector, boolean) eventOccurred}
  70.      * of the converted propagator <em>will</em> call the method with the same name in the prototype
  71.      * detector, in order to get the correct return value.
  72.      * </p>
  73.      * @param prototypeStartDetector prototype detector for firing start
  74.      * @param prototypeStopDetector prototype detector for firing stop
  75.      */
  76.     protected StartStopEventsTrigger(final A prototypeStartDetector, final O prototypeStopDetector) {

  77.         this.startDetector = prototypeStartDetector.withHandler(new StartHandler());
  78.         this.stopDetector  = prototypeStopDetector.withHandler(new StopHandler());
  79.         this.cachedStart   = new HashMap<>();
  80.         this.cachedStop    = new HashMap<>();

  81.     }

  82.     /**
  83.      * Getter for the firing start detector.
  84.      * @return firing start detector
  85.      */
  86.     public A getStartDetector() {
  87.         return startDetector;
  88.     }

  89.     /**
  90.      * Getter for the firing stop detector.
  91.      * @return firing stop detector
  92.      */
  93.     public O getStopDetector() {
  94.         return stopDetector;
  95.     }

  96.     /** {@inheritDoc} */
  97.     @Override
  98.     protected boolean isFiringOnInitialState(final SpacecraftState initialState, final boolean isForward) {

  99.         final double startG = startDetector.g(initialState);
  100.         if (startG == 0) {
  101.             final boolean increasing = startDetector.g(initialState.shiftedBy(2 * startDetector.getThreshold())) > 0;
  102.             if (increasing) {
  103.                 // we are at maneuver start
  104.                 notifyResetters(initialState, true);
  105.                 // if propagating forward, we start firing
  106.                 return isForward;
  107.             } else {
  108.                 // not a meaningful crossing
  109.                 return false;
  110.             }
  111.         } else if (startG < 0) {
  112.             // we are before start
  113.             return false;
  114.         } else {
  115.             // we are after start
  116.             final double stopG = stopDetector.g(initialState);
  117.             if (stopG == 0) {
  118.                 final boolean increasing = stopDetector.g(initialState.shiftedBy(2 * stopDetector.getThreshold())) > 0;
  119.                 if (increasing) {
  120.                     // we are at maneuver end
  121.                     notifyResetters(initialState, false);
  122.                     // if propagating backward, we start firing
  123.                     return !isForward;
  124.                 } else {
  125.                     // not a meaningful crossing
  126.                     return false;
  127.                 }
  128.             } else if (stopG > 0) {
  129.                 // we are after stop
  130.                 return false;
  131.             } else {
  132.                 // we are between start and stop
  133.                 return true;
  134.             }
  135.         }

  136.     }

  137.     /** {@inheritDoc} */
  138.     @Override
  139.     public Stream<EventDetector> getEventsDetectors() {
  140.         return Stream.of(startDetector, stopDetector);
  141.     }

  142.     /** {@inheritDoc} */
  143.     @Override
  144.     public <S extends CalculusFieldElement<S>> Stream<FieldEventDetector<S>> getFieldEventsDetectors(final Field<S> field) {

  145.         // get the field version of the start detector
  146.         @SuppressWarnings("unchecked")
  147.         FieldEventDetector<S> fStart = (FieldEventDetector<S>) cachedStart.get(field);
  148.         if (fStart == null) {
  149.             fStart = convertAndSetUpStartHandler(field);
  150.             cachedStart.put(field, fStart);
  151.         }

  152.         // get the field version of the stop detector
  153.         @SuppressWarnings("unchecked")
  154.         FieldEventDetector<S> fStop = (FieldEventDetector<S>) cachedStop.get(field);
  155.         if (fStop == null) {
  156.             fStop = convertAndSetUpStopHandler(field);
  157.             cachedStop.put(field, fStop);
  158.         }

  159.         return Stream.of(fStart, fStop);

  160.     }

  161.     /** Convert a detector and set up new handler.
  162.      * <p>
  163.      * This method is not inlined in {@link #getFieldEventsDetectors(Field)} because the
  164.      * parameterized types confuses the Java compiler.
  165.      * </p>
  166.      * @param field field to which the state belongs
  167.      * @param <D> type of the event detector
  168.      * @param <S> type of the field elements
  169.      * @return converted firing intervals detector
  170.      */
  171.     private <D extends FieldAbstractDetector<D, S>, S extends CalculusFieldElement<S>> D convertAndSetUpStartHandler(final Field<S> field) {
  172.         final FieldAbstractDetector<D, S> converted = convertStartDetector(field, startDetector);
  173.         return converted.
  174.                withMaxCheck(field.getZero().newInstance(startDetector.getMaxCheckInterval())).
  175.                withThreshold(field.getZero().newInstance(startDetector.getThreshold())).
  176.                withHandler(new FieldStartHandler<>());
  177.     }

  178.     /** Convert a detector and set up new handler.
  179.      * <p>
  180.      * This method is not inlined in {@link #getFieldEventsDetectors(Field)} because the
  181.      * parameterized types confuses the Java compiler.
  182.      * </p>
  183.      * @param field field to which the state belongs
  184.      * @param <D> type of the event detector
  185.      * @param <S> type of the field elements
  186.      * @return converted firing intervals detector
  187.      */
  188.     private <D extends FieldAbstractDetector<D, S>, S extends CalculusFieldElement<S>> D convertAndSetUpStopHandler(final Field<S> field) {
  189.         final FieldAbstractDetector<D, S> converted = convertStopDetector(field, stopDetector);
  190.         return converted.
  191.                withMaxCheck(field.getZero().newInstance(stopDetector.getMaxCheckInterval())).
  192.                withThreshold(field.getZero().newInstance(stopDetector.getThreshold())).
  193.                withHandler(new FieldStopHandler<>());
  194.     }

  195.     /** Convert a primitive firing start detector into a field firing start detector.
  196.      * <p>
  197.      * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(CalculusFieldElement) withMaxCheck},
  198.      * {@link FieldAbstractDetector#withThreshold(CalculusFieldElement) withThreshold}, or
  199.      * {@link FieldAbstractDetector#withHandler(org.orekit.propagation.events.handlers.FieldEventHandler) withHandler}
  200.      * in the converted detector, this will be done by caller.
  201.      * </p>
  202.      * <p>
  203.      * A skeleton implementation of this method to convert some {@code XyzDetector} into {@code FieldXyzDetector},
  204.      * considering these detectors are created from a date and a number parameter is:
  205.      * </p>
  206.      * <pre>{@code
  207.      *     protected <D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>>
  208.      *         FieldAbstractDetector<D, S> convertStartDetector(final Field<S> field, final XyzDetector detector) {
  209.      *
  210.      *         final FieldAbsoluteDate<S> date  = new FieldAbsoluteDate<>(field, detector.getDate());
  211.      *         final S                    param = field.getZero().newInstance(detector.getParam());
  212.      *
  213.      *         final FieldAbstractDetector<D, S> converted = (FieldAbstractDetector<D, S>) new FieldXyzDetector<>(date, param);
  214.      *         return converted;
  215.      *
  216.      *     }
  217.      * }
  218.      * </pre>
  219.      * @param field field to which the state belongs
  220.      * @param detector primitive firing start detector to convert
  221.      * @param <D> type of the event detector
  222.      * @param <S> type of the field elements
  223.      * @return converted firing start detector
  224.      */
  225.     protected abstract <D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>> FieldAbstractDetector<D, S>
  226.         convertStartDetector(Field<S> field, A detector);

  227.     /** Convert a primitive firing stop detector into a field firing stop detector.
  228.      * <p>
  229.      * There is not need to set up {@link FieldAbstractDetector#withMaxCheck(CalculusFieldElement) withMaxCheck},
  230.      * {@link FieldAbstractDetector#withThreshold(CalculusFieldElement) withThreshold}, or
  231.      * {@link FieldAbstractDetector#withHandler(org.orekit.propagation.events.handlers.FieldEventHandler) withHandler}
  232.      * in the converted detector, this will be done by caller.
  233.      * </p>
  234.      * <p>
  235.      * A skeleton implementation of this method to convert some {@code XyzDetector} into {@code FieldXyzDetector},
  236.      * considering these detectors are created from a date and a number parameter is:
  237.      * </p>
  238.      * <pre>{@code
  239.      *     protected <D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>>
  240.      *         FieldAbstractDetector<D, S> convertStopDetector(final Field<S> field, final XyzDetector detector) {
  241.      *
  242.      *         final FieldAbsoluteDate<S> date  = new FieldAbsoluteDate<>(field, detector.getDate());
  243.      *         final S                    param = field.getZero().newInstance(detector.getParam());
  244.      *
  245.      *         final FieldAbstractDetector<D, S> converted = (FieldAbstractDetector<D, S>) new FieldXyzDetector<>(date, param);
  246.      *         return converted;
  247.      *
  248.      *     }
  249.      * }
  250.      * </pre>
  251.      * @param field field to which the state belongs
  252.      * @param detector primitive firing stop detector to convert
  253.      * @param <D> type of the event detector
  254.      * @param <S> type of the field elements
  255.      * @return converted firing stop detector
  256.      */
  257.     protected abstract <D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>> FieldAbstractDetector<D, S>
  258.         convertStopDetector(Field<S> field, O detector);

  259.     /** Local handler for start triggers. */
  260.     private class StartHandler implements EventHandler<A> {

  261.         /** Propagation direction. */
  262.         private boolean forward;

  263.         /** {@inheritDoc} */
  264.         @Override
  265.         public void init(final SpacecraftState initialState, final AbsoluteDate target, final A detector) {
  266.             forward = target.isAfterOrEqualTo(initialState);
  267.             initializeResetters(initialState, target);
  268.         }

  269.         /** {@inheritDoc} */
  270.         @Override
  271.         public Action eventOccurred(final SpacecraftState s, final A detector, final boolean increasing) {
  272.             if (increasing) {
  273.                 // the event is meaningful for maneuver firing
  274.                 if (forward) {
  275.                     getFirings().addValidAfter(true, s.getDate(), false);
  276.                 } else {
  277.                     getFirings().addValidBefore(false, s.getDate(), false);
  278.                 }
  279.                 notifyResetters(s, true);
  280.                 return Action.RESET_STATE;
  281.             } else {
  282.                 // the event is not meaningful for maneuver firing
  283.                 return Action.CONTINUE;
  284.             }
  285.         }

  286.         /** {@inheritDoc} */
  287.         @Override
  288.         public SpacecraftState resetState(final A detector, final SpacecraftState oldState) {
  289.             return applyResetters(oldState);
  290.         }

  291.     }

  292.     /** Local handler for stop triggers. */
  293.     private class StopHandler implements EventHandler<O> {

  294.         /** Propagation direction. */
  295.         private boolean forward;

  296.         /** {@inheritDoc} */
  297.         @Override
  298.         public void init(final SpacecraftState initialState, final AbsoluteDate target, final O detector) {
  299.             forward = target.isAfterOrEqualTo(initialState);
  300.             initializeResetters(initialState, target);
  301.         }

  302.         /** {@inheritDoc} */
  303.         @Override
  304.         public Action eventOccurred(final SpacecraftState s, final O detector, final boolean increasing) {
  305.             if (increasing) {
  306.                 // the event is meaningful for maneuver firing
  307.                 if (forward) {
  308.                     getFirings().addValidAfter(false, s.getDate(), false);
  309.                 } else {
  310.                     getFirings().addValidBefore(true, s.getDate(), false);
  311.                 }
  312.                 notifyResetters(s, false);
  313.                 return Action.RESET_STATE;
  314.             } else {
  315.                 // the event is not meaningful for maneuver firing
  316.                 return Action.CONTINUE;
  317.             }
  318.         }

  319.         /** {@inheritDoc} */
  320.         @Override
  321.         public SpacecraftState resetState(final O detector, final SpacecraftState oldState) {
  322.             return applyResetters(oldState);
  323.         }

  324.     }

  325.     /** Local handler for start triggers.
  326.      * @param <S> type of the field elements
  327.      */
  328.     private class FieldStartHandler<D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>> implements FieldEventHandler<D, S> {

  329.         /** Propagation direction. */
  330.         private boolean forward;

  331.         /** {@inheritDoc} */
  332.         @Override
  333.         public void init(final FieldSpacecraftState<S> initialState,
  334.                          final FieldAbsoluteDate<S> target,
  335.                          final D detector) {
  336.             forward = target.isAfterOrEqualTo(initialState);
  337.             initializeResetters(initialState, target);
  338.         }

  339.         /** {@inheritDoc} */
  340.         @Override
  341.         public Action eventOccurred(final FieldSpacecraftState<S> s, final D detector, final boolean increasing) {
  342.             if (increasing) {
  343.                 // the event is meaningful for maneuver firing
  344.                 if (forward) {
  345.                     getFirings().addValidAfter(true, s.getDate().toAbsoluteDate(), false);
  346.                 } else {
  347.                     getFirings().addValidBefore(false, s.getDate().toAbsoluteDate(), false);
  348.                 }
  349.                 notifyResetters(s, true);
  350.                 return Action.RESET_STATE;
  351.             } else {
  352.                 // the event is not meaningful for maneuver firing
  353.                 return Action.CONTINUE;
  354.             }
  355.         }

  356.         /** {@inheritDoc} */
  357.         @Override
  358.         public FieldSpacecraftState<S> resetState(final D detector, final FieldSpacecraftState<S> oldState) {
  359.             return applyResetters(oldState);
  360.         }

  361.     }

  362.     /** Local handler for stop triggers.
  363.      * @param <S> type of the field elements
  364.      */
  365.     private class FieldStopHandler<D extends FieldEventDetector<S>, S extends CalculusFieldElement<S>> implements FieldEventHandler<D, S> {

  366.         /** Propagation direction. */
  367.         private boolean forward;

  368.         /** {@inheritDoc} */
  369.         @Override
  370.         public void init(final FieldSpacecraftState<S> initialState,
  371.                          final FieldAbsoluteDate<S> target,
  372.                          final D detector) {
  373.             forward = target.isAfterOrEqualTo(initialState);
  374.             initializeResetters(initialState, target);
  375.         }

  376.         /** {@inheritDoc} */
  377.         @Override
  378.         public Action eventOccurred(final FieldSpacecraftState<S> s, final D detector, final boolean increasing) {
  379.             if (increasing) {
  380.                 // the event is meaningful for maneuver firing
  381.                 if (forward) {
  382.                     getFirings().addValidAfter(false, s.getDate().toAbsoluteDate(), false);
  383.                 } else {
  384.                     getFirings().addValidBefore(true, s.getDate().toAbsoluteDate(), false);
  385.                 }
  386.                 notifyResetters(s, false);
  387.                 return Action.RESET_STATE;
  388.             } else {
  389.                 // the event is not meaningful for maneuver firing
  390.                 return Action.CONTINUE;
  391.             }
  392.         }

  393.         /** {@inheritDoc} */
  394.         @Override
  395.         public FieldSpacecraftState<S> resetState(final D detector, final FieldSpacecraftState<S> oldState) {
  396.             return applyResetters(oldState);
  397.         }

  398.     }

  399. }