1   /* Copyright 2002-2024 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  
18  package org.orekit.forces.maneuvers;
19  
20  import java.util.ArrayList;
21  import java.util.Arrays;
22  import java.util.List;
23  import java.util.stream.Stream;
24  
25  import org.hipparchus.CalculusFieldElement;
26  import org.hipparchus.Field;
27  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
28  import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
29  import org.hipparchus.geometry.euclidean.threed.Rotation;
30  import org.hipparchus.geometry.euclidean.threed.Vector3D;
31  import org.orekit.attitudes.Attitude;
32  import org.orekit.attitudes.AttitudeProvider;
33  import org.orekit.attitudes.FieldAttitude;
34  import org.orekit.forces.ForceModel;
35  import org.orekit.forces.maneuvers.propulsion.PropulsionModel;
36  import org.orekit.forces.maneuvers.trigger.ManeuverTriggers;
37  import org.orekit.propagation.FieldSpacecraftState;
38  import org.orekit.propagation.SpacecraftState;
39  import org.orekit.propagation.events.EventDetector;
40  import org.orekit.propagation.events.FieldEventDetector;
41  import org.orekit.propagation.numerical.FieldTimeDerivativesEquations;
42  import org.orekit.propagation.numerical.TimeDerivativesEquations;
43  import org.orekit.time.AbsoluteDate;
44  import org.orekit.time.FieldAbsoluteDate;
45  import org.orekit.utils.ParameterDriver;
46  
47  
48  /** A generic model for maneuvers with finite-valued acceleration magnitude, as opposed to instantaneous changes
49   * in the velocity vector which are defined via detectors (in {@link org.orekit.forces.maneuvers.ImpulseManeuver} and
50   * {@link org.orekit.forces.maneuvers.FieldImpulseManeuver}).
51   * It contains:
52   *  - An attitude override, this is the attitude used during the maneuver, it can be different from the one
53   *    used for propagation;
54   *  - 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).
55   *  - A propulsion model from sub-package propulsion. It defines the thrust or ΔV, isp, flow rate etc..
56   * Both the propulsion model and the maneuver triggers can contain parameter drivers (for estimation).
57   * The convention here is that the propulsion model drivers are given before the maneuver triggers when calling the
58   * method {@link #getParametersDrivers()}
59   * @author Maxime Journot
60   * @since 10.2
61   */
62  public class Maneuver implements ForceModel {
63  
64      /** The attitude to override during the maneuver, if set. */
65      private final AttitudeProvider attitudeOverride;
66  
67      /** Propulsion model to use for the thrust. */
68      private final PropulsionModel propulsionModel;
69  
70      /** Maneuver triggers. */
71      private final ManeuverTriggers maneuverTriggers;
72  
73      /** Generic maneuver constructor.
74       * @param attitudeOverride attitude provider for the attitude during the maneuver
75       * @param maneuverTriggers maneuver triggers
76       * @param propulsionModel propulsion model
77       */
78      public Maneuver(final AttitudeProvider attitudeOverride,
79                      final ManeuverTriggers maneuverTriggers,
80                      final PropulsionModel propulsionModel) {
81          this.maneuverTriggers = maneuverTriggers;
82          this.attitudeOverride = attitudeOverride;
83          this.propulsionModel = propulsionModel;
84      }
85  
86      /** Get the name of the maneuver.
87       * The name can be in the propulsion model, in the maneuver triggers or both.
88       * If it is in both it should be the same since it refers to the same maneuver.
89       * The name is inferred from the propulsion model first, then from the maneuver triggers if
90       * the propulsion model had an empty name.
91       * @return the name
92       */
93      public String getName() {
94  
95          //FIXME: Potentially, throw an exception if both propulsion model
96          // and maneuver triggers define a name but they are different
97          String name = propulsionModel.getName();
98  
99          if (name.length() < 1) {
100             name = maneuverTriggers.getName();
101         }
102         return name;
103     }
104 
105     /** Get the attitude override used for the maneuver.
106      * @return the attitude override
107      */
108     public AttitudeProvider getAttitudeOverride() {
109         return attitudeOverride;
110     }
111 
112     /** Get the control vector's cost type.
113      * @return control cost type
114      * @since 12.0
115      */
116     public Control3DVectorCostType getControl3DVectorCostType() {
117         return propulsionModel.getControl3DVectorCostType();
118     }
119 
120     /** Get the propulsion model.
121      * @return the propulsion model
122      */
123     public PropulsionModel getPropulsionModel() {
124         return propulsionModel;
125     }
126 
127     /** Get the maneuver triggers.
128      * @return the maneuver triggers
129      */
130     public ManeuverTriggers getManeuverTriggers() {
131         return maneuverTriggers;
132     }
133 
134     /** {@inheritDoc} */
135     @Override
136     public boolean dependsOnPositionOnly() {
137         return false;
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public void init(final SpacecraftState initialState, final AbsoluteDate target) {
143         propulsionModel.init(initialState, target);
144         maneuverTriggers.init(initialState, target);
145     }
146 
147     /** {@inheritDoc} */
148     @Override
149     public <T extends CalculusFieldElement<T>> void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) {
150         propulsionModel.init(initialState, target);
151         maneuverTriggers.init(initialState, target);
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public void addContribution(final SpacecraftState s, final TimeDerivativesEquations adder) {
157 
158         // Get the parameters associated to the maneuver (from ForceModel)
159         final double[] parameters = getParameters(s.getDate());
160 
161         // If the maneuver is active, compute and add its contribution
162         // Maneuver triggers are used to check if the maneuver is currently firing or not
163         // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
164         if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {
165 
166             // Compute thrust acceleration in inertial frame
167             adder.addNonKeplerianAcceleration(acceleration(s, parameters));
168 
169             // Compute flow rate using the propulsion model
170             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
171             adder.addMassDerivative(propulsionModel.getMassDerivatives(s, getPropulsionModelParameters(parameters)));
172         }
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     public <T extends CalculusFieldElement<T>> void addContribution(final FieldSpacecraftState<T> s,
178                         final FieldTimeDerivativesEquations<T> adder) {
179 
180         // Get the parameters associated to the maneuver (from ForceModel)
181         final T[] parameters = getParameters(s.getDate().getField(), s.getDate());
182 
183         // If the maneuver is active, compute and add its contribution
184         // Maneuver triggers are used to check if the maneuver is currently firing or not
185         // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
186         if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {
187 
188             // Compute thrust acceleration in inertial frame
189             // the acceleration method extracts the parameter in its core, that is why we call it with
190             // parameters and not extracted parameters
191             adder.addNonKeplerianAcceleration(acceleration(s, parameters));
192 
193             // Compute flow rate using the propulsion model
194             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
195             adder.addMassDerivative(propulsionModel.getMassDerivatives(s, getPropulsionModelParameters(parameters)));
196         }
197     }
198 
199     @Override
200     public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {
201 
202         // If the maneuver is active, compute and add its contribution
203         // Maneuver triggers are used to check if the maneuver is currently firing or not
204         // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
205         if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {
206 
207             // Attitude during maneuver
208             final Attitude maneuverAttitude;
209             if (attitudeOverride == null) {
210                 maneuverAttitude = s.getAttitude();
211             } else {
212                 final Rotation rotation = attitudeOverride.getAttitudeRotation(s.getOrbit(), s.getDate(), s.getFrame());
213                 // use dummy rates to build full attitude as they should not be used
214                 maneuverAttitude = new Attitude(s.getDate(), s.getFrame(), rotation, Vector3D.ZERO, Vector3D.ZERO);
215             }
216 
217             // Compute acceleration from propulsion model
218             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
219             return propulsionModel.getAcceleration(s, maneuverAttitude, getPropulsionModelParameters(parameters));
220         } else {
221             // Constant (and null) acceleration when not firing
222             return Vector3D.ZERO;
223         }
224     }
225 
226     @Override
227     public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s, final T[] parameters) {
228 
229         // If the maneuver is active, compute and add its contribution
230         // Maneuver triggers are used to check if the maneuver is currently firing or not
231         // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
232         if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {
233 
234             // Attitude during maneuver
235             final FieldAttitude<T> maneuverAttitude;
236             if (attitudeOverride == null) {
237                 maneuverAttitude = s.getAttitude();
238             } else {
239                 final FieldRotation<T> rotation = attitudeOverride.getAttitudeRotation(s.getOrbit(), s.getDate(), s.getFrame());
240                 // use dummy rates to build full attitude as they should not be used
241                 final FieldVector3D<T> zeroVector3D = FieldVector3D.getZero(s.getDate().getField());
242                 maneuverAttitude = new FieldAttitude<>(s.getDate(), s.getFrame(), rotation, zeroVector3D, zeroVector3D);
243             }
244 
245             // Compute acceleration from propulsion model
246             // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
247             return propulsionModel.getAcceleration(s, maneuverAttitude, getPropulsionModelParameters(parameters));
248         } else {
249             // Constant (and null) acceleration when not firing
250             return FieldVector3D.getZero(s.getMu().getField());
251         }
252     }
253 
254     /** {@inheritDoc} */
255     @Override
256     public Stream<EventDetector> getEventDetectors() {
257         // Event detectors are extracted from both the maneuver triggers and the propulsion model
258         return Stream.concat(maneuverTriggers.getEventDetectors(),
259                              propulsionModel.getEventDetectors());
260     }
261 
262     /** {@inheritDoc} */
263     @Override
264     public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
265         // Event detectors are extracted from both the maneuver triggers and the propulsion model
266         return Stream.concat(maneuverTriggers.getFieldEventDetectors(field),
267                              propulsionModel.getFieldEventDetectors(field));
268     }
269 
270     @Override
271     public List<ParameterDriver> getParametersDrivers() {
272 
273         // Extract parameter drivers from propulsion model and maneuver triggers
274         final List<ParameterDriver> propulsionModelDrivers  = propulsionModel.getParametersDrivers();
275         final List<ParameterDriver> maneuverTriggersDrivers = maneuverTriggers.getParametersDrivers();
276         final int propulsionModelDriversLength  = propulsionModelDrivers.size();
277         final int maneuverTriggersDriversLength = maneuverTriggersDrivers.size();
278 
279         // Prepare final drivers' array
280         final List<ParameterDriver> drivers = new ArrayList<>(propulsionModelDriversLength + maneuverTriggersDriversLength);
281 
282         // Convention: Propulsion drivers are given before maneuver triggers drivers
283         // Add propulsion drivers first
284         drivers.addAll(0, propulsionModelDrivers);
285 
286         // Then maneuver triggers' drivers
287         drivers.addAll(propulsionModelDriversLength, maneuverTriggersDrivers);
288 
289         // Return full drivers' array
290         return drivers;
291     }
292 
293     /** Extract propulsion model parameters from the parameters' array called in by the ForceModel interface.
294      *  Convention: Propulsion parameters are given before maneuver triggers parameters
295      * @param parameters parameters' array called in by ForceModel interface
296      * @return propulsion model parameters
297      */
298     public double[] getPropulsionModelParameters(final double[] parameters) {
299         return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size());
300     }
301 
302     /** Extract propulsion model parameters from the parameters' array called in by the ForceModel interface.
303      *  Convention: Propulsion parameters are given before maneuver triggers parameters
304      * @param parameters parameters' array called in by ForceModel interface
305      * @param <T> extends CalculusFieldElement&lt;T&gt;
306      * @return propulsion model parameters
307      */
308     public <T extends CalculusFieldElement<T>> T[] getPropulsionModelParameters(final T[] parameters) {
309         return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size());
310     }
311 
312     /** Extract maneuver triggers' parameters from the parameters' array called in by the ForceModel interface.
313      *  Convention: Propulsion parameters are given before maneuver triggers parameters
314      * @param parameters parameters' array called in by ForceModel interface
315      * @return maneuver triggers' parameters
316      */
317     public double[] getManeuverTriggersParameters(final double[] parameters) {
318         final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size();
319         return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers,
320                                   nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size());
321     }
322 
323     /** Extract maneuver triggers' parameters from the parameters' array called in by the ForceModel interface.
324      *  Convention: Propulsion parameters are given before maneuver triggers parameters
325      * @param parameters parameters' array called in by ForceModel interface
326      * @param <T> extends CalculusFieldElement&lt;T&gt;
327      * @return maneuver triggers' parameters
328      */
329     public <T extends CalculusFieldElement<T>> T[] getManeuverTriggersParameters(final T[] parameters) {
330         final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size();
331         return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers,
332                                   nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size());
333     }
334 }