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