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