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