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 }