ConstantThrustManeuver.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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;

  18. import java.util.stream.Stream;

  19. import org.hipparchus.Field;
  20. import org.hipparchus.RealFieldElement;
  21. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  22. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  23. import org.hipparchus.util.FastMath;
  24. import org.orekit.attitudes.Attitude;
  25. import org.orekit.attitudes.AttitudeProvider;
  26. import org.orekit.attitudes.FieldAttitude;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitInternalError;
  29. import org.orekit.forces.AbstractForceModel;
  30. import org.orekit.propagation.FieldSpacecraftState;
  31. import org.orekit.propagation.SpacecraftState;
  32. import org.orekit.propagation.events.DateDetector;
  33. import org.orekit.propagation.events.EventDetector;
  34. import org.orekit.propagation.events.FieldDateDetector;
  35. import org.orekit.propagation.events.FieldEventDetector;
  36. import org.orekit.propagation.events.handlers.EventHandler;
  37. import org.orekit.propagation.events.handlers.FieldEventHandler;
  38. import org.orekit.propagation.numerical.FieldTimeDerivativesEquations;
  39. import org.orekit.propagation.numerical.TimeDerivativesEquations;
  40. import org.orekit.time.AbsoluteDate;
  41. import org.orekit.time.FieldAbsoluteDate;
  42. import org.orekit.utils.Constants;
  43. import org.orekit.utils.ParameterDriver;

  44. /** This class implements a simple maneuver with constant thrust.
  45.  * <p>The maneuver is defined by a direction in satellite frame.
  46.  * The current attitude of the spacecraft, defined by the current
  47.  * spacecraft state, will be used to compute the thrust direction in
  48.  * inertial frame. A typical case for tangential maneuvers is to use a
  49.  * {@link org.orekit.attitudes.LofOffset LOF aligned} attitude provider
  50.  * for state propagation and a velocity increment along the +X satellite axis.</p>
  51.  * @author Fabien Maussion
  52.  * @author V&eacute;ronique Pommier-Maurussane
  53.  * @author Luc Maisonobe
  54.  */
  55. public class ConstantThrustManeuver extends AbstractForceModel {

  56.     /** Parameter name for thrust. */
  57.     public static final String THRUST = "thrust";

  58.     /** Parameter name for flow rate. */
  59.     public static final String FLOW_RATE = "flow rate";

  60.     /** Thrust scaling factor.
  61.      * <p>
  62.      * We use a power of 2 to avoid numeric noise introduction
  63.      * in the multiplications/divisions sequences.
  64.      * </p>
  65.      */
  66.     private static final double THRUST_SCALE = FastMath.scalb(1.0, -5);

  67.     /** Flow rate scaling factor.
  68.      * <p>
  69.      * We use a power of 2 to avoid numeric noise introduction
  70.      * in the multiplications/divisions sequences.
  71.      * </p>
  72.      */
  73.     private static final double FLOW_RATE_SCALE = FastMath.scalb(1.0, -12);

  74.     /** Driver for thrust parameter. */
  75.     private final ParameterDriver thrustDriver;

  76.     /** Driver for flow rate parameter. */
  77.     private final ParameterDriver flowRateDriver;

  78.     /** State of the engine. */
  79.     private boolean firing;

  80.     /** Start of the maneuver. */
  81.     private final AbsoluteDate startDate;

  82.     /** End of the maneuver. */
  83.     private final AbsoluteDate endDate;

  84.     /** The attitude to override during the maneuver, if set. */
  85.     private final AttitudeProvider attitudeOverride;

  86.     /** Direction of the acceleration in satellite frame. */
  87.     private final Vector3D direction;

  88.     /** User-defined name of the maneuver.
  89.      * This String attribute is empty by default.
  90.      * It is added as a prefix to the parameter drivers of the maneuver.
  91.      * The purpose is to differentiate between drivers in the case where several maneuvers
  92.      * were added to a propagator force model.
  93.      * Additionally, the user can retrieve the whole maneuver by looping on the force models of a propagator,
  94.      * scanning for its name.
  95.      * @since 9.2
  96.      */
  97.     private final String name;

  98.     /** Simple constructor for a constant direction and constant thrust.
  99.      * <p>
  100.      * Calling this constructor is equivalent to call {@link
  101.      * #ConstantThrustManeuver(AbsoluteDate, double, double, double, Vector3D, String)
  102.      * ConstantThrustManeuver(date, duration, thrust, isp, direction, "")},
  103.      * hence not using any prefix for the parameters drivers names.
  104.      * </p>
  105.      * @param date maneuver date
  106.      * @param duration the duration of the thrust (s) (if negative,
  107.      * the date is considered to be the stop date)
  108.      * @param thrust the thrust force (N)
  109.      * @param isp engine specific impulse (s)
  110.      * @param direction the acceleration direction in satellite frame.
  111.      */
  112.     public ConstantThrustManeuver(final AbsoluteDate date, final double duration,
  113.                                   final double thrust, final double isp,
  114.                                   final Vector3D direction) {
  115.         this(date, duration, thrust, isp, direction, "");
  116.     }

  117.     /** Simple constructor for a constant direction and constant thrust.
  118.      * <p>
  119.      * Calling this constructor is equivalent to call {@link
  120.      * #ConstantThrustManeuver(AbsoluteDate, double, double, double, Vector3D, String)
  121.      * ConstantThrustManeuver(date, duration, thrust, isp, direction, "")},
  122.      * hence not using any prefix for the parameters drivers names.
  123.      * </p>
  124.      * @param date maneuver date
  125.      * @param duration the duration of the thrust (s) (if negative,
  126.      * the date is considered to be the stop date)
  127.      * @param thrust the thrust force (N)
  128.      * @param isp engine specific impulse (s)
  129.      * @param attitudeOverride the attitude provider to use for the maneuver, or
  130.      * null if the attitude from the propagator should be used
  131.      * @param direction the acceleration direction in satellite frame.
  132.      * @since 9.2
  133.      */
  134.     public ConstantThrustManeuver(final AbsoluteDate date, final double duration,
  135.                                   final double thrust, final double isp,
  136.                                   final AttitudeProvider attitudeOverride, final Vector3D direction) {
  137.         this(date, duration, thrust, isp, attitudeOverride, direction, "");
  138.     }

  139.     /** Simple constructor for a constant direction and constant thrust.
  140.      * <p>
  141.      * If the {@code driversNamePrefix} is empty, the names will
  142.      * be {@link #THRUST "thrust"} and {@link #FLOW_RATE "flow rate"}, otherwise
  143.      * the prefix is prepended to these fixed strings. A typical use case is to
  144.      * use something like "1A-" or "2B-" as a prefix corresponding to the
  145.      * name of the thruster to use, so separate parameters can be adjusted
  146.      * for the different thrusters involved during an orbit determination
  147.      * where maneuvers parameters are estimated.
  148.      * </p>
  149.      * @param date maneuver date
  150.      * @param duration the duration of the thrust (s) (if negative,
  151.      * the date is considered to be the stop date)
  152.      * @param thrust the thrust force (N)
  153.      * @param isp engine specific impulse (s)
  154.      * @param direction the acceleration direction in satellite frame
  155.      * @param name name of the maneuver, used as a prefix for the {@link #getParametersDrivers() parameters drivers}
  156.      * @since 9.0
  157.      */
  158.     public ConstantThrustManeuver(final AbsoluteDate date, final double duration,
  159.                                   final double thrust, final double isp,
  160.                                   final Vector3D direction,
  161.                                   final String name) {
  162.         this(date, duration, thrust, isp, null, direction, name);
  163.     }

  164.     /** Simple constructor for a constant direction and constant thrust.
  165.      * <p>
  166.      * If the {@code driversNamePrefix} is empty, the names will
  167.      * be {@link #THRUST "thrust"} and {@link #FLOW_RATE "flow rate"}, otherwise
  168.      * the prefix is prepended to these fixed strings. A typical use case is to
  169.      * use something like "1A-" or "2B-" as a prefix corresponding to the
  170.      * name of the thruster to use, so separate parameters can be adjusted
  171.      * for the different thrusters involved during an orbit determination
  172.      * where maneuvers parameters are estimated.
  173.      * </p>
  174.      * @param date maneuver date
  175.      * @param duration the duration of the thrust (s) (if negative,
  176.      * the date is considered to be the stop date)
  177.      * @param thrust the thrust force (N)
  178.      * @param isp engine specific impulse (s)
  179.      * @param attitudeOverride the attitude provider to use for the maneuver, or
  180.      * null if the attitude from the propagator should be used
  181.      * @param direction the acceleration direction in satellite frame
  182.      * @param name name of the maneuver, used as a prefix for the {@link #getParametersDrivers() parameters drivers}
  183.      * @since 9.2
  184.      */
  185.     public ConstantThrustManeuver(final AbsoluteDate date, final double duration,
  186.                                   final double thrust, final double isp,
  187.                                   final AttitudeProvider attitudeOverride, final Vector3D direction,
  188.                                   final String name) {

  189.         if (duration >= 0) {
  190.             this.startDate = date;
  191.             this.endDate   = date.shiftedBy(duration);
  192.         } else {
  193.             this.endDate   = date;
  194.             this.startDate = endDate.shiftedBy(duration);
  195.         }

  196.         final double flowRate  = -thrust / (Constants.G0_STANDARD_GRAVITY * isp);
  197.         this.attitudeOverride = attitudeOverride;
  198.         this.direction = direction.normalize();
  199.         this.name = name;
  200.         firing = false;

  201.         // Build the parameter drivers, using maneuver name as prefix
  202.         ParameterDriver tpd = null;
  203.         ParameterDriver fpd = null;
  204.         try {
  205.             tpd = new ParameterDriver(name + THRUST, thrust, THRUST_SCALE,
  206.                                       0.0, Double.POSITIVE_INFINITY);
  207.             fpd = new ParameterDriver(name + FLOW_RATE, flowRate, FLOW_RATE_SCALE,
  208.                                       Double.NEGATIVE_INFINITY, 0.0 );
  209.         } catch (OrekitException oe) {
  210.             // this should never occur as valueChanged above never throws an exception
  211.             throw new OrekitInternalError(oe);
  212.         }

  213.         this.thrustDriver   = tpd;
  214.         this.flowRateDriver = fpd;

  215.     }

  216.     /** {@inheritDoc} */
  217.     @Override
  218.     public boolean dependsOnPositionOnly() {
  219.         return false;
  220.     }

  221.     /** {@inheritDoc} */
  222.     @Override
  223.     public void init(final SpacecraftState s0, final AbsoluteDate t) {
  224.         // set the initial value of firing
  225.         final AbsoluteDate sDate = s0.getDate();
  226.         final boolean isForward = sDate.compareTo(t) < 0;
  227.         final boolean isBetween =
  228.                 startDate.compareTo(sDate) < 0 && endDate.compareTo(sDate) > 0;
  229.         final boolean isOnStart = startDate.compareTo(sDate) == 0;
  230.         final boolean isOnEnd = endDate.compareTo(sDate) == 0;

  231.         firing = isBetween || (isForward && isOnStart) || (!isForward && isOnEnd);
  232.     }

  233.     /** Get the thrust.
  234.      * @return thrust force (N).
  235.      */
  236.     public double getThrust() {
  237.         return thrustDriver.getValue();
  238.     }

  239.     /** Get the specific impulse.
  240.      * @return specific impulse (s).
  241.      */
  242.     public double getISP() {
  243.         final double thrust   = getThrust();
  244.         final double flowRate = getFlowRate();
  245.         return -thrust / (Constants.G0_STANDARD_GRAVITY * flowRate);
  246.     }

  247.     /** Get the flow rate.
  248.      * @return flow rate (negative, kg/s).
  249.      */
  250.     public double getFlowRate() {
  251.         return flowRateDriver.getValue();
  252.     }

  253.     /** Get the direction.
  254.      * @return the direction
  255.      * @since 9.2
  256.      */
  257.     public Vector3D getDirection() {
  258.         return direction;
  259.     }

  260.     /** Get the name.
  261.      * @return the name
  262.      * @since 9.2
  263.      */
  264.     public String getName() {
  265.         return name;
  266.     }

  267.     /** Get the start date.
  268.      * @return the start date
  269.      * @since 9.2
  270.      */
  271.     public AbsoluteDate getStartDate() {
  272.         return startDate;
  273.     }

  274.     /** Get the end date.
  275.      * @return the end date
  276.      * @since 9.2
  277.      */
  278.     public AbsoluteDate getEndDate() {
  279.         return endDate;
  280.     }

  281.     /** Get the duration of the maneuver (s).
  282.      * duration = endDate - startDate
  283.      * @return the duration of the maneuver (s)
  284.      * @since 9.2
  285.      */
  286.     public double getDuration() {
  287.         return endDate.durationFrom(startDate);
  288.     }

  289.     /** Get the attitude override used for the maneuver.
  290.      * @return the attitude override
  291.      * @since 9.2
  292.      */
  293.     public AttitudeProvider getAttitudeOverride() {
  294.         return attitudeOverride;
  295.     }

  296.     /** {@inheritDoc} */
  297.     @Override
  298.     public void addContribution(final SpacecraftState s, final TimeDerivativesEquations adder)
  299.         throws OrekitException {

  300.         if (firing) {

  301.             // compute thrust acceleration in inertial frame
  302.             final double[] parameters = getParameters();
  303.             adder.addNonKeplerianAcceleration(acceleration(s, parameters));

  304.             // compute flow rate
  305.             adder.addMassDerivative(parameters[1]);

  306.         }
  307.     }

  308.     /** {@inheritDoc} */
  309.     @Override
  310.     public <T extends RealFieldElement<T>> void
  311.         addContribution(final FieldSpacecraftState<T> s,
  312.                         final FieldTimeDerivativesEquations<T> adder)
  313.         throws OrekitException {
  314.         if (firing) {

  315.             final T[] parameters = getParameters(s.getDate().getField());

  316.             // compute thrust acceleration in inertial frame
  317.             adder.addNonKeplerianAcceleration(acceleration(s, parameters));

  318.             // compute flow rate
  319.             adder.addMassDerivative(parameters[1]);
  320.         }
  321.     }

  322.     /** {@inheritDoc} */
  323.     @Override
  324.     public Vector3D acceleration(final SpacecraftState state, final double[] parameters)
  325.         throws OrekitException {
  326.         if (firing) {
  327.             final double thrust = parameters[0];
  328.             final Attitude attitude =
  329.                             attitudeOverride == null ?
  330.                             state.getAttitude() :
  331.                             attitudeOverride.getAttitude(state.getOrbit(),
  332.                                                          state.getDate(),
  333.                                                          state.getFrame());
  334.             return new Vector3D(thrust / state.getMass(),
  335.                                 attitude.getRotation().applyInverseTo(direction));
  336.         } else {
  337.             return Vector3D.ZERO;
  338.         }
  339.     }

  340.     /** {@inheritDoc} */
  341.     @Override
  342.     public <T extends RealFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s,
  343.                                                                          final T[] parameters)
  344.         throws OrekitException {
  345.         if (firing) {
  346.             // compute thrust acceleration in inertial frame
  347.             final T thrust = parameters[0];
  348.             final FieldAttitude<T> attitude =
  349.                             attitudeOverride == null ?
  350.                             s.getAttitude() :
  351.                             attitudeOverride.getAttitude(s.getOrbit(),
  352.                                                          s.getDate(),
  353.                                                          s.getFrame());
  354.             return new FieldVector3D<>(s.getMass().reciprocal().multiply(thrust),
  355.                                        attitude.getRotation().applyInverseTo(direction));
  356.         } else {
  357.             // constant (and null) acceleration when not firing
  358.             return FieldVector3D.getZero(s.getMass().getField());
  359.         }
  360.     }

  361.     /** {@inheritDoc} */
  362.     @Override
  363.     public Stream<EventDetector> getEventsDetectors() {
  364.         // in forward propagation direction, firing must be enabled
  365.         // at start time and disabled at end time; in backward
  366.         // propagation direction, firing must be enabled
  367.         // at end time and disabled at start time
  368.         final DateDetector startDetector = new DateDetector(startDate).
  369.             withHandler((SpacecraftState state, DateDetector d, boolean increasing) -> {
  370.                 firing = d.isForward();
  371.                 return EventHandler.Action.RESET_DERIVATIVES;
  372.             });
  373.         final DateDetector endDetector = new DateDetector(endDate).
  374.             withHandler((SpacecraftState state, DateDetector d, boolean increasing) -> {
  375.                 firing = !d.isForward();
  376.                 return EventHandler.Action.RESET_DERIVATIVES;
  377.             });
  378.         return Stream.of(startDetector, endDetector);
  379.     }

  380.     /** {@inheritDoc} */
  381.     @Override
  382.     public ParameterDriver[] getParametersDrivers() {
  383.         return new ParameterDriver[] {
  384.             thrustDriver, flowRateDriver
  385.         };
  386.     }

  387.     /** {@inheritDoc} */
  388.     @Override
  389.     public <T extends RealFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventsDetectors(final Field<T> field) {
  390.         // in forward propagation direction, firing must be enabled
  391.         // at start time and disabled at end time; in backward
  392.         // propagation direction, firing must be enabled
  393.         // at end time and disabled at start time
  394.         final FieldDateDetector<T> startDetector = new FieldDateDetector<>(new FieldAbsoluteDate<>(field, startDate)).
  395.             withHandler((FieldSpacecraftState<T> state, FieldDateDetector<T> d, boolean increasing) -> {
  396.                 firing = d.isForward();
  397.                 return FieldEventHandler.Action.RESET_DERIVATIVES;
  398.             });
  399.         final FieldDateDetector<T> endDetector = new FieldDateDetector<>(new FieldAbsoluteDate<>(field, endDate)).
  400.             withHandler((FieldSpacecraftState<T> state, FieldDateDetector<T> d, boolean increasing) -> {
  401.                 firing = !d.isForward();
  402.                 return FieldEventHandler.Action.RESET_DERIVATIVES;
  403.             });
  404.         return Stream.of(startDetector, endDetector);
  405.     }

  406. }