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