EventBasedManeuverTriggers.java

  1. /* Copyright 2020 Exotrail
  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.  * Exotrail 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.stream.Stream;

  19. import org.hipparchus.Field;
  20. import org.hipparchus.CalculusFieldElement;
  21. import org.hipparchus.ode.events.Action;
  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.errors.OrekitMessages;
  24. import org.orekit.propagation.SpacecraftState;
  25. import org.orekit.propagation.events.AbstractDetector;
  26. import org.orekit.propagation.events.EventDetector;
  27. import org.orekit.propagation.events.FieldEventDetector;
  28. import org.orekit.propagation.events.handlers.EventHandler;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.FieldAbsoluteDate;

  31. /**
  32.  * Maneuver triggers based on start and stop detectors. This allow a succession
  33.  * of burn interval. The thruster starts firing when the start detector becomes
  34.  * positive. The thruster stops firing when the stop detector becomes positive.
  35.  * The 2 detectors should not be positive at the same time. A date detector is
  36.  * not suited as it does not delimit an interval. They can be both negative at
  37.  * the same time.
  38.  * @author Mikael Fillastre
  39.  * @author Andrea Fiorentino
  40.  * @since 10.2
  41.  */
  42. public class EventBasedManeuverTriggers implements ManeuverTriggers, EventHandler<EventDetector> {

  43.     /** Detector to start firing, only detect increasing sign change. */
  44.     private final AbstractDetector<? extends EventDetector> startFiringDetector;
  45.     /**
  46.      * Detector to stop firing, only detect increasing sign change. e.g. it can be a
  47.      * negate detector of the start detector
  48.      */
  49.     private final AbstractDetector<? extends EventDetector> stopFiringDetector;

  50.     /** Flag for allowing backward propagation. */
  51.     private final boolean allowBackwardPropagation;

  52.     /**
  53.      * Flag for init method, called several times : force models + each detector.
  54.      */
  55.     private boolean initialized;

  56.     /** Triggered date of engine start. */
  57.     private AbsoluteDate triggeredStart;

  58.     /** Triggered date of engine stop. */
  59.     private AbsoluteDate triggeredEnd;

  60.     /** Propagation direction. */
  61.     private boolean forward;

  62.     /**
  63.      * Constructor.
  64.      * <p>
  65.      * This legacy constructor forbids backward propagation.
  66.      * </p>
  67.      * @param startFiringDetector Detector to start firing, only detect increasing
  68.      *                            sign change
  69.      * @param stopFiringDetector  Detector to stop firing, only detect increasing
  70.      *                            sign change. e.g. it can be a negate detector of
  71.      *                            the start detector.
  72.      */
  73.     public EventBasedManeuverTriggers(final AbstractDetector<? extends EventDetector> startFiringDetector,
  74.                                       final AbstractDetector<? extends EventDetector> stopFiringDetector) {
  75.         this(startFiringDetector, stopFiringDetector, false);
  76.     }

  77.     /**
  78.      * Constructor.
  79.      * @param startFiringDetector Detector to start firing, only detect increasing
  80.      *                            sign change
  81.      * @param stopFiringDetector  Detector to stop firing, only detect increasing
  82.      *                            sign change. e.g. it can be a negate detector of
  83.      *                            the start detector.
  84.      * @param allowBackwardPropagation if true, backward propagation is allowed
  85.      * @since 11.1
  86.      */
  87.     public EventBasedManeuverTriggers(final AbstractDetector<? extends EventDetector> startFiringDetector,
  88.                                       final AbstractDetector<? extends EventDetector> stopFiringDetector,
  89.                                       final boolean allowBackwardPropagation) {
  90.         if (startFiringDetector == null) {
  91.             throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "stopFiringDetector",
  92.                     EventBasedManeuverTriggers.class.getSimpleName());
  93.         }
  94.         if (stopFiringDetector == null) {
  95.             throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "startFiringDetector",
  96.                     EventBasedManeuverTriggers.class.getSimpleName());
  97.         }
  98.         this.startFiringDetector      = startFiringDetector.withHandler(this);
  99.         this.stopFiringDetector       = stopFiringDetector.withHandler(this);
  100.         this.allowBackwardPropagation = allowBackwardPropagation;
  101.         this.triggeredStart           = null;
  102.         this.triggeredEnd             = null;
  103.         this.initialized              = false;
  104.         this.forward                  = true;

  105.     }

  106.     /**
  107.      * Getter for the start firing detector.
  108.      * @return Detectors to start firing,
  109.      */
  110.     public AbstractDetector<? extends EventDetector> getStartFiringDetector() {
  111.         return startFiringDetector;
  112.     }

  113.     /**
  114.      * Getter for the stop firing detector.
  115.      * @return Detectors to stop firing
  116.      */
  117.     public AbstractDetector<? extends EventDetector> getStopFiringDetector() {
  118.         return stopFiringDetector;
  119.     }

  120.     /** {@inheritDoc} */
  121.     @Override
  122.     public void init(final SpacecraftState initialState, final AbsoluteDate target) {

  123.         if (!initialized) {

  124.             initialized = true;
  125.             forward     = target.isAfterOrEqualTo(initialState);
  126.             if (!forward && !allowBackwardPropagation) {
  127.                 // backward propagation was forbidden
  128.                 throw new OrekitException(OrekitMessages.BACKWARD_PROPAGATION_NOT_ALLOWED);
  129.             }
  130.             startFiringDetector.init(initialState, target);
  131.             stopFiringDetector.init(initialState, target);

  132.             checkInitialFiringState(initialState);

  133.         } // multiples calls to init : because it is a force model and by each detector
  134.     }

  135.     /**
  136.      * Method to set the firing state on initialization. can be overloaded by sub
  137.      * classes.
  138.      *
  139.      * @param initialState initial spacecraft state
  140.      */
  141.     protected void checkInitialFiringState(final SpacecraftState initialState) {
  142.         if (isFiringOnInitialState(initialState)) {
  143.             setFiring(true, initialState.getDate());
  144.         }
  145.     }

  146.     /**
  147.      * Method to check if the thruster is firing on initialization. can be called by
  148.      * sub classes
  149.      *
  150.      * @param initialState initial spacecraft state
  151.      * @return true if firing
  152.      */
  153.     protected boolean isFiringOnInitialState(final SpacecraftState initialState) {
  154.         // set the initial value of firing
  155.         final double insideThrustArcG = getStartFiringDetector().g(initialState);
  156.         boolean isInsideThrustArc = false;

  157.         if (insideThrustArcG == 0) {
  158.             // bound of arc
  159.             // check state for the next second (which can be forward or backward)
  160.             final double nextSecond = forward ? 1 : -1;
  161.             final double nextValue  = getStartFiringDetector().g(initialState.shiftedBy(nextSecond));
  162.             isInsideThrustArc = nextValue > 0;
  163.         } else {
  164.             isInsideThrustArc = insideThrustArcG > 0;
  165.         }
  166.         return isInsideThrustArc;
  167.     }

  168.     /** {@inheritDoc} */
  169.     @Override
  170.     public Stream<EventDetector> getEventsDetectors() {
  171.         return Stream.of(getStartFiringDetector(), getStopFiringDetector());
  172.     }

  173.     /** {@inheritDoc} */
  174.     @Override
  175.     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
  176.         // not implemented, it depends on the input detectors
  177.         throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED,
  178.                 "EventBasedManeuverTriggers.getFieldEventsDetectors");
  179.     }

  180.     /**
  181.      * Set the firing start or end date depending on the firing flag. There is no
  182.      * effect if the firing state is not changing.
  183.      * @param firing true to start a maneuver, false to stop
  184.      * @param date   date of event
  185.      */
  186.     public void setFiring(final boolean firing, final AbsoluteDate date) {
  187.         if (forward) {
  188.             if (firing) {
  189.                 if (!date.equals(triggeredEnd)) {
  190.                     triggeredStart = date;
  191.                     triggeredEnd   = null;
  192.                 } // else no gap between stop and start, can not handle correctly : skip it
  193.             } else {
  194.                 triggeredEnd = date;
  195.             }
  196.         } else { // backward propagation
  197.             if (firing) { // start firing by end date
  198.                 if (!date.equals(triggeredStart)) {
  199.                     triggeredEnd   = date;
  200.                     triggeredStart = null;
  201.                 } // else no gap between stop and start, can not handle correctly : skip it
  202.             } else {
  203.                 triggeredStart = date;
  204.             }
  205.         }
  206.     }

  207.     /** {@inheritDoc} */
  208.     @Override
  209.     public boolean isFiring(final AbsoluteDate date, final double[] parameters) {
  210.         // Firing state does not depend on a parameter driver here
  211.         return isFiring(date);
  212.     }

  213.     /** {@inheritDoc} */
  214.     @Override
  215.     public <T extends CalculusFieldElement<T>> boolean isFiring(final FieldAbsoluteDate<T> date, final T[] parameters) {
  216.         // Firing state does not depend on a parameter driver here
  217.         return isFiring(date.toAbsoluteDate());
  218.     }

  219.     /** {@inheritDoc} */
  220.     @Override
  221.     public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
  222.         Action action = Action.CONTINUE; // default not taken into account
  223.         final boolean detectorManaged = getEventsDetectors().anyMatch(managedDetector -> managedDetector.equals(detector));
  224.         if (detectorManaged) {
  225.             if (increasing) {
  226.                 action = Action.RESET_EVENTS;
  227.                 if (forward) {
  228.                     if (detector.equals(startFiringDetector)) { // start of firing arc
  229.                         setFiring(true, s.getDate());
  230.                         action = Action.RESET_DERIVATIVES;
  231.                     } else if (detector.equals(stopFiringDetector)) { // end of firing arc
  232.                         setFiring(false, s.getDate());
  233.                         action = Action.RESET_DERIVATIVES;
  234.                     }
  235.                 } else { // backward propagation. We could write a code on 3 lines but that would be
  236.                     // harder to understand and debug. So we do prefer explicit code
  237.                     if (detector.equals(startFiringDetector)) { // end of firing arc
  238.                         setFiring(false, s.getDate());
  239.                         action = Action.RESET_DERIVATIVES;
  240.                     } else if (detector.equals(stopFiringDetector)) { // start of firing arc
  241.                         setFiring(true, s.getDate());
  242.                         action = Action.RESET_DERIVATIVES;
  243.                     }
  244.                 }
  245.             }
  246.         }
  247.         return action;
  248.     }

  249.     /**
  250.      * Check if maneuvering is on.
  251.      *
  252.      * @param date current date
  253.      * @return true if maneuver is on at this date
  254.      */
  255.     public boolean isFiring(final AbsoluteDate date) {
  256.         if (forward) {
  257.             if (triggeredStart == null) {
  258.                 // explicitly ignores state date, as propagator did not allow us to introduce
  259.                 // discontinuity
  260.                 return false;
  261.             } else if (date.isBefore(triggeredStart)) {
  262.                 // we are unambiguously before maneuver start
  263.                 // robustness, we should not pass here
  264.                 return false;
  265.             } else {
  266.                 // after start date
  267.                 if (triggeredEnd == null) {
  268.                     // explicitly ignores state date, as propagator did not allow us to introduce
  269.                     // discontinuity
  270.                     return true;
  271.                 } else if (date.isBefore(triggeredEnd)) {
  272.                     // we are unambiguously before maneuver end
  273.                     // robustness, we should not pass here
  274.                     return true;
  275.                 } else {
  276.                     // we are at or after maneuver end
  277.                     return false;
  278.                 }
  279.             }
  280.         } else { // backward propagation, start firing by triggeredEnd
  281.             if (triggeredEnd == null) {
  282.                 // explicitly ignores state date, as propagator did not allow us to introduce
  283.                 // discontinuity
  284.                 return false;
  285.             } else if (date.isAfter(triggeredEnd)) {
  286.                 // we are unambiguously after maneuver end
  287.                 return false;
  288.             } else {
  289.                 if (triggeredStart == null) {
  290.                     // explicitly ignores state date, as propagator did not allow us to introduce
  291.                     // discontinuity
  292.                     return true;
  293.                 } else if (date.isAfter(triggeredStart)) {
  294.                     // we are unambiguously after maneuver start
  295.                     return true;
  296.                 } else {
  297.                     // we are at or before maneuver start
  298.                     return false;
  299.                 }
  300.             }
  301.         }
  302.     }

  303.     /**
  304.      * Getter for the triggered date of engine stop.
  305.      * @return Triggered date of engine stop
  306.      */
  307.     public AbsoluteDate getTriggeredEnd() {
  308.         return triggeredEnd;
  309.     }

  310.     /**
  311.      * Getter triggered date of engine start.
  312.      * @return Triggered date of engine start
  313.      */
  314.     public AbsoluteDate getTriggeredStart() {
  315.         return triggeredStart;
  316.     }

  317. }