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(propulsionModel.getMassDerivatives(s, getPropulsionModelParameters(parameters)));
  149.         }
  150.     }

  151.     /** {@inheritDoc} */
  152.     @Override
  153.     public <T extends CalculusFieldElement<T>> void addContribution(final FieldSpacecraftState<T> s,
  154.                         final FieldTimeDerivativesEquations<T> adder) {

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

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

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

  165.             // Compute flow rate using the propulsion model
  166.             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
  167.             adder.addMassDerivative(propulsionModel.getMassDerivatives(s, getPropulsionModelParameters(parameters)));
  168.         }
  169.     }

  170.     /** {@inheritDoc} */
  171.     @Override
  172.     public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {

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

  177.             // Attitude during maneuver
  178.             final Attitude maneuverAttitude;
  179.             if (attitudeOverride == null) {
  180.                 maneuverAttitude = s.getAttitude();
  181.             } else {
  182.                 final Rotation rotation = attitudeOverride.getAttitudeRotation(s, getAttitudeModelParameters(parameters));
  183.                 // use dummy rates to build full attitude as they should not be used
  184.                 maneuverAttitude = new Attitude(s.getDate(), s.getFrame(), rotation, Vector3D.ZERO, Vector3D.ZERO);
  185.             }

  186.             // Compute acceleration from propulsion model
  187.             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
  188.             return propulsionModel.getAcceleration(s, maneuverAttitude, getPropulsionModelParameters(parameters));
  189.         } else {
  190.             // Constant (and null) acceleration when not firing
  191.             return Vector3D.ZERO;
  192.         }
  193.     }

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

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

  201.             // Attitude during maneuver
  202.             final FieldAttitude<T> maneuverAttitude;
  203.             if (attitudeOverride == null) {
  204.                 maneuverAttitude = s.getAttitude();
  205.             } else {
  206.                 final FieldRotation<T> rotation = attitudeOverride.getAttitudeRotation(s, getAttitudeModelParameters(parameters));
  207.                 // use dummy rates to build full attitude as they should not be used
  208.                 final FieldVector3D<T> zeroVector3D = FieldVector3D.getZero(s.getDate().getField());
  209.                 maneuverAttitude = new FieldAttitude<>(s.getDate(), s.getFrame(), rotation, zeroVector3D, zeroVector3D);
  210.             }

  211.             // Compute acceleration from propulsion model
  212.             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
  213.             return propulsionModel.getAcceleration(s, maneuverAttitude, getPropulsionModelParameters(parameters));
  214.         } else {
  215.             // Constant (and null) acceleration when not firing
  216.             return FieldVector3D.getZero(s.getMass().getField());
  217.         }
  218.     }

  219.     /** {@inheritDoc} */
  220.     @Override
  221.     public Stream<EventDetector> getEventDetectors() {
  222.         // Event detectors are extracted from both the maneuver triggers and the propulsion model
  223.         return Stream.concat(maneuverTriggers.getEventDetectors(),
  224.                              propulsionModel.getEventDetectors());
  225.     }

  226.     /** {@inheritDoc} */
  227.     @Override
  228.     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
  229.         // Event detectors are extracted from both the maneuver triggers and the propulsion model
  230.         return Stream.concat(maneuverTriggers.getFieldEventDetectors(field),
  231.                              propulsionModel.getFieldEventDetectors(field));
  232.     }

  233.     /** {@inheritDoc} */
  234.     @Override
  235.     public List<ParameterDriver> getParametersDrivers() {
  236.         // Prepare final drivers' array
  237.         final List<ParameterDriver> drivers = new ArrayList<>();

  238.         // Convention: Propulsion drivers are given before maneuver triggers drivers
  239.         // Add propulsion drivers first
  240.         drivers.addAll(0, propulsionModel.getParametersDrivers());

  241.         // Then maneuver triggers' drivers
  242.         drivers.addAll(drivers.size(), maneuverTriggers.getParametersDrivers());

  243.         // Then attitude override' drivers if defined
  244.         if (attitudeOverride != null) {
  245.             drivers.addAll(attitudeOverride.getParametersDrivers());
  246.         }

  247.         // Return full drivers' array
  248.         return drivers;
  249.     }

  250.     /** Extract propulsion model parameters from the parameters' array called in by the ForceModel interface.
  251.      *  Convention: Propulsion parameters are given before maneuver triggers parameters
  252.      * @param parameters parameters' array called in by ForceModel interface
  253.      * @return propulsion model parameters
  254.      */
  255.     public double[] getPropulsionModelParameters(final double[] parameters) {
  256.         return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size());
  257.     }

  258.     /** Extract propulsion model parameters from the parameters' array called in by the ForceModel interface.
  259.      *  Convention: Propulsion parameters are given before maneuver triggers parameters
  260.      * @param parameters parameters' array called in by ForceModel interface
  261.      * @param <T> extends CalculusFieldElement&lt;T&gt;
  262.      * @return propulsion model parameters
  263.      */
  264.     public <T extends CalculusFieldElement<T>> T[] getPropulsionModelParameters(final T[] parameters) {
  265.         return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size());
  266.     }

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

  277.     /** Extract maneuver triggers' 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 maneuver triggers' parameters
  282.      */
  283.     public <T extends CalculusFieldElement<T>> T[] getManeuverTriggersParameters(final T[] parameters) {
  284.         final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size();
  285.         return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers,
  286.                                   nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size());
  287.     }

  288.     /** Extract attitude model' parameters from the parameters' array called in by the ForceModel interface.
  289.      *  Convention: Attitude model parameters are given last
  290.      * @param parameters parameters' array called in by ForceModel interface
  291.      * @return maneuver triggers' parameters
  292.      */
  293.     protected double[] getAttitudeModelParameters(final double[] parameters) {
  294.         final int nbAttitudeModelDrivers = (attitudeOverride == null) ? 0 : attitudeOverride.getParametersDrivers().size();
  295.         return Arrays.copyOfRange(parameters, parameters.length - nbAttitudeModelDrivers, parameters.length);
  296.     }

  297.     /** Extract attitude model' parameters from the parameters' array called in by the ForceModel interface.
  298.      *  Convention: Attitude parameters are given last
  299.      * @param parameters parameters' array called in by ForceModel interface
  300.      * @param <T> extends CalculusFieldElement&lt;T&gt;
  301.      * @return maneuver triggers' parameters
  302.      */
  303.     protected <T extends CalculusFieldElement<T>> T[] getAttitudeModelParameters(final T[] parameters) {
  304.         final int nbAttitudeModelDrivers = (attitudeOverride == null) ? 0 : attitudeOverride.getParametersDrivers().size();
  305.         return Arrays.copyOfRange(parameters, parameters.length - nbAttitudeModelDrivers, parameters.length);
  306.     }
  307. }