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.propagation.analytical;
18
19 import org.hipparchus.exception.LocalizedCoreFormats;
20 import org.hipparchus.exception.MathIllegalArgumentException;
21 import org.hipparchus.linear.RealMatrix;
22 import org.orekit.attitudes.Attitude;
23 import org.orekit.attitudes.AttitudeProvider;
24 import org.orekit.attitudes.FrameAlignedProvider;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitIllegalArgumentException;
27 import org.orekit.errors.OrekitIllegalStateException;
28 import org.orekit.errors.OrekitMessages;
29 import org.orekit.frames.Frame;
30 import org.orekit.orbits.Orbit;
31 import org.orekit.propagation.AbstractMatricesHarvester;
32 import org.orekit.propagation.BoundedPropagator;
33 import org.orekit.propagation.SpacecraftState;
34 import org.orekit.propagation.SpacecraftStateInterpolator;
35 import org.orekit.propagation.StateCovariance;
36 import org.orekit.time.AbsoluteDate;
37 import org.orekit.time.AbstractTimeInterpolator;
38 import org.orekit.time.TimeInterpolator;
39 import org.orekit.time.TimeStampedPair;
40 import org.orekit.utils.DoubleArrayDictionary;
41 import org.orekit.utils.ImmutableTimeStampedCache;
42
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.Optional;
46
47 /**
48 * This class is designed to accept and handle tabulated orbital entries. Tabulated entries are classified and then
49 * extrapolated in way to obtain continuous output, with accuracy and computation methods configured by the user.
50 *
51 * @author Fabien Maussion
52 * @author Véronique Pommier-Maurussane
53 * @author Luc Maisonobe
54 * @author Vincent Cucchietti
55 */
56 public class Ephemeris extends AbstractAnalyticalPropagator implements BoundedPropagator {
57
58 /** First date in range. */
59 private final AbsoluteDate minDate;
60
61 /** Last date in range. */
62 private final AbsoluteDate 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 transient ImmutableTimeStampedCache<SpacecraftState> statesCache;
72
73 /** List of covariances. **/
74 private final transient ImmutableTimeStampedCache<StateCovariance> covariancesCache;
75
76 /** Spacecraft state interpolator. */
77 private final transient TimeInterpolator<SpacecraftState> stateInterpolator;
78
79 /** State covariance interpolator. */
80 private final transient TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>> covarianceInterpolator;
81
82 /** Flag defining if states are defined using an orbit or an absolute position-velocity-acceleration. */
83 private final transient boolean statesAreOrbitDefined;
84
85 /**
86 * Legacy constructor with tabulated states and default Hermite interpolation.
87 * <p>
88 * As this implementation of interpolation is polynomial, it should be used only with small samples (about 10-20 points)
89 * in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a> and numerical
90 * problems (including NaN appearing).
91 *
92 * @param states list of spacecraft states
93 * @param interpolationPoints number of points to use in interpolation
94 *
95 * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in
96 * interpolation
97 * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
98 * position-velocity-acceleration)
99 * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator)
100 * @see SpacecraftStateInterpolator
101 */
102 public Ephemeris(final List<SpacecraftState> states, final int interpolationPoints)
103 throws MathIllegalArgumentException {
104 // If states is empty an exception will be thrown in the other constructor
105 this(states, new SpacecraftStateInterpolator(interpolationPoints,
106 states.get(0).getFrame(),
107 states.get(0).getFrame()),
108 new ArrayList<>(), null);
109 }
110
111 /**
112 * Constructor with tabulated states.
113 *
114 * @param states list of spacecraft states
115 * @param stateInterpolator spacecraft state interpolator
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 * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator)
122 */
123 public Ephemeris(final List<SpacecraftState> states, final TimeInterpolator<SpacecraftState> stateInterpolator)
124 throws MathIllegalArgumentException {
125 this(states, stateInterpolator, new ArrayList<>(), null);
126 }
127
128 /**
129 * Constructor with tabulated states.
130 *
131 * @param states list of spacecraft states
132 * @param stateInterpolator spacecraft state interpolator
133 * @param attitudeProvider attitude law to use, null by default
134 *
135 * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in
136 * interpolation
137 * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
138 * position-velocity-acceleration)
139 * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator)
140 */
141 public Ephemeris(final List<SpacecraftState> states, final TimeInterpolator<SpacecraftState> stateInterpolator,
142 final AttitudeProvider attitudeProvider)
143 throws MathIllegalArgumentException {
144 this(states, stateInterpolator, new ArrayList<>(), null, attitudeProvider);
145 }
146
147 /**
148 * Constructor with tabulated states and associated covariances.
149 *
150 * @param states list of spacecraft states
151 * @param stateInterpolator spacecraft state interpolator
152 * @param covariances tabulated covariances associated to tabulated states ephemeris bounds to be doing extrapolation
153 * @param covarianceInterpolator covariance interpolator
154 *
155 * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in
156 * interpolation
157 * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
158 * position-velocity-acceleration)
159 * @throws OrekitIllegalArgumentException if number of states is different from the number of covariances
160 * @throws OrekitIllegalStateException if dates between states and associated covariances are different
161 * @see #Ephemeris(List, TimeInterpolator, List, TimeInterpolator, AttitudeProvider)
162 * @since 9.0
163 */
164 public Ephemeris(final List<SpacecraftState> states,
165 final TimeInterpolator<SpacecraftState> stateInterpolator,
166 final List<StateCovariance> covariances,
167 final TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>> covarianceInterpolator)
168 throws MathIllegalArgumentException {
169 this(states, stateInterpolator, covariances, covarianceInterpolator,
170 // if states is empty an exception will be thrown in the other constructor
171 states.isEmpty() ? null : FrameAlignedProvider.of(states.get(0).getFrame()));
172 }
173
174 /**
175 * Constructor with tabulated states and associated covariances.
176 * <p>
177 * The user is expected to explicitly define an attitude provider if they want to use one. Otherwise, it is null by
178 * default
179 *
180 * @param states list of spacecraft states
181 * @param stateInterpolator spacecraft state interpolator
182 * @param covariances tabulated covariances associated to tabulated states
183 * @param covarianceInterpolator covariance interpolator
184 * @param attitudeProvider attitude law to use, null by default
185 *
186 * @throws MathIllegalArgumentException if the number of states is smaller than the number of points to use in
187 * interpolation
188 * @throws OrekitIllegalArgumentException if states are not defined the same way (orbit or absolute
189 * position-velocity-acceleration)
190 * @throws OrekitIllegalArgumentException if number of states is different from the number of covariances
191 * @throws OrekitIllegalStateException if dates between states and associated covariances are different
192 * @since 10.1
193 */
194 public Ephemeris(final List<SpacecraftState> states,
195 final TimeInterpolator<SpacecraftState> stateInterpolator,
196 final List<StateCovariance> covariances,
197 final TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>> covarianceInterpolator,
198 final AttitudeProvider attitudeProvider)
199 throws MathIllegalArgumentException {
200
201 super(attitudeProvider);
202
203 // Check input consistency
204 checkInputConsistency(states, stateInterpolator, covariances, covarianceInterpolator);
205
206 // Initialize variables
207 final SpacecraftState s0 = states.get(0);
208 minDate = s0.getDate();
209 maxDate = states.get(states.size() - 1).getDate();
210 frame = s0.getFrame();
211
212 final List<DoubleArrayDictionary.Entry> as = s0.getAdditionalStatesValues().getData();
213 additional = new String[as.size()];
214 for (int i = 0; i < additional.length; ++i) {
215 additional[i] = as.get(i).getKey();
216 }
217
218 this.statesCache = new ImmutableTimeStampedCache<>(stateInterpolator.getNbInterpolationPoints(), states);
219 this.stateInterpolator = stateInterpolator;
220
221 this.covarianceInterpolator = covarianceInterpolator;
222 if (covarianceInterpolator != null) {
223 this.covariancesCache = new ImmutableTimeStampedCache<>(covarianceInterpolator.getNbInterpolationPoints(),
224 covariances);
225 } else {
226 this.covariancesCache = null;
227 }
228 this.statesAreOrbitDefined = s0.isOrbitDefined();
229
230 // Initialize initial state
231 super.resetInitialState(getInitialState());
232 }
233
234 /**
235 * Check input consistency between states, covariances and their associated interpolators.
236 *
237 * @param states spacecraft states sample
238 * @param stateInterpolator spacecraft state interpolator
239 * @param covariances covariances sample
240 * @param covarianceInterpolator covariance interpolator
241 */
242 public static void checkInputConsistency(final List<SpacecraftState> states,
243 final TimeInterpolator<SpacecraftState> stateInterpolator,
244 final List<StateCovariance> covariances,
245 final TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>> covarianceInterpolator) {
246 // Checks to perform is states are provided
247 if (!states.isEmpty()) {
248 // Check given that given states definition are consistent
249 // (all defined by either orbits or absolute position-velocity-acceleration coordinates)
250 SpacecraftStateInterpolator.checkStatesDefinitionsConsistency(states);
251
252 // Check that every interpolator used in the state interpolator are compatible with the sample size
253 AbstractTimeInterpolator.checkInterpolatorCompatibilityWithSampleSize(stateInterpolator, states.size());
254
255 // Additional checks if covariances are provided
256 if (!covariances.isEmpty()) {
257 // Check that every interpolator used in the state covariance interpolator are compatible with the sample size
258 AbstractTimeInterpolator.checkInterpolatorCompatibilityWithSampleSize(covarianceInterpolator,
259 covariances.size());
260 // Check states and covariances consistency
261 checkStatesAndCovariancesConsistency(states, covariances);
262 }
263 }
264 else {
265 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, 0);
266 }
267 }
268
269 /**
270 * Check that given states and covariances are consistent.
271 *
272 * @param states tabulates states to check
273 * @param covariances tabulated covariances associated to tabulated states to check
274 */
275 public static void checkStatesAndCovariancesConsistency(final List<SpacecraftState> states,
276 final List<StateCovariance> covariances) {
277 final int nbStates = states.size();
278
279 // Check that we have an equal number of states and covariances
280 if (nbStates != covariances.size()) {
281 throw new OrekitIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
282 states.size(),
283 covariances.size());
284 }
285
286 // Check that states and covariance are defined at the same date
287 for (int i = 0; i < nbStates; i++) {
288 if (!states.get(i).getDate().isCloseTo(covariances.get(i).getDate(),
289 TimeStampedPair.DEFAULT_DATE_EQUALITY_THRESHOLD)) {
290 throw new OrekitIllegalStateException(OrekitMessages.STATE_AND_COVARIANCE_DATES_MISMATCH,
291 states.get(i).getDate(), covariances.get(i).getDate());
292 }
293 }
294 }
295
296 /**
297 * Get the first date of the range.
298 *
299 * @return the first date of the range
300 */
301 public AbsoluteDate getMinDate() {
302 return minDate;
303 }
304
305 /**
306 * Get the last date of the range.
307 *
308 * @return the last date of the range
309 */
310 public AbsoluteDate getMaxDate() {
311 return maxDate;
312 }
313
314 /** {@inheritDoc} */
315 @Override
316 public Frame getFrame() {
317 return frame;
318 }
319
320 /**
321 * Get the covariance at given date.
322 * <p>
323 * BEWARE : If this instance has been created without sample of covariances and/or with spacecraft states defined with
324 * absolute position-velocity-acceleration, it will return an empty {@link Optional}.
325 *
326 * @param date date at which the covariance is desired
327 *
328 * @return covariance at given date
329 *
330 * @see Optional
331 */
332 public Optional<StateCovariance> getCovariance(final AbsoluteDate date) {
333 if (covarianceInterpolator != null && covariancesCache != null && statesAreOrbitDefined) {
334
335 // Build list of time stamped pair of orbits and their associated covariances
336 final List<TimeStampedPair<Orbit, StateCovariance>> sample = buildOrbitAndCovarianceSample();
337
338 // Interpolate
339 final TimeStampedPair<Orbit, StateCovariance> interpolatedOrbitAndCovariance =
340 covarianceInterpolator.interpolate(date, sample);
341
342 return Optional.of(interpolatedOrbitAndCovariance.getSecond());
343 }
344 else {
345 return Optional.empty();
346 }
347
348 }
349
350 /** @return sample of orbits and their associated covariances */
351 private List<TimeStampedPair<Orbit, StateCovariance>> buildOrbitAndCovarianceSample() {
352 final List<TimeStampedPair<Orbit, StateCovariance>> sample = new ArrayList<>();
353 final List<SpacecraftState> states = statesCache.getAll();
354 final List<StateCovariance> covariances = covariancesCache.getAll();
355 for (int i = 0; i < states.size(); i++) {
356 sample.add(new TimeStampedPair<>(states.get(i).getOrbit(), covariances.get(i)));
357 }
358
359 return sample;
360 }
361
362 /** {@inheritDoc} */
363 @Override
364 public SpacecraftState basicPropagate(final AbsoluteDate date) {
365
366 final AbsoluteDate centralDate =
367 AbstractTimeInterpolator.getCentralDate(date, statesCache, stateInterpolator.getExtrapolationThreshold());
368 final SpacecraftState evaluatedState = stateInterpolator.interpolate(date, statesCache.getNeighbors(centralDate));
369 final AttitudeProvider attitudeProvider = getAttitudeProvider();
370 if (attitudeProvider == null) {
371 return evaluatedState;
372 }
373 else {
374 final Attitude calculatedAttitude;
375 // Verify if orbit is defined
376 if (evaluatedState.isOrbitDefined()) {
377 calculatedAttitude =
378 attitudeProvider.getAttitude(evaluatedState.getOrbit(), date, evaluatedState.getFrame());
379 return new SpacecraftState(evaluatedState.getOrbit(), calculatedAttitude, evaluatedState.getMass(),
380 evaluatedState.getAdditionalStatesValues(),
381 evaluatedState.getAdditionalStatesDerivatives());
382 }
383 else {
384 calculatedAttitude =
385 attitudeProvider.getAttitude(evaluatedState.getAbsPVA(), date, evaluatedState.getFrame());
386 return new SpacecraftState(evaluatedState.getAbsPVA(), calculatedAttitude, evaluatedState.getMass(),
387 evaluatedState.getAdditionalStatesValues(),
388 evaluatedState.getAdditionalStatesDerivatives());
389 }
390
391 }
392 }
393
394 /** {@inheritDoc} */
395 protected Orbit propagateOrbit(final AbsoluteDate date) {
396 return basicPropagate(date).getOrbit();
397 }
398
399 /** {@inheritDoc} */
400 protected double getMass(final AbsoluteDate date) {
401 return basicPropagate(date).getMass();
402 }
403
404 /**
405 * Try (and fail) to reset the initial state.
406 * <p>
407 * This method always throws an exception, as ephemerides cannot be reset.
408 * </p>
409 *
410 * @param state new initial state to consider
411 */
412 public void resetInitialState(final SpacecraftState state) {
413 throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
414 }
415
416 /** {@inheritDoc} */
417 protected void resetIntermediateState(final SpacecraftState state, final boolean forward) {
418 throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
419 }
420
421 /** {@inheritDoc} */
422 public SpacecraftState getInitialState() {
423 return basicPropagate(getMinDate());
424 }
425
426 /** {@inheritDoc} */
427 @Override
428 public boolean isAdditionalStateManaged(final String name) {
429
430 // the additional state may be managed by a specific provider in the base class
431 if (super.isAdditionalStateManaged(name)) {
432 return true;
433 }
434
435 // the additional state may be managed in the states sample
436 for (final String a : additional) {
437 if (a.equals(name)) {
438 return true;
439 }
440 }
441
442 return false;
443
444 }
445
446 /** {@inheritDoc} */
447 @Override
448 public String[] getManagedAdditionalStates() {
449 final String[] upperManaged = super.getManagedAdditionalStates();
450 final String[] managed = new String[upperManaged.length + additional.length];
451 System.arraycopy(upperManaged, 0, managed, 0, upperManaged.length);
452 System.arraycopy(additional, 0, managed, upperManaged.length, additional.length);
453 return managed;
454 }
455
456 /** {@inheritDoc} */
457 @Override
458 protected AbstractMatricesHarvester createHarvester(final String stmName, final RealMatrix initialStm,
459 final DoubleArrayDictionary initialJacobianColumns) {
460 // In order to not throw an Orekit exception during ephemeris based orbit determination
461 // The default behavior of the method is overridden to return a null parameter
462 return null;
463 }
464
465 /** Get state interpolator.
466 * @return state interpolator
467 */
468 public TimeInterpolator<SpacecraftState> getStateInterpolator() {
469 return stateInterpolator;
470 }
471
472 /** Get covariance interpolator.
473 * @return optional covariance interpolator
474 * @see Optional
475 */
476 public Optional<TimeInterpolator<TimeStampedPair<Orbit, StateCovariance>>> getCovarianceInterpolator() {
477 return Optional.ofNullable(covarianceInterpolator);
478 }
479
480 }