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