1   /* Copyright 2022-2026 Romain Serra
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.propagation.analytical;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.stream.Collectors;
22  
23  import org.hipparchus.CalculusFieldElement;
24  import org.hipparchus.Field;
25  import org.hipparchus.exception.MathIllegalArgumentException;
26  import org.orekit.attitudes.AttitudeProvider;
27  import org.orekit.attitudes.FieldAttitude;
28  import org.orekit.attitudes.FrameAlignedProvider;
29  import org.orekit.errors.OrekitException;
30  import org.orekit.errors.OrekitIllegalArgumentException;
31  import org.orekit.errors.OrekitMessages;
32  import org.orekit.frames.Frame;
33  import org.orekit.orbits.FieldOrbit;
34  import org.orekit.propagation.FieldSpacecraftState;
35  import org.orekit.propagation.FieldSpacecraftStateInterpolator;
36  import org.orekit.time.AbstractFieldTimeInterpolator;
37  import org.orekit.time.FieldAbsoluteDate;
38  import org.orekit.time.FieldTimeInterpolator;
39  import org.orekit.utils.FieldBoundedPVCoordinatesProvider;
40  import org.orekit.utils.FieldDataDictionary;
41  import org.orekit.utils.ImmutableFieldTimeStampedCache;
42  import org.orekit.utils.ParameterDriver;
43  
44  /**
45   * This class is designed to accept and handle tabulated orbital entries. Tabulated entries are classified and then
46   * extrapolated in way to obtain continuous output, with accuracy and computation methods configured by the user.
47   *
48   * @see Ephemeris
49   * @author Luc Maisonobe
50   * @author Vincent Cucchietti
51   * @author Romain Serra
52   * @since 14.0
53   */
54  public class FieldEphemeris<T extends CalculusFieldElement<T>> extends FieldAbstractAnalyticalPropagator<T>
55          implements FieldBoundedPVCoordinatesProvider<T> {
56  
57      /** First date in range. */
58      private final FieldAbsoluteDate<T> minDate;
59  
60      /** Last date in range. */
61      private final FieldAbsoluteDate<T> maxDate;
62  
63      /** Reference frame. */
64      private final Frame frame;
65  
66      /** Names of the additional states. */
67      private final String[] additional;
68  
69      /** List of spacecraft states. */
70      private final ImmutableFieldTimeStampedCache<FieldSpacecraftState<T>, T> statesCache;
71  
72      /** Spacecraft state interpolator. */
73      private final FieldTimeInterpolator<FieldSpacecraftState<T>, T> stateInterpolator;
74  
75      /**
76       * Constructor with tabulated states and default Hermite interpolation.
77       *
78       * @param states list of spacecraft states
79       * @param interpolationPoints number of points to use in interpolation
80       *
81       * @throws MathIllegalArgumentException   if the number of states is smaller than the number of points to use in
82       *                                        interpolation
83       * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
84       *                                        position-velocity-acceleration)
85       * @see FieldSpacecraftStateInterpolator
86       */
87      public FieldEphemeris(final List<FieldSpacecraftState<T>> states, final int interpolationPoints)
88              throws MathIllegalArgumentException {
89          this(states, new FieldSpacecraftStateInterpolator<>(interpolationPoints, states.getFirst().getFrame(), states.getFirst().getFrame()));
90      }
91  
92      /**
93       * Constructor with tabulated states.
94       *
95       * @param states list of spacecraft states
96       * @param stateInterpolator spacecraft state interpolator
97       *
98       * @throws MathIllegalArgumentException   if the number of states is smaller than the number of points to use in
99       *                                        interpolation
100      * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
101      *                                        position-velocity-acceleration)
102      */
103     public FieldEphemeris(final List<FieldSpacecraftState<T>> states,
104                           final FieldTimeInterpolator<FieldSpacecraftState<T>, T> stateInterpolator)
105             throws MathIllegalArgumentException {
106         this(states, stateInterpolator, new FrameAlignedProvider(states.getFirst().getFrame()));
107     }
108 
109     /**
110      * Constructor with tabulated states and attitude provider.
111      *
112      * @param states list of spacecraft states
113      * @param stateInterpolator spacecraft state interpolator
114      * @param attitudeProvider attitude law to use
115      *
116      * @throws MathIllegalArgumentException   if the number of states is smaller than the number of points to use in
117      *                                        interpolation
118      * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
119      *                                        position-velocity-acceleration)
120      */
121     public FieldEphemeris(final List<FieldSpacecraftState<T>> states,
122                           final FieldTimeInterpolator<FieldSpacecraftState<T>, T> stateInterpolator,
123                           final AttitudeProvider attitudeProvider)
124             throws MathIllegalArgumentException {
125         super(states.getFirst().getDate().getField(), attitudeProvider);
126 
127         // Check input consistency
128         checkInputConsistency(states, stateInterpolator);
129 
130         // Initialize variables
131         final FieldSpacecraftState<T> s0 = states.getFirst();
132         minDate = s0.getDate();
133         maxDate = states.getLast().getDate();
134         frame   = s0.getFrame();
135 
136         final List<FieldDataDictionary<T>.Entry> as = s0.getAdditionalDataValues().getData();
137         additional = new String[as.size()];
138         for (int i = 0; i < additional.length; ++i) {
139             additional[i] = as.get(i).getKey();
140         }
141 
142         this.statesCache       = new ImmutableFieldTimeStampedCache<>(stateInterpolator.getNbInterpolationPoints(), states);
143         this.stateInterpolator = stateInterpolator;
144 
145         // Initialize initial state
146         super.resetInitialState(getInitialState());
147     }
148 
149     /**
150      * Constructor from non-Field object.
151      * @param field field
152      * @param ephemeris ephemeris
153      */
154     public FieldEphemeris(final Field<T> field, final Ephemeris ephemeris) {
155         this(ephemeris.getStates().stream().map(state -> new FieldSpacecraftState<>(field, state)).collect(Collectors.toList()),
156                 new FieldSpacecraftStateInterpolator<>(ephemeris.getStateInterpolator().getNbInterpolationPoints(),
157                 ephemeris.getFrame()), ephemeris.getAttitudeProvider());
158     }
159 
160     /**
161      * Getter for the interpolated states.
162      * @return copy of states
163      */
164     public List<FieldSpacecraftState<T>> getStates() {
165         return statesCache.getAll();
166     }
167 
168     /**
169      * Check input consistency between states and the interpolator.
170      *
171      * @param states spacecraft states sample
172      * @param interpolator spacecraft state interpolator
173      */
174     public void checkInputConsistency(final List<FieldSpacecraftState<T>> states,
175                                       final FieldTimeInterpolator<FieldSpacecraftState<T>, T> interpolator) {
176         checkStatesDefinitionsConsistency(states);
177 
178         // Check that every interpolator used in the state interpolator are compatible with the sample size
179         AbstractFieldTimeInterpolator.checkInterpolatorCompatibilityWithSampleSize(interpolator, states.size());
180     }
181 
182     /**
183      * Check that all state are either orbit defined or based on absolute position-velocity-acceleration.
184      *
185      * @param states spacecraft state sample
186      */
187     public void checkStatesDefinitionsConsistency(final List<FieldSpacecraftState<T>> states) {
188         // Check all states handle the same additional states and are defined the same way (orbit or absolute PVA)
189         final FieldSpacecraftState<T> s0               = states.getFirst();
190         final boolean         s0IsOrbitDefined = s0.isOrbitDefined();
191         for (final FieldSpacecraftState<T> state : states) {
192             s0.ensureCompatibleAdditionalStates(state);
193             if (s0IsOrbitDefined != state.isOrbitDefined()) {
194                 throw new OrekitIllegalArgumentException(OrekitMessages.DIFFERENT_STATE_DEFINITION);
195             }
196         }
197     }
198 
199     /**
200      * Get the first date of the range.
201      *
202      * @return the first date of the range
203      */
204     public FieldAbsoluteDate<T> getMinDate() {
205         return minDate;
206     }
207 
208     /**
209      * Get the last date of the range.
210      *
211      * @return the last date of the range
212      */
213     public FieldAbsoluteDate<T> getMaxDate() {
214         return maxDate;
215     }
216 
217     /** {@inheritDoc} */
218     @Override
219     public Frame getFrame() {
220         return frame;
221     }
222 
223     /** {@inheritDoc} */
224     @Override
225     public FieldSpacecraftState<T> basicPropagate(final FieldAbsoluteDate<T> date) {
226 
227         final FieldAbsoluteDate<T> centralDate =
228                 AbstractFieldTimeInterpolator.getCentralDate(date, statesCache, stateInterpolator.getExtrapolationThreshold());
229         final FieldSpacecraftState<T>  evaluatedState   = stateInterpolator.interpolate(date, statesCache.getNeighbors(centralDate));
230         final AttitudeProvider attitudeProvider = getAttitudeProvider();
231         final FieldAttitude<T> calculatedAttitude;
232         // Verify if orbit is defined
233         if (evaluatedState.isOrbitDefined()) {
234             calculatedAttitude =
235                     attitudeProvider.getAttitude(evaluatedState.getOrbit(), date, evaluatedState.getFrame());
236             return new FieldSpacecraftState<>(evaluatedState.getOrbit(), calculatedAttitude, evaluatedState.getMass(),
237                                        evaluatedState.getMassRate(), evaluatedState.getAdditionalDataValues(),
238                                        evaluatedState.getAdditionalStatesDerivatives());
239         }
240         else {
241             calculatedAttitude =
242                     attitudeProvider.getAttitude(evaluatedState.getAbsPVA(), date, evaluatedState.getFrame());
243             return new FieldSpacecraftState<>(evaluatedState.getAbsPVA(), calculatedAttitude, evaluatedState.getMass(),
244                                        evaluatedState.getMassRate(), evaluatedState.getAdditionalDataValues(),
245                                        evaluatedState.getAdditionalStatesDerivatives());
246         }
247     }
248 
249     /** {@inheritDoc} */
250     public FieldOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date, final T[] parameters) {
251         return basicPropagate(date).getOrbit();
252     }
253 
254     /** {@inheritDoc} */
255     protected T getMass(final FieldAbsoluteDate<T> date) {
256         return basicPropagate(date).getMass();
257     }
258 
259     /**
260      * Try (and fail) to reset the initial state.
261      * <p>
262      * This method always throws an exception, as ephemerides cannot be reset.
263      * </p>
264      *
265      * @param state new initial state to consider
266      */
267     @Override
268     public void resetInitialState(final FieldSpacecraftState<T> state) {
269         resetIntermediateState(state, true);
270     }
271 
272     /** {@inheritDoc} */
273     protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
274         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
275     }
276 
277     /** {@inheritDoc} */
278     @Override
279     public FieldSpacecraftState<T> getInitialState() {
280         return basicPropagate(getMinDate());
281     }
282 
283     /** {@inheritDoc} */
284     @Override
285     public boolean isAdditionalDataManaged(final String name) {
286 
287         // the additional state may be managed by a specific provider in the base class
288         if (super.isAdditionalDataManaged(name)) {
289             return true;
290         }
291 
292         // the additional state may be managed in the states sample
293         for (final String a : additional) {
294             if (a.equals(name)) {
295                 return true;
296             }
297         }
298 
299         return false;
300 
301     }
302 
303     /** {@inheritDoc} */
304     @Override
305     public String[] getManagedAdditionalData() {
306         final String[] upperManaged = super.getManagedAdditionalData();
307         final String[] managed      = new String[upperManaged.length + additional.length];
308         System.arraycopy(upperManaged, 0, managed, 0, upperManaged.length);
309         System.arraycopy(additional, 0, managed, upperManaged.length, additional.length);
310         return managed;
311     }
312 
313     /** Get state interpolator.
314      * @return state interpolator
315      */
316     public FieldTimeInterpolator<FieldSpacecraftState<T>, T> getStateInterpolator() {
317         return stateInterpolator;
318     }
319 
320     @Override
321     public List<ParameterDriver> getParametersDrivers() {
322         return new ArrayList<>();
323     }
324 
325 }