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