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
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.AttitudeRotationModel;
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), as well as the attitude override if set.
57 * The convention here is the following: drivers from propulsion model first, then maneuver triggers and if any the attitude override 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 AttitudeRotationModel 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 AttitudeRotationModel 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.isEmpty()) {
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 * @since 13.0
108 */
109 public AttitudeRotationModel getAttitudeOverride() {
110 return attitudeOverride;
111 }
112
113 /** Get the control vector's cost type.
114 * @return control cost type
115 * @since 12.0
116 */
117 public Control3DVectorCostType getControl3DVectorCostType() {
118 return propulsionModel.getControl3DVectorCostType();
119 }
120
121 /** Get the propulsion model.
122 * @return the propulsion model
123 */
124 public PropulsionModel getPropulsionModel() {
125 return propulsionModel;
126 }
127
128 /** Get the maneuver triggers.
129 * @return the maneuver triggers
130 */
131 public ManeuverTriggers getManeuverTriggers() {
132 return maneuverTriggers;
133 }
134
135 /** {@inheritDoc} */
136 @Override
137 public boolean dependsOnPositionOnly() {
138 return false;
139 }
140
141 /** {@inheritDoc} */
142 @Override
143 public void init(final SpacecraftState initialState, final AbsoluteDate target) {
144 propulsionModel.init(initialState, target);
145 maneuverTriggers.init(initialState, target);
146 }
147
148 /** {@inheritDoc} */
149 @Override
150 public <T extends CalculusFieldElement<T>> void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) {
151 propulsionModel.init(initialState, target);
152 maneuverTriggers.init(initialState, target);
153 }
154
155 /** {@inheritDoc} */
156 @Override
157 public void addContribution(final SpacecraftState s, final TimeDerivativesEquations adder) {
158
159 // Get the parameters associated to the maneuver (from ForceModel)
160 final double[] parameters = getParameters(s.getDate());
161
162 // If the maneuver is active, compute and add its contribution
163 // Maneuver triggers are used to check if the maneuver is currently firing or not
164 // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
165 if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {
166
167 // Compute thrust acceleration in inertial frame
168 adder.addNonKeplerianAcceleration(acceleration(s, parameters));
169
170 // Compute flow rate using the propulsion model
171 // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
172 adder.addMassDerivative(propulsionModel.getMassDerivatives(s, getPropulsionModelParameters(parameters)));
173 }
174 }
175
176 /** {@inheritDoc} */
177 @Override
178 public <T extends CalculusFieldElement<T>> void addContribution(final FieldSpacecraftState<T> s,
179 final FieldTimeDerivativesEquations<T> adder) {
180
181 // Get the parameters associated to the maneuver (from ForceModel)
182 final T[] parameters = getParameters(s.getDate().getField(), s.getDate());
183
184 // If the maneuver is active, compute and add its contribution
185 // Maneuver triggers are used to check if the maneuver is currently firing or not
186 // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
187 if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {
188
189 // Compute thrust acceleration in inertial frame
190 // the acceleration method extracts the parameter in its core, that is why we call it with
191 // parameters and not extracted parameters
192 adder.addNonKeplerianAcceleration(acceleration(s, parameters));
193
194 // Compute flow rate using the propulsion model
195 // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
196 adder.addMassDerivative(propulsionModel.getMassDerivatives(s, getPropulsionModelParameters(parameters)));
197 }
198 }
199
200 /** {@inheritDoc} */
201 @Override
202 public Vector3D acceleration(final SpacecraftState s, final double[] parameters) {
203
204 // If the maneuver is active, compute and add its contribution
205 // Maneuver triggers are used to check if the maneuver is currently firing or not
206 // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
207 if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {
208
209 // Attitude during maneuver
210 final Attitude maneuverAttitude;
211 if (attitudeOverride == null) {
212 maneuverAttitude = s.getAttitude();
213 } else {
214 final Rotation rotation = attitudeOverride.getAttitudeRotation(s, getAttitudeModelParameters(parameters));
215 // use dummy rates to build full attitude as they should not be used
216 maneuverAttitude = new Attitude(s.getDate(), s.getFrame(), rotation, Vector3D.ZERO, Vector3D.ZERO);
217 }
218
219 // Compute acceleration from propulsion model
220 // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
221 return propulsionModel.getAcceleration(s, maneuverAttitude, getPropulsionModelParameters(parameters));
222 } else {
223 // Constant (and null) acceleration when not firing
224 return Vector3D.ZERO;
225 }
226 }
227
228 /** {@inheritDoc} */
229 @Override
230 public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> s, final T[] parameters) {
231
232 // If the maneuver is active, compute and add its contribution
233 // Maneuver triggers are used to check if the maneuver is currently firing or not
234 // Specific drivers for the triggers are extracted from the array given by the ForceModel interface
235 if (maneuverTriggers.isFiring(s.getDate(), getManeuverTriggersParameters(parameters))) {
236
237 // Attitude during maneuver
238 final FieldAttitude<T> maneuverAttitude;
239 if (attitudeOverride == null) {
240 maneuverAttitude = s.getAttitude();
241 } else {
242 final FieldRotation<T> rotation = attitudeOverride.getAttitudeRotation(s, getAttitudeModelParameters(parameters));
243 // use dummy rates to build full attitude as they should not be used
244 final FieldVector3D<T> zeroVector3D = FieldVector3D.getZero(s.getDate().getField());
245 maneuverAttitude = new FieldAttitude<>(s.getDate(), s.getFrame(), rotation, zeroVector3D, zeroVector3D);
246 }
247
248 // Compute acceleration from propulsion model
249 // Specific drivers for the propulsion model are extracted from the array given by the ForceModel interface
250 return propulsionModel.getAcceleration(s, maneuverAttitude, getPropulsionModelParameters(parameters));
251 } else {
252 // Constant (and null) acceleration when not firing
253 return FieldVector3D.getZero(s.getMass().getField());
254 }
255 }
256
257 /** {@inheritDoc} */
258 @Override
259 public Stream<EventDetector> getEventDetectors() {
260 // Event detectors are extracted from both the maneuver triggers and the propulsion model
261 return Stream.concat(maneuverTriggers.getEventDetectors(),
262 propulsionModel.getEventDetectors());
263 }
264
265 /** {@inheritDoc} */
266 @Override
267 public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
268 // Event detectors are extracted from both the maneuver triggers and the propulsion model
269 return Stream.concat(maneuverTriggers.getFieldEventDetectors(field),
270 propulsionModel.getFieldEventDetectors(field));
271 }
272
273 /** {@inheritDoc} */
274 @Override
275 public List<ParameterDriver> getParametersDrivers() {
276 // Prepare final drivers' array
277 final List<ParameterDriver> drivers = new ArrayList<>();
278
279 // Convention: Propulsion drivers are given before maneuver triggers drivers
280 // Add propulsion drivers first
281 drivers.addAll(0, propulsionModel.getParametersDrivers());
282
283 // Then maneuver triggers' drivers
284 drivers.addAll(drivers.size(), maneuverTriggers.getParametersDrivers());
285
286 // Then attitude override' drivers if defined
287 if (attitudeOverride != null) {
288 drivers.addAll(attitudeOverride.getParametersDrivers());
289 }
290
291 // Return full drivers' array
292 return drivers;
293 }
294
295 /** Extract propulsion model parameters from the parameters' array called in by the ForceModel interface.
296 * Convention: Propulsion parameters are given before maneuver triggers parameters
297 * @param parameters parameters' array called in by ForceModel interface
298 * @return propulsion model parameters
299 */
300 public double[] getPropulsionModelParameters(final double[] parameters) {
301 return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size());
302 }
303
304 /** Extract propulsion model parameters from the parameters' array called in by the ForceModel interface.
305 * Convention: Propulsion parameters are given before maneuver triggers parameters
306 * @param parameters parameters' array called in by ForceModel interface
307 * @param <T> extends CalculusFieldElement<T>
308 * @return propulsion model parameters
309 */
310 public <T extends CalculusFieldElement<T>> T[] getPropulsionModelParameters(final T[] parameters) {
311 return Arrays.copyOfRange(parameters, 0, propulsionModel.getParametersDrivers().size());
312 }
313
314 /** Extract maneuver triggers' parameters from the parameters' array called in by the ForceModel interface.
315 * Convention: Propulsion parameters are given before maneuver triggers parameters
316 * @param parameters parameters' array called in by ForceModel interface
317 * @return maneuver triggers' parameters
318 */
319 public double[] getManeuverTriggersParameters(final double[] parameters) {
320 final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size();
321 return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers,
322 nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size());
323 }
324
325 /** Extract maneuver triggers' parameters from the parameters' array called in by the ForceModel interface.
326 * Convention: Propulsion parameters are given before maneuver triggers parameters
327 * @param parameters parameters' array called in by ForceModel interface
328 * @param <T> extends CalculusFieldElement<T>
329 * @return maneuver triggers' parameters
330 */
331 public <T extends CalculusFieldElement<T>> T[] getManeuverTriggersParameters(final T[] parameters) {
332 final int nbPropulsionModelDrivers = propulsionModel.getParametersDrivers().size();
333 return Arrays.copyOfRange(parameters, nbPropulsionModelDrivers,
334 nbPropulsionModelDrivers + maneuverTriggers.getParametersDrivers().size());
335 }
336
337 /** Extract attitude model' parameters from the parameters' array called in by the ForceModel interface.
338 * Convention: Attitude model parameters are given last
339 * @param parameters parameters' array called in by ForceModel interface
340 * @return maneuver triggers' parameters
341 */
342 protected double[] getAttitudeModelParameters(final double[] parameters) {
343 final int nbAttitudeModelDrivers = (attitudeOverride == null) ? 0 : attitudeOverride.getParametersDrivers().size();
344 return Arrays.copyOfRange(parameters, parameters.length - nbAttitudeModelDrivers, parameters.length);
345 }
346
347 /** Extract attitude model' parameters from the parameters' array called in by the ForceModel interface.
348 * Convention: Attitude parameters are given last
349 * @param parameters parameters' array called in by ForceModel interface
350 * @param <T> extends CalculusFieldElement<T>
351 * @return maneuver triggers' parameters
352 */
353 protected <T extends CalculusFieldElement<T>> T[] getAttitudeModelParameters(final T[] parameters) {
354 final int nbAttitudeModelDrivers = (attitudeOverride == null) ? 0 : attitudeOverride.getParametersDrivers().size();
355 return Arrays.copyOfRange(parameters, parameters.length - nbAttitudeModelDrivers, parameters.length);
356 }
357 }