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