1 /* Copyright 2002-2026 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;
18
19 import java.util.Comparator;
20 import org.hipparchus.util.Pair;
21 import org.orekit.attitudes.Attitude;
22 import org.orekit.attitudes.AttitudeInterpolator;
23 import org.orekit.attitudes.AttitudeProvider;
24 import org.orekit.attitudes.FrameAlignedProvider;
25 import org.orekit.errors.OrekitIllegalArgumentException;
26 import org.orekit.errors.OrekitInternalError;
27 import org.orekit.errors.OrekitMessages;
28 import org.orekit.frames.Frame;
29 import org.orekit.orbits.Orbit;
30 import org.orekit.orbits.OrbitHermiteInterpolator;
31 import org.orekit.time.AbsoluteDate;
32 import org.orekit.time.AbstractTimeInterpolator;
33 import org.orekit.time.TimeInterpolator;
34 import org.orekit.time.TimeStamped;
35 import org.orekit.time.TimeStampedDouble;
36 import org.orekit.time.TimeStampedDoubleHermiteInterpolator;
37 import org.orekit.utils.AbsolutePVCoordinates;
38 import org.orekit.utils.AbsolutePVCoordinatesHermiteInterpolator;
39 import org.orekit.utils.AngularDerivativesFilter;
40 import org.orekit.utils.CartesianDerivativesFilter;
41 import org.orekit.utils.DoubleArrayDictionary;
42 import org.orekit.utils.DataDictionary;
43 import org.orekit.utils.PVCoordinatesProvider;
44 import org.orekit.utils.TimeStampedAngularCoordinatesHermiteInterpolator;
45
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.HashMap;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Optional;
52
53 /**
54 * Generic class for spacecraft state interpolator.
55 * <p>
56 * The user can specify what interpolator to use for each attribute of the spacecraft state. However, at least one
57 * interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other interpolators can be
58 * left to null if the user do not want to interpolate these values.
59 *
60 * @author Luc Maisonobe
61 * @author Vincent Cucchietti
62 * @see SpacecraftState
63 */
64 public class SpacecraftStateInterpolator extends AbstractTimeInterpolator<SpacecraftState> {
65
66 /**
67 * Output frame.
68 * <p><b>Must be inertial</b> if interpolating spacecraft states defined by orbit</p>
69 */
70 private final Frame outputFrame;
71
72 /** Orbit interpolator. */
73 private final TimeInterpolator<Orbit> orbitInterpolator;
74
75 /** Absolute position-velocity-acceleration interpolator. */
76 private final TimeInterpolator<AbsolutePVCoordinates> absPVAInterpolator;
77
78 /** Mass interpolator. */
79 private final TimeInterpolator<TimeStampedDouble> massInterpolator;
80
81 /** Attitude interpolator. */
82 private final TimeInterpolator<Attitude> attitudeInterpolator;
83
84 /** Additional state interpolator. */
85 private final TimeInterpolator<TimeStampedDouble> additionalStateInterpolator;
86
87 /**
88 * Simplest constructor to create a default Hermite interpolator for every spacecraft state field.
89 * <p>
90 * The interpolators will have the following configuration :
91 * <ul>
92 * <li>Same frame for coordinates and attitude </li>
93 * <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
94 * <li>Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s</li>
95 * <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
96 * <li>Use of angular and first time derivative for attitude interpolation</li>
97 * </ul>
98 * <p>
99 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
100 * points (about 10-20 points) in order to avoid <a href="https://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
101 * phenomenon</a> and numerical problems (including NaN appearing).
102 * <p>
103 * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
104 * tabulated spacecraft states defined by orbit, will throw an error otherwise.
105 *
106 * @param outputFrame output frame
107 *
108 * @see AbstractTimeInterpolator
109 */
110 public SpacecraftStateInterpolator(final Frame outputFrame) {
111 this(DEFAULT_INTERPOLATION_POINTS, outputFrame);
112 }
113
114 /**
115 * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
116 * <p>
117 * The interpolators will have the following configuration :
118 * <ul>
119 * <li>Same frame for coordinates and attitude </li>
120 * <li>Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s</li>
121 * <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
122 * <li>Use of angular and first time derivative for attitude interpolation</li>
123 * </ul>
124 * <p>
125 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
126 * points (about 10-20 points) in order to avoid <a href="https://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
127 * phenomenon</a> and numerical problems (including NaN appearing).
128 * <p>
129 * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
130 * tabulated spacecraft states defined by orbit, will throw an error otherwise.
131 *
132 * @param interpolationPoints number of interpolation points
133 * @param outputFrame output frame
134 *
135 * @see AbstractTimeInterpolator
136 */
137 public SpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame) {
138 this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputFrame, outputFrame);
139 }
140
141 /**
142 * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
143 * <p>
144 * The interpolators will have the following configuration :
145 * <ul>
146 * <li>Same frame for coordinates and attitude </li>
147 * <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
148 * <li>Use of angular and first time derivative for attitude interpolation</li>
149 * </ul>
150 * <p>
151 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
152 * points (about 10-20 points) in order to avoid <a href="https://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
153 * phenomenon</a> and numerical problems (including NaN appearing).
154 * <p>
155 * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
156 * tabulated spacecraft states defined by orbit, will throw an error otherwise.
157 *
158 * @param interpolationPoints number of interpolation points
159 * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
160 * @param outputFrame output frame
161 * @since 12.1
162 * @see AbstractTimeInterpolator
163 */
164 public SpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold, final Frame outputFrame) {
165 this(interpolationPoints, extrapolationThreshold, outputFrame, outputFrame);
166 }
167
168 /**
169 * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
170 * <p>
171 * The interpolators will have the following configuration :
172 * <ul>
173 * <li>Default extrapolation threshold of {@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s</li>
174 * <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
175 * <li>Use of angular and first time derivative for attitude interpolation</li>
176 * </ul>
177 * <p>
178 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
179 * points (about 10-20 points) in order to avoid <a href="https://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
180 * phenomenon</a> and numerical problems (including NaN appearing).
181 * <p>
182 * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
183 * tabulated spacecraft states defined by orbit, will throw an error otherwise.
184 *
185 * @param interpolationPoints number of interpolation points
186 * @param outputFrame output frame
187 * @param attitudeReferenceFrame reference frame from which attitude is defined
188 *
189 * @see AbstractTimeInterpolator
190 */
191 public SpacecraftStateInterpolator(final int interpolationPoints, final Frame outputFrame,
192 final Frame attitudeReferenceFrame) {
193 this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, outputFrame, attitudeReferenceFrame,
194 CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RR);
195 }
196
197 /**
198 * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
199 * <p>
200 * The interpolators will have the following configuration :
201 * <ul>
202 * <li>Use of position and two time derivatives for absolute position-velocity-acceleration coordinates interpolation</li>
203 * <li>Use of angular and first time derivative for attitude interpolation</li>
204 * </ul>
205 * <p>
206 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
207 * points (about 10-20 points) in order to avoid <a href="https://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
208 * phenomenon</a> and numerical problems (including NaN appearing).
209 * <p>
210 * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
211 * tabulated spacecraft states defined by orbit, will throw an error otherwise.
212 *
213 * @param interpolationPoints number of interpolation points
214 * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
215 * @param outputFrame output frame
216 * @param attitudeReferenceFrame reference frame from which attitude is defined
217 */
218 public SpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold,
219 final Frame outputFrame, final Frame attitudeReferenceFrame) {
220 this(interpolationPoints, extrapolationThreshold, outputFrame, attitudeReferenceFrame,
221 CartesianDerivativesFilter.USE_PVA, AngularDerivativesFilter.USE_RR);
222 }
223
224 /**
225 * Constructor to create a customizable Hermite interpolator for every spacecraft state field.
226 * <p>
227 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
228 * points (about 10-20 points) in order to avoid <a href="https://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
229 * phenomenon</a> and numerical problems (including NaN appearing).
230 * <p>
231 * <b>BEWARE:</b> output frame <b>must be inertial</b> if this instance is going to interpolate between
232 * tabulated spacecraft states defined by orbit, will throw an error otherwise.
233 *
234 * @param interpolationPoints number of interpolation points
235 * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
236 * @param outputFrame output frame
237 * @param attitudeReferenceFrame reference frame from which attitude is defined
238 * @param pvaFilter filter for derivatives from the sample to use in position-velocity-acceleration interpolation
239 * @param angularFilter filter for derivatives from the sample to use in attitude interpolation
240 */
241 public SpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold,
242 final Frame outputFrame, final Frame attitudeReferenceFrame,
243 final CartesianDerivativesFilter pvaFilter,
244 final AngularDerivativesFilter angularFilter) {
245 this(interpolationPoints, extrapolationThreshold, outputFrame,
246 new OrbitHermiteInterpolator(interpolationPoints, extrapolationThreshold, outputFrame, pvaFilter),
247 new AbsolutePVCoordinatesHermiteInterpolator(interpolationPoints, extrapolationThreshold, outputFrame,
248 pvaFilter),
249 new TimeStampedDoubleHermiteInterpolator(interpolationPoints, extrapolationThreshold),
250 new AttitudeInterpolator(attitudeReferenceFrame,
251 new TimeStampedAngularCoordinatesHermiteInterpolator(interpolationPoints,
252 extrapolationThreshold,
253 angularFilter)),
254 new TimeStampedDoubleHermiteInterpolator(interpolationPoints, extrapolationThreshold));
255 }
256
257 /**
258 * Constructor.
259 * <p>
260 * At least one interpolator for either orbit or absolute position-velocity-acceleration is needed. All the other
261 * interpolators can be left to null if the user do not want to interpolate these values.
262 * <p>
263 * <b>BEWARE:</b> output frame <b>must be inertial</b> if interpolated spacecraft states are defined by orbit. Throws an
264 * error otherwise.
265 * <p>
266 * <b>BEWARE:</b> it is up to the user to check the consistency of input interpolators.
267 *
268 * @param interpolationPoints number of interpolation points
269 * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
270 * @param outputFrame output frame (inertial if the user is planning to use the orbit interpolator)
271 * @param orbitInterpolator orbit interpolator (can be null if absPVAInterpolator is defined)
272 * @param absPVAInterpolator absolute position-velocity-acceleration (can be null if orbitInterpolator is defined)
273 * @param massInterpolator mass interpolator (can be null)
274 * @param attitudeInterpolator attitude interpolator (can be null)
275 * @param additionalStateInterpolator additional state interpolator (can be null)
276 *
277 * @since 12.0.1
278 */
279 public SpacecraftStateInterpolator(final int interpolationPoints, final double extrapolationThreshold,
280 final Frame outputFrame, final TimeInterpolator<Orbit> orbitInterpolator,
281 final TimeInterpolator<AbsolutePVCoordinates> absPVAInterpolator,
282 final TimeInterpolator<TimeStampedDouble> massInterpolator,
283 final TimeInterpolator<Attitude> attitudeInterpolator,
284 final TimeInterpolator<TimeStampedDouble> additionalStateInterpolator) {
285 super(interpolationPoints, extrapolationThreshold);
286 checkAtLeastOneInterpolator(orbitInterpolator, absPVAInterpolator);
287 this.outputFrame = outputFrame;
288 this.orbitInterpolator = orbitInterpolator;
289 this.absPVAInterpolator = absPVAInterpolator;
290 this.massInterpolator = massInterpolator;
291 this.attitudeInterpolator = attitudeInterpolator;
292 this.additionalStateInterpolator = additionalStateInterpolator;
293 }
294
295 /**
296 * Check that an interpolator exist for given sample state definition.
297 *
298 * @param sample sample (non empty)
299 * @param orbitInterpolatorIsPresent flag defining if an orbit interpolator has been defined for this instance
300 * @param absPVInterpolatorIsPresent flag defining if an absolute position-velocity-acceleration interpolator has been
301 * defined for this instance
302 *
303 * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state
304 * definition type
305 */
306 public static void checkSampleAndInterpolatorConsistency(final List<SpacecraftState> sample,
307 final boolean orbitInterpolatorIsPresent,
308 final boolean absPVInterpolatorIsPresent) {
309 // Get first state definition
310 final SpacecraftState earliestState = sample.getFirst();
311
312 if (earliestState.isOrbitDefined() && !orbitInterpolatorIsPresent ||
313 !earliestState.isOrbitDefined() && !absPVInterpolatorIsPresent) {
314 throw new OrekitIllegalArgumentException(OrekitMessages.WRONG_INTERPOLATOR_DEFINED_FOR_STATE_INTERPOLATION);
315 }
316 }
317
318 /**
319 * Check that all state are either orbit defined or based on absolute position-velocity-acceleration.
320 *
321 * @param states spacecraft state sample
322 */
323 public static void checkStatesDefinitionsConsistency(final List<SpacecraftState> states) {
324 // Check all states handle the same additional states and are defined the same way (orbit or absolute PVA)
325 final SpacecraftState s0 = states.getFirst();
326 final boolean s0IsOrbitDefined = s0.isOrbitDefined();
327 for (final SpacecraftState state : states) {
328 s0.ensureCompatibleAdditionalStates(state);
329 if (s0IsOrbitDefined != state.isOrbitDefined()) {
330 throw new OrekitIllegalArgumentException(OrekitMessages.DIFFERENT_STATE_DEFINITION);
331 }
332 }
333 }
334
335 /**
336 * {@inheritDoc}
337 * <p>
338 * The additional states that are interpolated are the ones already present in the first neighbor instance. The sample
339 * instances must therefore have at least the same additional states as this neighbor instance. They may have more
340 * additional states, but the extra ones will be ignored.
341 * <p>
342 * All the sample instances <em>must</em> be based on similar trajectory data, i.e. they must either all be based on
343 * orbits or all be based on absolute position-velocity-acceleration. Any inconsistency will trigger an
344 * {@link OrekitIllegalArgumentException}.
345 *
346 * @throws OrekitIllegalArgumentException if there are states defined by orbits and absolute
347 * position-velocity-acceleration coordinates
348 * @throws OrekitIllegalArgumentException if there is no defined interpolator for given sample spacecraft state
349 * definition type
350 */
351 @Override
352 public SpacecraftState interpolate(final AbsoluteDate interpolationDate,
353 final Collection<? extends SpacecraftState> sample) {
354
355 final List<SpacecraftState> sampleList = new ArrayList<>(sample);
356
357 // If sample is empty, an error will be thrown in super method
358 if (!sample.isEmpty()) {
359
360 // Check given that given states definition are consistent
361 // (all defined by either orbits or absolute position-velocity-acceleration coordinates)
362 checkStatesDefinitionsConsistency(sampleList);
363
364 // Check interpolator and sample consistency
365 checkSampleAndInterpolatorConsistency(sampleList, orbitInterpolator != null, absPVAInterpolator != null);
366 }
367
368 return super.interpolate(interpolationDate, sample);
369 }
370
371 /** {@inheritDoc} */
372 @Override
373 public List<TimeInterpolator<? extends TimeStamped>> getSubInterpolators() {
374
375 // Add all sub interpolators that are defined
376 final List<TimeInterpolator<? extends TimeStamped>> subInterpolators = new ArrayList<>();
377
378 addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators);
379 addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators);
380 addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators);
381 addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators);
382 addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators);
383
384 return subInterpolators;
385
386 }
387
388 /**
389 * {@inheritDoc}
390 */
391 @Override
392 protected SpacecraftState interpolate(final InterpolationData interpolationData) {
393
394 // Get first state definition
395 final List<SpacecraftState> samples = interpolationData.getNeighborList();
396 final SpacecraftState earliestState = samples.getFirst();
397 final boolean areOrbitDefined = earliestState.isOrbitDefined();
398
399 // Prepare samples
400 final List<Attitude> attitudes = new ArrayList<>();
401
402 final List<TimeStampedDouble> masses = new ArrayList<>();
403
404 final List<DataDictionary.Entry> additionalEntries = earliestState.getAdditionalDataValues().getData();
405 final Map<String, List<Pair<AbsoluteDate, Object>>> additionalSample =
406 createAdditionalDataSample(additionalEntries);
407
408 final List<DoubleArrayDictionary.Entry> additionalDotEntries =
409 earliestState.getAdditionalStatesDerivatives().getData();
410 final Map<String, List<Pair<AbsoluteDate, double[]>>> additionalDotSample =
411 createAdditionalStateSample(additionalDotEntries);
412
413 // Fill interpolators with samples
414 final List<Orbit> orbitSample = new ArrayList<>();
415 final List<AbsolutePVCoordinates> absPVASample = new ArrayList<>();
416 for (SpacecraftState state : samples) {
417 final AbsoluteDate currentDate = state.getDate();
418
419 // Add orbit sample if state is defined with an orbit
420 if (state.isOrbitDefined()) {
421 orbitSample.add(state.getOrbit());
422 }
423 // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration
424 else {
425 absPVASample.add(state.getAbsPVA());
426 }
427
428 // Add mass sample
429 if (massInterpolator != null) {
430 masses.add(new TimeStampedDouble(state.getDate(), state.getMass()));
431 }
432
433 // Add attitude sample if it is interpolated
434 if (attitudeInterpolator != null) {
435 attitudes.add(state.getAttitude());
436 }
437
438 if (additionalStateInterpolator != null) {
439
440 // Add all additional state values if they are interpolated
441 for (final Map.Entry<String, List<Pair<AbsoluteDate, Object>>> entry : additionalSample.entrySet()) {
442 entry.getValue().add(new Pair<>(currentDate, state.getAdditionalState(entry.getKey())));
443 }
444
445 // Add all additional state derivative values if they are interpolated
446 for (final Map.Entry<String, List<Pair<AbsoluteDate, double[]>>> entry : additionalDotSample.entrySet()) {
447 entry.getValue().add(new Pair<>(currentDate, state.getAdditionalStateDerivative(entry.getKey())));
448 }
449 }
450 }
451
452 // Interpolate mass
453 final AbsoluteDate interpolationDate = interpolationData.getInterpolationDate();
454 final double interpolatedMass;
455 if (massInterpolator != null) {
456 interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue();
457 } else {
458 interpolatedMass = SpacecraftState.DEFAULT_MASS;
459 }
460
461 // Interpolate additional states and derivatives
462 final DataDictionary interpolatedAdditional;
463 final DoubleArrayDictionary interpolatedAdditionalDot;
464 if (additionalStateInterpolator != null) {
465 interpolatedAdditional = interpolateAdditionalState(interpolationDate, additionalSample).orElse(null);
466 interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample).map(DataDictionary::toDoubleDictionary).orElse(null);
467 } else {
468 interpolatedAdditional = null;
469 interpolatedAdditionalDot = null;
470 }
471
472 // Interpolate orbit
473 if (areOrbitDefined && orbitInterpolator != null) {
474 final Orbit interpolatedOrbit = orbitInterpolator.interpolate(interpolationDate, orbitSample);
475
476 final Attitude interpolatedAttitude = interpolateAttitude(interpolationDate, attitudes, interpolatedOrbit);
477
478 return new SpacecraftState(interpolatedOrbit, interpolatedAttitude, interpolatedMass, interpolatedAdditional,
479 interpolatedAdditionalDot);
480 }
481 // Interpolate absolute position-velocity-acceleration
482 else if (!areOrbitDefined && absPVAInterpolator != null) {
483
484 final AbsolutePVCoordinates interpolatedAbsPva = absPVAInterpolator.interpolate(interpolationDate, absPVASample);
485
486 final Attitude interpolatedAttitude = interpolateAttitude(interpolationDate, attitudes, interpolatedAbsPva);
487
488 return new SpacecraftState(interpolatedAbsPva, interpolatedAttitude, interpolatedMass, interpolatedAdditional,
489 interpolatedAdditionalDot);
490 }
491 // Should never happen
492 else {
493 throw new OrekitInternalError(null);
494 }
495
496 }
497
498 /**
499 * Get output frame.
500 *
501 * @return output frame
502 */
503 public Frame getOutputFrame() {
504 return outputFrame;
505 }
506
507 /**
508 * Get orbit interpolator.
509 *
510 * @return optional orbit interpolator
511 *
512 * @see Optional
513 */
514 public Optional<TimeInterpolator<Orbit>> getOrbitInterpolator() {
515 return Optional.ofNullable(orbitInterpolator);
516 }
517
518 /**
519 * Get absolute position-velocity-acceleration interpolator.
520 *
521 * @return optional absolute position-velocity-acceleration interpolator
522 *
523 * @see Optional
524 */
525 public Optional<TimeInterpolator<AbsolutePVCoordinates>> getAbsPVAInterpolator() {
526 return Optional.ofNullable(absPVAInterpolator);
527 }
528
529 /**
530 * Get mass interpolator.
531 *
532 * @return optional mass interpolator
533 *
534 * @see Optional
535 */
536 public Optional<TimeInterpolator<TimeStampedDouble>> getMassInterpolator() {
537 return Optional.ofNullable(massInterpolator);
538 }
539
540 /**
541 * Get attitude interpolator.
542 *
543 * @return optional attitude interpolator
544 *
545 * @see Optional
546 */
547 public Optional<TimeInterpolator<Attitude>> getAttitudeInterpolator() {
548 return Optional.ofNullable(attitudeInterpolator);
549 }
550
551 /**
552 * Get additional state interpolator.
553 *
554 * @return optional additional state interpolator
555 *
556 * @see Optional
557 */
558 public Optional<TimeInterpolator<TimeStampedDouble>> getAdditionalStateInterpolator() {
559 return Optional.ofNullable(additionalStateInterpolator);
560 }
561
562 /**
563 * Check that at least one interpolator is defined.
564 *
565 * @param orbitInterpolatorToCheck orbit interpolator
566 * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator
567 */
568 private void checkAtLeastOneInterpolator(final TimeInterpolator<Orbit> orbitInterpolatorToCheck,
569 final TimeInterpolator<AbsolutePVCoordinates> absPVAInterpolatorToCheck) {
570 if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) {
571 throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION);
572 }
573 }
574
575 /**
576 * Create empty samples for given additional entries.
577 *
578 * @param additionalEntries tabulated additional entries
579 *
580 * @return empty samples for given additional entries
581 */
582 private Map<String, List<Pair<AbsoluteDate, double[]>>> createAdditionalStateSample(
583 final List<DoubleArrayDictionary.Entry> additionalEntries) {
584 final Map<String, List<Pair<AbsoluteDate, double[]>>> additionalSamples = new HashMap<>(additionalEntries.size());
585
586 for (final DoubleArrayDictionary.Entry entry : additionalEntries) {
587 additionalSamples.put(entry.getKey(), new ArrayList<>());
588 }
589 return additionalSamples;
590 }
591
592 /**
593 * Create empty samples for given additional entries.
594 *
595 * @param additionalEntries tabulated additional entries
596 *
597 * @return empty samples for given additional entries
598 */
599 private Map<String, List<Pair<AbsoluteDate, Object>>> createAdditionalDataSample(
600 final List<DataDictionary.Entry> additionalEntries) {
601 final Map<String, List<Pair<AbsoluteDate, Object>>> additionalSamples = new HashMap<>(additionalEntries.size());
602
603 for (final DataDictionary.Entry entry : additionalEntries) {
604 additionalSamples.put(entry.getKey(), new ArrayList<>());
605 }
606 return additionalSamples;
607 }
608
609 /**
610 * Interpolate additional values.
611 * Double arrays are interpolated whereas objects values are kept identical until change.
612 *
613 * @param <T> type of the data
614 * @param interpolationDate interpolation date
615 * @param additionalSamples additional data samples
616 *
617 * @return interpolated additional data values
618 */
619 private <T> Optional<DataDictionary> interpolateAdditionalState(final AbsoluteDate interpolationDate,
620 final Map<String, List<Pair<AbsoluteDate, T>>> additionalSamples) {
621 final Optional<DataDictionary> interpolatedAdditional;
622
623 if (additionalSamples.isEmpty()) {
624 interpolatedAdditional = Optional.empty();
625 } else {
626 interpolatedAdditional = Optional.of(new DataDictionary(additionalSamples.size()));
627 for (final Map.Entry<String, List<Pair<AbsoluteDate, T>>> entry : additionalSamples.entrySet()) {
628 // Get current entry
629 final List<Pair<AbsoluteDate, T>> currentAdditionalSamples = entry.getValue();
630 final T currentInterpolatedAdditional;
631 if (currentAdditionalSamples.getFirst().getValue() instanceof double[]) {
632 currentInterpolatedAdditional = (T) interpolateAdditionalSamples(interpolationDate, currentAdditionalSamples);
633 } else {
634 currentInterpolatedAdditional = currentAdditionalSamples.stream()
635 .filter(pair -> pair.getKey().isAfter(interpolationDate))
636 .min(Comparator.comparing(Pair::getKey))
637 .get()
638 .getValue();
639 }
640 interpolatedAdditional.get().put(entry.getKey(), currentInterpolatedAdditional);
641 }
642 }
643 return interpolatedAdditional;
644 }
645
646 private <T> double[] interpolateAdditionalSamples(final AbsoluteDate interpolationDate, final List<Pair<AbsoluteDate, T>> currentAdditionalSamples) {
647 // Extract number of values for this specific entry
648 final int nbOfValues = ((double[]) currentAdditionalSamples.getFirst().getValue()).length;
649
650 // For each value of current additional state entry
651 final double[] currentInterpolatedAdditional = new double[nbOfValues];
652 for (int i = 0; i < nbOfValues; i++) {
653
654 // Create final index for lambda expression use
655 final int currentIndex = i;
656
657 // Create sample for specific value of current additional state values
658 final List<TimeStampedDouble> currentValueSample = new ArrayList<>();
659
660 currentAdditionalSamples.forEach(
661 currentSamples -> currentValueSample.add(new TimeStampedDouble(currentSamples.getFirst(), ((double[]) currentSamples.getValue())[currentIndex])));
662
663 // Interpolate
664 currentInterpolatedAdditional[i] = additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue();
665 }
666 return currentInterpolatedAdditional;
667 }
668
669 /**
670 * Interpolate attitude.
671 * <p>
672 * If no attitude interpolator were defined, create a default inertial provider with respect to the output frame.
673 *
674 * @param interpolationDate interpolation date
675 * @param attitudes attitudes sample
676 * @param pvProvider position-velocity-acceleration coordinates provider
677 *
678 * @return interpolated attitude if attitude interpolator is present, default attitude otherwise
679 */
680 private Attitude interpolateAttitude(final AbsoluteDate interpolationDate, final List<Attitude> attitudes,
681 final PVCoordinatesProvider pvProvider) {
682 if (attitudes.isEmpty()) {
683 final AttitudeProvider attitudeProvider = new FrameAlignedProvider(outputFrame);
684 return attitudeProvider.getAttitude(pvProvider, interpolationDate, outputFrame);
685 } else {
686 return attitudeInterpolator.interpolate(interpolationDate, attitudes);
687 }
688 }
689 }