Maneuver.java

  1. /* Copyright 2002-2025 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;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.List;
  21. import java.util.stream.Stream;

  22. import org.hipparchus.CalculusFieldElement;
  23. import org.hipparchus.Field;
  24. import org.hipparchus.geometry.euclidean.threed.FieldRotation;
  25. import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
  26. import org.hipparchus.geometry.euclidean.threed.Rotation;
  27. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  28. import org.orekit.attitudes.Attitude;
  29. import org.orekit.attitudes.AttitudeRotationModel;
  30. import org.orekit.attitudes.FieldAttitude;
  31. import org.orekit.forces.ForceModel;
  32. import org.orekit.forces.maneuvers.propulsion.PropulsionModel;
  33. import org.orekit.forces.maneuvers.trigger.ManeuverTriggers;
  34. import org.orekit.propagation.FieldSpacecraftState;
  35. import org.orekit.propagation.SpacecraftState;
  36. import org.orekit.propagation.events.EventDetector;
  37. import org.orekit.propagation.events.FieldEventDetector;
  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.ParameterDriver;


  43. /** A generic model for maneuvers with finite-valued acceleration magnitude, as opposed to instantaneous changes
  44.  * in the velocity vector which are defined via detectors (in {@link org.orekit.forces.maneuvers.ImpulseManeuver} and
  45.  * {@link org.orekit.forces.maneuvers.FieldImpulseManeuver}).
  46.  * It contains:
  47.  *  - An attitude override, this is the attitude used during the maneuver, it can be different from the one
  48.  *    used for propagation;
  49.  *  - A maneuver triggers object from the trigger sub-package. It defines the triggers used to start and stop the maneuvers (dates or events for example).
  50.  *  - A propulsion model from sub-package propulsion. It defines the thrust or ΔV, isp, flow rate etc.
  51.  * Both the propulsion model and the maneuver triggers can contain parameter drivers (for estimation), as well as the attitude override if set.
  52.  * The convention here is the following: drivers from propulsion model first, then maneuver triggers and if any the attitude override when calling the
  53.  * method {@link #getParametersDrivers()}
  54.  * @author Maxime Journot
  55.  * @since 10.2
  56.  */
  57. public class Maneuver implements ForceModel {

  58.     /** The attitude to override during the maneuver, if set. */
  59.     private final AttitudeRotationModel attitudeOverride;

  60.     /** Propulsion model to use for the thrust. */
  61.     private final PropulsionModel propulsionModel;

  62.     /** Maneuver triggers. */
  63.     private final ManeuverTriggers maneuverTriggers;

  64.     /** Generic maneuver constructor.
  65.      * @param attitudeOverride attitude provider for the attitude during the maneuver
  66.      * @param maneuverTriggers maneuver triggers
  67.      * @param propulsionModel propulsion model
  68.      */
  69.     public Maneuver(final AttitudeRotationModel attitudeOverride,
  70.                     final ManeuverTriggers maneuverTriggers,
  71.                     final PropulsionModel propulsionModel) {
  72.         this.maneuverTriggers = maneuverTriggers;
  73.         this.attitudeOverride = attitudeOverride;
  74.         this.propulsionModel = propulsionModel;
  75.     }

  76.     /** Get the name of the maneuver.
  77.      * The name can be in the propulsion model, in the maneuver triggers or both.
  78.      * If it is in both it should be the same since it refers to the same maneuver.
  79.      * The name is inferred from the propulsion model first, then from the maneuver triggers if
  80.      * the propulsion model had an empty name.
  81.      * @return the name
  82.      */
  83.     public String getName() {

  84.         //FIXME: Potentially, throw an exception if both propulsion model
  85.         // and maneuver triggers define a name but they are different
  86.         String name = propulsionModel.getName();

  87.         if (name.isEmpty()) {
  88.             name = maneuverTriggers.getName();
  89.         }
  90.         return name;
  91.     }

  92.     /** Get the attitude override used for the maneuver.
  93.      * @return the attitude override
  94.      * @since 13.0
  95.      */
  96.     public AttitudeRotationModel getAttitudeOverride() {
  97.         return attitudeOverride;
  98.     }

  99.     /** Get the control vector's cost type.
  100.      * @return control cost type
  101.      * @since 12.0
  102.      */
  103.     public Control3DVectorCostType getControl3DVectorCostType() {
  104.         return propulsionModel.getControl3DVectorCostType();
  105.     }

  106.     /** Get the propulsion model.
  107.      * @return the propulsion model
  108.      */
  109.     public PropulsionModel getPropulsionModel() {
  110.         return propulsionModel;
  111.     }

  112.     /** Get the maneuver triggers.
  113.      * @return the maneuver triggers
  114.      */
  115.     public ManeuverTriggers getManeuverTriggers() {
  116.         return maneuverTriggers;
  117.     }

  118.     /** {@inheritDoc} */
  119.     @Override
  120.     public boolean dependsOnPositionOnly() {
  121.         return false;
  122.     }

  123.     /** {@inheritDoc} */
  124.     @Override
  125.     public void init(final SpacecraftState initialState, final AbsoluteDate target) {
  126.         propulsionModel.init(initialState, target);
  127.         maneuverTriggers.init(initialState, target);
  128.     }

  129.     /** {@inheritDoc} */
  130.     @Override
  131.     public <T extends CalculusFieldElement<T>> void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) {
  132.         propulsionModel.init(initialState, target);
  133.         maneuverTriggers.init(initialState, target);
  134.     }

  135.     /** {@inheritDoc} */
  136.     @Override
  137.     public void addContribution(final SpacecraftState s, final TimeDerivativesEquations adder) {

  138.         // Get the parameters associated to the maneuver (from ForceModel)
  139.         final double[] parameters = getParameters(s.getDate());

  140.         // If the maneuver is active, compute and add its contribution
  141.         // Maneuver triggers are used to check if the maneuver is currently firing or not
  142.         // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
  143.         if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {

  144.             // Compute thrust acceleration in inertial frame
  145.             adder.addNonKeplerianAcceleration(acceleration(s, parameters));

  146.             // Compute flow rate using the propulsion model
  147.             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
  148.             adder.addMassDerivative(getMassDerivative(s, getPropulsionModelParameters(parameters)));
  149.         }
  150.     }

  151.     /** {@inheritDoc} */
  152.     @Override
  153.     public double getMassDerivative(final SpacecraftState state, final double[] parameters) {
  154.         if (maneuverTriggers.isFiring(state.getDate(), getManeuverTriggersParameters(parameters))) {
  155.             return propulsionModel.getMassDerivatives(state, getPropulsionModelParameters(parameters));
  156.         } else {
  157.             return 0.;
  158.         }
  159.     }

  160.     /** {@inheritDoc} */
  161.     @Override
  162.     public <T extends CalculusFieldElement<T>> void addContribution(final FieldSpacecraftState<T> s,
  163.                         final FieldTimeDerivativesEquations<T> adder) {

  164.         // Get the parameters associated to the maneuver (from ForceModel)
  165.         final T[] parameters = getParameters(s.getDate().getField(), s.getDate());

  166.         // If the maneuver is active, compute and add its contribution
  167.         // Maneuver triggers are used to check if the maneuver is currently firing or not
  168.         // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
  169.         if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {

  170.             // Compute thrust acceleration in inertial frame
  171.             // the acceleration method extracts the parameter in its core, that is why we call it with
  172.             // parameters and not extracted parameters
  173.             adder.addNonKeplerianAcceleration(acceleration(s, parameters));

  174.             // Compute flow rate using the propulsion model
  175.             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
  176.             adder.addMassDerivative(getMassDerivative(s, parameters));
  177.         }
  178.     }

  179.     /** {@inheritDoc} */
  180.     @Override
  181.     public <T extends CalculusFieldElement<T>> T getMassDerivative(final FieldSpacecraftState<T> state,
  182.                                                                    final T[] parameters) {
  183.         if (maneuverTriggers.isFiring(state.getDate(), getManeuverTriggersParameters(parameters))) {
  184.             return propulsionModel.getMassDerivatives(state, getPropulsionModelParameters(parameters));
  185.         } else {
  186.             return state.getMass().getField().getZero();
  187.         }
  188.     }

  189.     /** {@inheritDoc} */
  190.     @Override
  191.     public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {

  192.         // If the maneuver is active, compute and add its contribution
  193.         // Maneuver triggers are used to check if the maneuver is currently firing or not
  194.         // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
  195.         if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {

  196.             // Attitude during maneuver
  197.             final Attitude maneuverAttitude;
  198.             if (attitudeOverride == null) {
  199.                 maneuverAttitude = s.getAttitude();
  200.             } else {
  201.                 final Rotation rotation = attitudeOverride.getAttitudeRotation(s, getAttitudeModelParameters(parameters));
  202.                 // use dummy rates to build full attitude as they should not be used
  203.                 maneuverAttitude = new Attitude(s.getDate(), s.getFrame(), rotation, Vector3D.ZERO, Vector3D.ZERO);
  204.             }

  205.             // Compute acceleration from propulsion model
  206.             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
  207.             return propulsionModel.getAcceleration(s, maneuverAttitude, getPropulsionModelParameters(parameters));
  208.         } else {
  209.             // Constant (and null) acceleration when not firing
  210.             return Vector3D.ZERO;
  211.         }
  212.     }

  213.     /** {@inheritDoc} */
  214.     @Override
  215.     public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s, final T[] parameters) {

  216.         // If the maneuver is active, compute and add its contribution
  217.         // Maneuver triggers are used to check if the maneuver is currently firing or not
  218.         // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
  219.         if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {

  220.             // Attitude during maneuver
  221.             final FieldAttitude<T> maneuverAttitude;
  222.             if (attitudeOverride == null) {
  223.                 maneuverAttitude = s.getAttitude();
  224.             } else {
  225.                 final FieldRotation<T> rotation = attitudeOverride.getAttitudeRotation(s, getAttitudeModelParameters(parameters));
  226.                 // use dummy rates to build full attitude as they should not be used
  227.                 final FieldVector3D<T> zeroVector3D = FieldVector3D.getZero(s.getDate().getField());
  228.                 maneuverAttitude = new FieldAttitude<>(s.getDate(), s.getFrame(), rotation, zeroVector3D, zeroVector3D);
  229.             }

  230.             // Compute acceleration from propulsion model
  231.             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
  232.             return propulsionModel.getAcceleration(s, maneuverAttitude, getPropulsionModelParameters(parameters));
  233.         } else {
  234.             // Constant (and null) acceleration when not firing
  235.             return FieldVector3D.getZero(s.getMass().getField());
  236.         }
  237.     }

  238.     /** {@inheritDoc} */
  239.     @Override
  240.     public Stream<EventDetector> getEventDetectors() {
  241.         // Event detectors are extracted from both the maneuver triggers and the propulsion model
  242.         return Stream.concat(maneuverTriggers.getEventDetectors(),
  243.                              propulsionModel.getEventDetectors());
  244.     }

  245.     /** {@inheritDoc} */
  246.     @Override
  247.     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
  248.         // Event detectors are extracted from both the maneuver triggers and the propulsion model
  249.         return Stream.concat(maneuverTriggers.getFieldEventDetectors(field),
  250.                              propulsionModel.getFieldEventDetectors(field));
  251.     }

  252.     /** {@inheritDoc} */
  253.     @Override
  254.     public List<ParameterDriver> getParametersDrivers() {
  255.         // Prepare final drivers' array
  256.         final List<ParameterDriver> drivers = new ArrayList<>();

  257.         // Convention: Propulsion drivers are given before maneuver triggers drivers
  258.         // Add propulsion drivers first
  259.         drivers.addAll(0, propulsionModel.getParametersDrivers());

  260.         // Then maneuver triggers' drivers
  261.         drivers.addAll(drivers.size(), maneuverTriggers.getParametersDrivers());

  262.         // Then attitude override' drivers if defined
  263.         if (attitudeOverride != null) {
  264.             drivers.addAll(attitudeOverride.getParametersDrivers());
  265.         }

  266.         // Return full drivers' array
  267.         return drivers;
  268.     }

  269.     /** Extract propulsion model parameters from the parameters' array called in by the ForceModel interface.
  270.      *  Convention: Propulsion parameters are given before maneuver triggers parameters
  271.      * @param parameters parameters' array called in by ForceModel interface
  272.      * @return propulsion model parameters
  273.      */
  274.     public double[] getPropulsionModelParameters(final double[] parameters) {
  275.         return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size());
  276.     }

  277.     /** Extract propulsion model parameters from the parameters' array called in by the ForceModel interface.
  278.      *  Convention: Propulsion parameters are given before maneuver triggers parameters
  279.      * @param parameters parameters' array called in by ForceModel interface
  280.      * @param <T> extends CalculusFieldElement&lt;T&gt;
  281.      * @return propulsion model parameters
  282.      */
  283.     public <T extends CalculusFieldElement<T>> T[] getPropulsionModelParameters(final T[] parameters) {
  284.         return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size());
  285.     }

  286.     /** Extract maneuver triggers' parameters from the parameters' array called in by the ForceModel interface.
  287.      *  Convention: Propulsion parameters are given before maneuver triggers parameters
  288.      * @param parameters parameters' array called in by ForceModel interface
  289.      * @return maneuver triggers' parameters
  290.      */
  291.     public double[] getManeuverTriggersParameters(final double[] parameters) {
  292.         final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size();
  293.         return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers,
  294.                                   nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size());
  295.     }

  296.     /** Extract maneuver triggers' parameters from the parameters' array called in by the ForceModel interface.
  297.      *  Convention: Propulsion parameters are given before maneuver triggers parameters
  298.      * @param parameters parameters' array called in by ForceModel interface
  299.      * @param <T> extends CalculusFieldElement&lt;T&gt;
  300.      * @return maneuver triggers' parameters
  301.      */
  302.     public <T extends CalculusFieldElement<T>> T[] getManeuverTriggersParameters(final T[] parameters) {
  303.         final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size();
  304.         return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers,
  305.                                   nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size());
  306.     }

  307.     /** Extract attitude model' parameters from the parameters' array called in by the ForceModel interface.
  308.      *  Convention: Attitude model parameters are given last
  309.      * @param parameters parameters' array called in by ForceModel interface
  310.      * @return maneuver triggers' parameters
  311.      */
  312.     protected double[] getAttitudeModelParameters(final double[] parameters) {
  313.         final int nbAttitudeModelDrivers = (attitudeOverride == null) ? 0 : attitudeOverride.getParametersDrivers().size();
  314.         return Arrays.copyOfRange(parameters, parameters.length - nbAttitudeModelDrivers, parameters.length);
  315.     }

  316.     /** Extract attitude model' parameters from the parameters' array called in by the ForceModel interface.
  317.      *  Convention: Attitude parameters are given last
  318.      * @param parameters parameters' array called in by ForceModel interface
  319.      * @param <T> extends CalculusFieldElement&lt;T&gt;
  320.      * @return maneuver triggers' parameters
  321.      */
  322.     protected <T extends CalculusFieldElement<T>> T[] getAttitudeModelParameters(final T[] parameters) {
  323.         final int nbAttitudeModelDrivers = (attitudeOverride == null) ? 0 : attitudeOverride.getParametersDrivers().size();
  324.         return Arrays.copyOfRange(parameters, parameters.length - nbAttitudeModelDrivers, parameters.length);
  325.     }
  326. }