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.     /**
  51.      * Flag for init method, called several times : force models + each detector.
  52.      */
  53.     private boolean initialized;

  54.     /** Triggered date of engine start. */
  55.     private AbsoluteDate triggeredStart;

  56.     /** Triggered date of engine stop. */
  57.     private AbsoluteDate triggeredEnd;

  58.     /**
  59.      * Constructor.
  60.      * @param startFiringDetector Detector to start firing, only detect increasing
  61.      *                            sign change
  62.      * @param stopFiringDetector  Detector to stop firing, only detect increasing
  63.      *                            sign change. e.g. it can be a negate detector of
  64.      *                            the start detector.
  65.      */
  66.     public EventBasedManeuverTriggers(final AbstractDetector<? extends EventDetector> startFiringDetector,
  67.             final AbstractDetector<? extends EventDetector> stopFiringDetector) {
  68.         if (startFiringDetector == null) {
  69.             throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "stopFiringDetector",
  70.                     EventBasedManeuverTriggers.class.getSimpleName());
  71.         }
  72.         if (stopFiringDetector == null) {
  73.             throw new OrekitException(OrekitMessages.PARAMETER_NOT_SET, "startFiringDetector",
  74.                     EventBasedManeuverTriggers.class.getSimpleName());
  75.         }
  76.         this.startFiringDetector = startFiringDetector.withHandler(this);
  77.         this.stopFiringDetector = stopFiringDetector.withHandler(this);
  78.         this.triggeredStart = null;
  79.         this.triggeredEnd = null;
  80.         initialized = false;
  81.     }

  82.     /**
  83.      * Getter for the start firing detector.
  84.      * @return Detectors to start firing,
  85.      */
  86.     public AbstractDetector<? extends EventDetector> getStartFiringDetector() {
  87.         return startFiringDetector;
  88.     }

  89.     /**
  90.      * Getter for the stop firing detector.
  91.      * @return Detectors to stop firing
  92.      */
  93.     public AbstractDetector<? extends EventDetector> getStopFiringDetector() {
  94.         return stopFiringDetector;
  95.     }

  96.     /** {@inheritDoc} */
  97.     @Override
  98.     public void init(final SpacecraftState initialState, final AbsoluteDate target) {

  99.         if (!initialized) {

  100.             initialized = true;
  101.             final AbsoluteDate sDate = initialState.getDate();
  102.             if (sDate.compareTo(target) > 0) {
  103.                 // backward propagation not managed because events on detectors can not be
  104.                 // reversed :
  105.                 // the stop event of the maneuver in forward direction won't be the start in the
  106.                 // backward.
  107.                 // e.g. if a stop detector is combination of orbit position and system
  108.                 // constraint
  109.                 throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED,
  110.                         "EventBasedManeuverTriggers in backward propagation");
  111.             }

  112.             checkInitialFiringState(initialState);

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

  115.     /**
  116.      * Method to set the firing state on initialization. can be overloaded by sub
  117.      * classes.
  118.      *
  119.      * @param initialState initial spacecraft state
  120.      */
  121.     protected void checkInitialFiringState(final SpacecraftState initialState) {
  122.         if (isFiringOnInitialState(initialState)) {
  123.             setFiring(true, initialState.getDate());
  124.         }
  125.     }

  126.     /**
  127.      * Method to check if the thruster is firing on initialization. can be called by
  128.      * sub classes
  129.      *
  130.      * @param initialState initial spacecraft state
  131.      * @return true if firing
  132.      */
  133.     protected boolean isFiringOnInitialState(final SpacecraftState initialState) {
  134.         // set the initial value of firing
  135.         final double insideThrustArcG = getStartFiringDetector().g(initialState);
  136.         boolean isInsideThrustArc = false;

  137.         if (insideThrustArcG == 0) {
  138.             // bound of arc
  139.             // check state for the next second (which can be forward or backward)
  140.             final double nextSecond = 1;
  141.             final double nextValue = getStartFiringDetector().g(initialState.shiftedBy(nextSecond));
  142.             isInsideThrustArc = nextValue > 0;
  143.         } else {
  144.             isInsideThrustArc = insideThrustArcG > 0;
  145.         }
  146.         return isInsideThrustArc;
  147.     }

  148.     /** {@inheritDoc} */
  149.     @Override
  150.     public Stream<EventDetector> getEventsDetectors() {
  151.         return Stream.of(getStartFiringDetector(), getStopFiringDetector());
  152.     }

  153.     /** {@inheritDoc} */
  154.     @Override
  155.     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
  156.         // not implemented, it depends on the input detectors
  157.         throw new OrekitException(OrekitMessages.FUNCTION_NOT_IMPLEMENTED,
  158.                 "EventBasedManeuverTriggers.getFieldEventsDetectors");
  159.     }

  160.     /**
  161.      * Set the firing start or end date depending on the firing flag. There is no
  162.      * effect if the firing state is not changing.
  163.      * @param firing true to start a maneuver, false to stop
  164.      * @param date   date of event
  165.      */
  166.     public void setFiring(final boolean firing, final AbsoluteDate date) {
  167.         if (firing != isFiring(date)) {
  168.             if (firing) {
  169.                 if (!date.equals(triggeredEnd)) {
  170.                     triggeredStart = date;
  171.                 } // else no gap between stop and start, can not handle correctly : skip it
  172.             } else {
  173.                 triggeredEnd = date;
  174.             }
  175.         }
  176.     }

  177.     /** {@inheritDoc} */
  178.     @Override
  179.     public boolean isFiring(final AbsoluteDate date, final double[] parameters) {
  180.         // Firing state does not depend on a parameter driver here
  181.         return isFiring(date);
  182.     }

  183.     /** {@inheritDoc} */
  184.     @Override
  185.     public <T extends CalculusFieldElement<T>> boolean isFiring(final FieldAbsoluteDate<T> date, final T[] parameters) {
  186.         // Firing state does not depend on a parameter driver here
  187.         return isFiring(date.toAbsoluteDate());
  188.     }

  189.     /** {@inheritDoc} */
  190.     @Override
  191.     public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
  192.         Action action = Action.CONTINUE; // default not taken into account
  193.         final boolean detectorManaged = getEventsDetectors()
  194.                 .anyMatch(managedDetector -> managedDetector.equals(detector));
  195.         if (detectorManaged) {
  196.             action = Action.RESET_EVENTS;
  197.             if (increasing) {
  198.                 if (detector.equals(getStartFiringDetector())) { // start of firing arc
  199.                     setFiring(true, s.getDate());
  200.                     action = Action.RESET_DERIVATIVES;
  201.                 } else if (detector.equals(getStopFiringDetector())) { // end of firing arc
  202.                     setFiring(false, s.getDate());
  203.                     action = Action.RESET_DERIVATIVES;
  204.                 }
  205.             }
  206.         }
  207.         return action;
  208.     }

  209.     /**
  210.      * Check if maneuvering is on.
  211.      *
  212.      * @param date current date
  213.      * @return true if maneuver is on at this date
  214.      */
  215.     public boolean isFiring(final AbsoluteDate date) {
  216.         if (triggeredStart == null) {
  217.             // explicitly ignores state date, as propagator did not allow us to introduce
  218.             // discontinuity
  219.             return false;
  220.         } else if (date.isBefore(triggeredStart)) {
  221.             // we are unambiguously before maneuver start
  222.             // robustness, we should not pass here
  223.             return false;
  224.         } else {
  225.             // after start date
  226.             if (getTriggeredEnd() == null) {
  227.                 // explicitly ignores state date, as propagator did not allow us to introduce
  228.                 // discontinuity
  229.                 return true;
  230.             } else if (getTriggeredStart().isAfter(getTriggeredEnd())) {
  231.                 // last event is a start of maneuver, end not set yet
  232.                 // we are unambiguously before maneuver end
  233.                 return true;
  234.             } else if (date.isBefore(getTriggeredEnd())) {
  235.                 // we are unambiguously before maneuver end
  236.                 // robustness, we should not pass here
  237.                 return true;
  238.             } else {
  239.                 // we are at or after maneuver end
  240.                 return false;
  241.             }
  242.         }
  243.     }

  244.     /**
  245.      * Getter for the triggered date of engine stop.
  246.      * @return Triggered date of engine stop
  247.      */
  248.     public AbsoluteDate getTriggeredEnd() {
  249.         return triggeredEnd;
  250.     }

  251.     /**
  252.      * Getter triggered date of engine start.
  253.      * @return Triggered date of engine start
  254.      */
  255.     public AbsoluteDate getTriggeredStart() {
  256.         return triggeredStart;
  257.     }

  258. }