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 package org.orekit.forces.empirical;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.stream.Stream;
23
24 import org.hipparchus.CalculusFieldElement;
25 import org.hipparchus.Field;
26 import org.hipparchus.geometry.euclidean.threed.FieldRotation;
27 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
28 import org.hipparchus.geometry.euclidean.threed.Rotation;
29 import org.hipparchus.geometry.euclidean.threed.Vector3D;
30 import org.hipparchus.util.MathArrays;
31 import org.orekit.attitudes.AttitudeProvider;
32 import org.orekit.forces.ForceModel;
33 import org.orekit.propagation.FieldSpacecraftState;
34 import org.orekit.propagation.SpacecraftState;
35 import org.orekit.propagation.events.EventDetector;
36 import org.orekit.propagation.events.FieldEventDetector;
37 import org.orekit.time.AbsoluteDate;
38 import org.orekit.time.FieldAbsoluteDate;
39 import org.orekit.utils.ParameterDriver;
40 import org.orekit.utils.TimeSpanMap;
41 import org.orekit.utils.TimeSpanMap.Span;
42
43 /** Time span parametric acceleration model.
44 * <p>
45 * This class is closely related to {@link org.orekit.forces.empirical.ParametricAcceleration ParametricAcceleration} class.<br>
46 * The difference is that it has a {@link TimeSpanMap} of {@link AccelerationModel} objects as attribute
47 * instead of a single {@link AccelerationModel} object. <br>
48 * The idea behind this model is to allow the user to design a parametric acceleration model that can see its physical parameters
49 * change with time, at dates chosen by the user. <br>
50 * </p>
51 * <p>
52 * This is a behavior that can be sought in precise orbit determination.<br>
53 * Indeed for this type of application, the empirical parameters must be revalued at
54 * each new orbit.
55 * </p>
56 * <b>Usage</b>:<ul>
57 * <li><u>Construction</u>: constructor takes an acceleration direction, an attitude mode (or an inertial flag) and
58 * an AccelerationModel model.<br>
59 * This last model will be your initial AccelerationModel model and it will be initially valid for the whole time line.<br>
60 * The real validity of this first entry will be truncated as other AccelerationModel models are added.
61 * <li><u>Time spans</u>: AccelerationModel models are added using methods {@link #addAccelerationModelValidAfter(AccelerationModel, AbsoluteDate)}
62 * or {@link #addAccelerationModelValidBefore(AccelerationModel, AbsoluteDate)}.<br>
63 * Recommendations are the same than the ones in {@link TimeSpanMap}, meaning: <ul>
64 * <li>As an entry is added, it truncates the validity of the neighboring entries already present in the map;
65 * <li><b>The transition dates should be entered only once</b>. Repeating a transition date will lead to unexpected result and is not supported;
66 * <li>It is advised to order your AccelerationModel models chronologically when adding them to avoid any confusion.
67 * </ul>
68 * <li><u>Naming the parameter drivers</u>: It is strongly advised to give a custom name to the {@link ParameterDriver}(s)
69 * of each AccelerationModel model that is added to the object. This will allow you keeping track of the evolution of your models.<br>
70 * Different names are mandatory to differentiate the different drivers.<br>
71 * Since there is no default name for acceleration model parameters, you must handle the driver names to consider
72 * different names when adding a new acceleration model.
73 * </ul>
74 * @author Bryan Cazabonne
75 * @since 10.3
76 */
77 public class TimeSpanParametricAcceleration implements ForceModel {
78
79 /** Prefix for dates before in the parameter drivers' name. */
80 public static final String DATE_BEFORE = " - Before ";
81
82 /** Prefix for dates after in the parameter drivers' name. */
83 public static final String DATE_AFTER = " - After ";
84
85 /** Direction of the acceleration in defining frame. */
86 private final Vector3D direction;
87
88 /** Flag for inertial acceleration direction. */
89 private final boolean isInertial;
90
91 /** The attitude to override, if set. */
92 private final AttitudeProvider attitudeOverride;
93
94 /** TimeSpanMap of AccelerationModel objects. */
95 private final TimeSpanMap<AccelerationModel> accelerationModelTimeSpanMap;
96
97 /** Simple constructor.
98 * @param direction acceleration direction in overridden spacecraft frame
99 * @param isInertial if true, direction is defined in the same inertial
100 * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
101 * otherwise direction is defined in spacecraft frame (i.e. using the
102 * propagation {@link
103 * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
104 * attitude law})
105 * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
106 */
107 public TimeSpanParametricAcceleration(final Vector3D direction,
108 final boolean isInertial,
109 final AccelerationModel accelerationModel) {
110 this(direction, isInertial, null, accelerationModel);
111 }
112
113 /** Simple constructor.
114 * @param direction acceleration direction in overridden spacecraft frame
115 * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
116 * otherwise direction is defined in spacecraft frame (i.e. using the
117 * propagation {@link
118 * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
119 * attitude law})
120 * @param attitudeOverride provider for attitude used to compute acceleration
121 * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
122 */
123 public TimeSpanParametricAcceleration(final Vector3D direction,
124 final AttitudeProvider attitudeOverride,
125 final AccelerationModel accelerationModel) {
126 this(direction, false, attitudeOverride, accelerationModel);
127 }
128
129 /** Simple constructor.
130 * @param direction acceleration direction in overridden spacecraft frame
131 * @param isInertial if true, direction is defined in the same inertial
132 * frame used for propagation (i.e. {@link SpacecraftState#getFrame()}),
133 * otherwise direction is defined in spacecraft frame (i.e. using the
134 * propagation {@link
135 * org.orekit.propagation.Propagator#setAttitudeProvider(AttitudeProvider)
136 * attitude law})
137 * @param attitudeOverride provider for attitude used to compute acceleration
138 * @param accelerationModel acceleration model used to compute the contribution of the empirical acceleration
139 */
140 private TimeSpanParametricAcceleration(final Vector3D direction,
141 final boolean isInertial,
142 final AttitudeProvider attitudeOverride,
143 final AccelerationModel accelerationModel) {
144 this.direction = direction;
145 this.isInertial = isInertial;
146 this.attitudeOverride = attitudeOverride;
147 this.accelerationModelTimeSpanMap = new TimeSpanMap<>(accelerationModel);
148 }
149
150 /** {@inheritDoc} */
151 @Override
152 public void init(final SpacecraftState initialState, final AbsoluteDate target) {
153 accelerationModelTimeSpanMap.forEach(accelerationModel -> accelerationModel.init(initialState, target));
154 }
155
156 /** Add an AccelerationModel entry valid before a limit date.<br>
157 * <p>
158 * Using <code>addAccelerationModelValidBefore(entry, t)</code> will make <code>entry</code>
159 * valid in ]-∞, t[ (note the open bracket).
160 * <p>
161 * <b>WARNING</b>: Since there is no default name for acceleration model parameters,
162 * the user must handle itself the driver names to consider different names
163 * (i.e. different parameters) when adding a new acceleration model.
164 * @param accelerationModel AccelerationModel entry
165 * @param latestValidityDate date before which the entry is valid
166 * (must be different from <b>all</b> dates already used for transitions)
167 */
168 public void addAccelerationModelValidBefore(final AccelerationModel accelerationModel, final AbsoluteDate latestValidityDate) {
169 accelerationModelTimeSpanMap.addValidBefore(accelerationModel, latestValidityDate, false);
170 }
171
172 /** Add a AccelerationModel entry valid after a limit date.<br>
173 * <p>
174 * Using <code>addAccelerationModelValidAfter(entry, t)</code> will make <code>entry</code>
175 * valid in [t, +∞[ (note the closed bracket).
176 * <p>
177 * <b>WARNING</b>: Since there is no default name for acceleration model parameters,
178 * the user must handle itself the driver names to consider different names
179 * (i.e. different parameters) when adding a new acceleration model.
180 * @param accelerationModel AccelerationModel entry
181 * @param earliestValidityDate date after which the entry is valid
182 * (must be different from <b>all</b> dates already used for transitions)
183 */
184 public void addAccelerationModelValidAfter(final AccelerationModel accelerationModel, final AbsoluteDate earliestValidityDate) {
185 accelerationModelTimeSpanMap.addValidAfter(accelerationModel, earliestValidityDate, false);
186 }
187
188 /** Get the {@link AccelerationModel} model valid at a date.
189 * @param date the date of validity
190 * @return the AccelerationModel model valid at date
191 */
192 public AccelerationModel getAccelerationModel(final AbsoluteDate date) {
193 return accelerationModelTimeSpanMap.get(date);
194 }
195
196 /** Get the {@link AccelerationModel} {@link Span} containing a specified date.
197 * @param date date belonging to the desired time span
198 * @return the AccelerationModel time span containing the specified date
199 */
200 public Span<AccelerationModel> getAccelerationModelSpan(final AbsoluteDate date) {
201 return accelerationModelTimeSpanMap.getSpan(date);
202 }
203
204 /** Extract a range of the {@link AccelerationModel} map.
205 * <p>
206 * The object returned will be a new independent instance that will contain
207 * only the transitions that lie in the specified range.
208 * </p>
209 * See the {@link TimeSpanMap#extractRange TimeSpanMap.extractRange method} for more.
210 * @param start earliest date at which a transition is included in the range
211 * (may be set to {@link AbsoluteDate#PAST_INFINITY} to keep all early transitions)
212 * @param end latest date at which a transition is included in the r
213 * (may be set to {@link AbsoluteDate#FUTURE_INFINITY} to keep all late transitions)
214 * @return a new TimeSpanMap instance of AccelerationModel with all transitions restricted to the specified range
215 */
216 public TimeSpanMap<AccelerationModel> extractAccelerationModelRange(final AbsoluteDate start, final AbsoluteDate end) {
217 return accelerationModelTimeSpanMap.extractRange(start, end);
218 }
219
220 /** Get the first {@link Span time span} of the acceleration model time span map.
221 * @return the first {@link Span time span} of the acceleration model time span map
222 * @since 11.1
223 */
224 public Span<AccelerationModel> getFirstSpan() {
225 return accelerationModelTimeSpanMap.getFirstSpan();
226 }
227
228 /** {@inheritDoc} */
229 @Override
230 public boolean dependsOnPositionOnly() {
231 return isInertial;
232 }
233
234 /** {@inheritDoc} */
235 @Override
236 public Vector3D acceleration(final SpacecraftState state,
237 final double[] parameters) {
238
239 // Date
240 final AbsoluteDate date = state.getDate();
241
242 // Compute inertial direction
243 final Vector3D inertialDirection;
244 if (isInertial) {
245 // the acceleration direction is already defined in the inertial frame
246 inertialDirection = direction;
247 } else {
248 final Rotation rotation;
249 if (attitudeOverride == null) {
250 // the acceleration direction is defined in spacecraft frame as set by the propagator
251 rotation = state.getAttitude().getRotation();
252 } else {
253 // the acceleration direction is defined in a dedicated frame
254 rotation = attitudeOverride.getAttitudeRotation(state.getOrbit(), date, state.getFrame());
255 }
256 inertialDirection = rotation.applyInverseTo(direction);
257 }
258
259 // Extract the proper parameters valid at date from the input array
260 final double[] extractedParameters = extractParameters(parameters, date);
261
262 // Compute and return the parametric acceleration
263 return new Vector3D(getAccelerationModel(date).signedAmplitude(state, extractedParameters), inertialDirection);
264
265 }
266
267 /** {@inheritDoc} */
268 @Override
269 public <T extends CalculusFieldElement<T>> FieldVector3D<T> acceleration(final FieldSpacecraftState<T> state,
270 final T[] parameters) {
271
272 // Date
273 final FieldAbsoluteDate<T> date = state.getDate();
274
275 // Compute inertial direction
276 final FieldVector3D<T> inertialDirection;
277 if (isInertial) {
278 // the acceleration direction is already defined in the inertial frame
279 inertialDirection = new FieldVector3D<>(state.getDate().getField(), direction);
280 } else {
281 final FieldRotation<T> rotation;
282 if (attitudeOverride == null) {
283 // the acceleration direction is defined in spacecraft frame as set by the propagator
284 rotation = state.getAttitude().getRotation();
285 } else {
286 // the acceleration direction is defined in a dedicated frame
287 rotation = attitudeOverride.getAttitudeRotation(state.getOrbit(), date, state.getFrame());
288 }
289 inertialDirection = rotation.applyInverseTo(direction);
290 }
291
292 // Extract the proper parameters valid at date from the input array
293 final T[] extractedParameters = extractParameters(parameters, date);
294
295 // Compute and return the parametric acceleration
296 return new FieldVector3D<>(getAccelerationModel(date.toAbsoluteDate()).signedAmplitude(state, extractedParameters), inertialDirection);
297
298 }
299
300 /** {@inheritDoc} */
301 @Override
302 public Stream<EventDetector> getEventDetectors() {
303 return Stream.empty();
304 }
305
306 /** {@inheritDoc} */
307 @Override
308 public <T extends CalculusFieldElement<T>> Stream<FieldEventDetector<T>> getFieldEventDetectors(final Field<T> field) {
309 return Stream.empty();
310 }
311
312 /** {@inheritDoc}
313 * <p>
314 * All the parameter drivers of all AccelerationModel models are returned in an array.
315 * Models are ordered chronologically.
316 * </p>
317 */
318 @Override
319 public List<ParameterDriver> getParametersDrivers() {
320
321 // Get all transitions from the TimeSpanMap
322 final List<ParameterDriver> listParameterDrivers = new ArrayList<>();
323
324 // Loop on the spans
325 for (Span<AccelerationModel> span = getFirstSpan(); span != null; span = span.next()) {
326 // Add all the parameter drivers of the time span
327 for (ParameterDriver driver : span.getData().getParametersDrivers()) {
328 // Add the driver only if the name does not exist already
329 if (!findByName(listParameterDrivers, driver.getName())) {
330 listParameterDrivers.add(driver);
331 }
332 }
333 }
334
335 // Return an array of parameter drivers with no duplicated name
336 return Collections.unmodifiableList(listParameterDrivers);
337
338 }
339
340 /** Extract the proper parameter drivers' values from the array in input of the
341 * {@link #acceleration(SpacecraftState, double[]) acceleration} method.
342 * Parameters are filtered given an input date.
343 * @param parameters the input parameters array
344 * @param date the date
345 * @return the parameters given the date
346 */
347 public double[] extractParameters(final double[] parameters, final AbsoluteDate date) {
348
349 // Get the acceleration model parameter drivers of the date
350 final List<ParameterDriver> empiricalParameterDriver = getAccelerationModel(date).getParametersDrivers();
351
352 // Find out the indexes of the parameters in the whole array of parameters
353 final List<ParameterDriver> allParameters = getParametersDrivers();
354 final double[] outParameters = new double[empiricalParameterDriver.size()];
355 int index = 0;
356 for (int i = 0; i < allParameters.size(); i++) {
357 final String driverName = allParameters.get(i).getName();
358 for (ParameterDriver accDriver : empiricalParameterDriver) {
359 if (accDriver.getName().equals(driverName)) {
360 outParameters[index++] = parameters[i];
361 }
362 }
363 }
364 return outParameters;
365 }
366
367 /** Extract the proper parameter drivers' values from the array in input of the
368 * {@link #acceleration(FieldSpacecraftState, CalculusFieldElement[]) acceleration} method.
369 * Parameters are filtered given an input date.
370 * @param parameters the input parameters array
371 * @param date the date
372 * @param <T> extends CalculusFieldElement
373 * @return the parameters given the date
374 */
375 public <T extends CalculusFieldElement<T>> T[] extractParameters(final T[] parameters,
376 final FieldAbsoluteDate<T> date) {
377
378 // Get the acceleration parameter drivers of the date
379 final List<ParameterDriver> empiricalParameterDriver = getAccelerationModel(date.toAbsoluteDate()).getParametersDrivers();
380
381 // Find out the indexes of the parameters in the whole array of parameters
382 final List<ParameterDriver> allParameters = getParametersDrivers();
383 final T[] outParameters = MathArrays.buildArray(date.getField(), empiricalParameterDriver.size());
384 int index = 0;
385 for (int i = 0; i < allParameters.size(); i++) {
386 final String driverName = allParameters.get(i).getName();
387 for (ParameterDriver accDriver : empiricalParameterDriver) {
388 if (accDriver.getName().equals(driverName)) {
389 outParameters[index++] = parameters[i];
390 }
391 }
392 }
393 return outParameters;
394 }
395
396 /** Find if a parameter driver with a given name already exists in a list of parameter drivers.
397 * @param driversList the list of parameter drivers
398 * @param name the parameter driver's name to filter with
399 * @return true if the name was found, false otherwise
400 */
401 private boolean findByName(final List<ParameterDriver> driversList, final String name) {
402 for (final ParameterDriver d : driversList) {
403 if (d.getName().equals(name)) {
404 return true;
405 }
406 }
407 return false;
408 }
409
410 }