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.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="http://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="http://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="http://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="http://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="http://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="http://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.get(0);
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.get(0);
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, final Collection<SpacecraftState> sample) {
353
354 final List<SpacecraftState> sampleList = new ArrayList<>(sample);
355
356 // If sample is empty, an error will be thrown in super method
357 if (!sample.isEmpty()) {
358
359 // Check given that given states definition are consistent
360 // (all defined by either orbits or absolute position-velocity-acceleration coordinates)
361 checkStatesDefinitionsConsistency(sampleList);
362
363 // Check interpolator and sample consistency
364 checkSampleAndInterpolatorConsistency(sampleList, orbitInterpolator != null, absPVAInterpolator != null);
365 }
366
367 return super.interpolate(interpolationDate, sample);
368 }
369
370 /** {@inheritDoc} */
371 @Override
372 public List<TimeInterpolator<? extends TimeStamped>> getSubInterpolators() {
373
374 // Add all sub interpolators that are defined
375 final List<TimeInterpolator<? extends TimeStamped>> subInterpolators = new ArrayList<>();
376
377 addOptionalSubInterpolatorIfDefined(orbitInterpolator, subInterpolators);
378 addOptionalSubInterpolatorIfDefined(absPVAInterpolator, subInterpolators);
379 addOptionalSubInterpolatorIfDefined(massInterpolator, subInterpolators);
380 addOptionalSubInterpolatorIfDefined(attitudeInterpolator, subInterpolators);
381 addOptionalSubInterpolatorIfDefined(additionalStateInterpolator, subInterpolators);
382
383 return subInterpolators;
384
385 }
386
387 /**
388 * {@inheritDoc}
389 */
390 @Override
391 protected SpacecraftState interpolate(final InterpolationData interpolationData) {
392
393 // Get first state definition
394 final List<SpacecraftState> samples = interpolationData.getNeighborList();
395 final SpacecraftState earliestState = samples.get(0);
396 final boolean areOrbitDefined = earliestState.isOrbitDefined();
397
398 // Prepare samples
399 final List<Attitude> attitudes = new ArrayList<>();
400
401 final List<TimeStampedDouble> masses = new ArrayList<>();
402
403 final List<DataDictionary.Entry> additionalEntries = earliestState.getAdditionalDataValues().getData();
404 final Map<String, List<Pair<AbsoluteDate, Object>>> additionalSample =
405 createAdditionalDataSample(additionalEntries);
406
407 final List<DoubleArrayDictionary.Entry> additionalDotEntries =
408 earliestState.getAdditionalStatesDerivatives().getData();
409 final Map<String, List<Pair<AbsoluteDate, double[]>>> additionalDotSample =
410 createAdditionalStateSample(additionalDotEntries);
411
412 // Fill interpolators with samples
413 final List<Orbit> orbitSample = new ArrayList<>();
414 final List<AbsolutePVCoordinates> absPVASample = new ArrayList<>();
415 for (SpacecraftState state : samples) {
416 final AbsoluteDate currentDate = state.getDate();
417
418 // Add orbit sample if state is defined with an orbit
419 if (state.isOrbitDefined()) {
420 orbitSample.add(state.getOrbit());
421 }
422 // Add absolute position-velocity-acceleration sample if state is defined with an absolute position-velocity-acceleration
423 else {
424 absPVASample.add(state.getAbsPVA());
425 }
426
427 // Add mass sample
428 if (massInterpolator != null) {
429 masses.add(new TimeStampedDouble(state.getMass(), state.getDate()));
430 }
431
432 // Add attitude sample if it is interpolated
433 if (attitudeInterpolator != null) {
434 attitudes.add(state.getAttitude());
435 }
436
437 if (additionalStateInterpolator != null) {
438
439 // Add all additional state values if they are interpolated
440 for (final Map.Entry<String, List<Pair<AbsoluteDate, Object>>> entry : additionalSample.entrySet()) {
441 entry.getValue().add(new Pair<>(currentDate, state.getAdditionalState(entry.getKey())));
442 }
443
444 // Add all additional state derivative values if they are interpolated
445 for (final Map.Entry<String, List<Pair<AbsoluteDate, double[]>>> entry : additionalDotSample.entrySet()) {
446 entry.getValue().add(new Pair<>(currentDate, state.getAdditionalStateDerivative(entry.getKey())));
447 }
448 }
449 }
450
451 // Interpolate mass
452 final AbsoluteDate interpolationDate = interpolationData.getInterpolationDate();
453 final double interpolatedMass;
454 if (massInterpolator != null) {
455 interpolatedMass = massInterpolator.interpolate(interpolationDate, masses).getValue();
456 } else {
457 interpolatedMass = SpacecraftState.DEFAULT_MASS;
458 }
459
460 // Interpolate additional states and derivatives
461 final DataDictionary interpolatedAdditional;
462 final DoubleArrayDictionary interpolatedAdditionalDot;
463 if (additionalStateInterpolator != null) {
464 interpolatedAdditional = interpolateAdditionalState(interpolationDate, additionalSample).orElse(null);
465 interpolatedAdditionalDot = interpolateAdditionalState(interpolationDate, additionalDotSample).map(DataDictionary::toDoubleDictionary).orElse(null);
466 } else {
467 interpolatedAdditional = null;
468 interpolatedAdditionalDot = null;
469 }
470
471 // Interpolate orbit
472 if (areOrbitDefined && orbitInterpolator != null) {
473 final Orbit interpolatedOrbit = orbitInterpolator.interpolate(interpolationDate, orbitSample);
474
475 final Attitude interpolatedAttitude = interpolateAttitude(interpolationDate, attitudes, interpolatedOrbit);
476
477 return new SpacecraftState(interpolatedOrbit, interpolatedAttitude, interpolatedMass, interpolatedAdditional,
478 interpolatedAdditionalDot);
479 }
480 // Interpolate absolute position-velocity-acceleration
481 else if (!areOrbitDefined && absPVAInterpolator != null) {
482
483 final AbsolutePVCoordinates interpolatedAbsPva = absPVAInterpolator.interpolate(interpolationDate, absPVASample);
484
485 final Attitude interpolatedAttitude = interpolateAttitude(interpolationDate, attitudes, interpolatedAbsPva);
486
487 return new SpacecraftState(interpolatedAbsPva, interpolatedAttitude, interpolatedMass, interpolatedAdditional,
488 interpolatedAdditionalDot);
489 }
490 // Should never happen
491 else {
492 throw new OrekitInternalError(null);
493 }
494
495 }
496
497 /**
498 * Get output frame.
499 *
500 * @return output frame
501 */
502 public Frame getOutputFrame() {
503 return outputFrame;
504 }
505
506 /**
507 * Get orbit interpolator.
508 *
509 * @return optional orbit interpolator
510 *
511 * @see Optional
512 */
513 public Optional<TimeInterpolator<Orbit>> getOrbitInterpolator() {
514 return Optional.ofNullable(orbitInterpolator);
515 }
516
517 /**
518 * Get absolute position-velocity-acceleration interpolator.
519 *
520 * @return optional absolute position-velocity-acceleration interpolator
521 *
522 * @see Optional
523 */
524 public Optional<TimeInterpolator<AbsolutePVCoordinates>> getAbsPVAInterpolator() {
525 return Optional.ofNullable(absPVAInterpolator);
526 }
527
528 /**
529 * Get mass interpolator.
530 *
531 * @return optional mass interpolator
532 *
533 * @see Optional
534 */
535 public Optional<TimeInterpolator<TimeStampedDouble>> getMassInterpolator() {
536 return Optional.ofNullable(massInterpolator);
537 }
538
539 /**
540 * Get attitude interpolator.
541 *
542 * @return optional attitude interpolator
543 *
544 * @see Optional
545 */
546 public Optional<TimeInterpolator<Attitude>> getAttitudeInterpolator() {
547 return Optional.ofNullable(attitudeInterpolator);
548 }
549
550 /**
551 * Get additional state interpolator.
552 *
553 * @return optional additional state interpolator
554 *
555 * @see Optional
556 */
557 public Optional<TimeInterpolator<TimeStampedDouble>> getAdditionalStateInterpolator() {
558 return Optional.ofNullable(additionalStateInterpolator);
559 }
560
561 /**
562 * Check that at least one interpolator is defined.
563 *
564 * @param orbitInterpolatorToCheck orbit interpolator
565 * @param absPVAInterpolatorToCheck absolute position-velocity-acceleration interpolator
566 */
567 private void checkAtLeastOneInterpolator(final TimeInterpolator<Orbit> orbitInterpolatorToCheck,
568 final TimeInterpolator<AbsolutePVCoordinates> absPVAInterpolatorToCheck) {
569 if (orbitInterpolatorToCheck == null && absPVAInterpolatorToCheck == null) {
570 throw new OrekitIllegalArgumentException(OrekitMessages.NO_INTERPOLATOR_FOR_STATE_DEFINITION);
571 }
572 }
573
574 /**
575 * Create empty samples for given additional entries.
576 *
577 * @param additionalEntries tabulated additional entries
578 *
579 * @return empty samples for given additional entries
580 */
581 private Map<String, List<Pair<AbsoluteDate, double[]>>> createAdditionalStateSample(
582 final List<DoubleArrayDictionary.Entry> additionalEntries) {
583 final Map<String, List<Pair<AbsoluteDate, double[]>>> additionalSamples = new HashMap<>(additionalEntries.size());
584
585 for (final DoubleArrayDictionary.Entry entry : additionalEntries) {
586 additionalSamples.put(entry.getKey(), new ArrayList<>());
587 }
588 return additionalSamples;
589 }
590
591 /**
592 * Create empty samples for given additional entries.
593 *
594 * @param additionalEntries tabulated additional entries
595 *
596 * @return empty samples for given additional entries
597 */
598 private Map<String, List<Pair<AbsoluteDate, Object>>> createAdditionalDataSample(
599 final List<DataDictionary.Entry> additionalEntries) {
600 final Map<String, List<Pair<AbsoluteDate, Object>>> additionalSamples = new HashMap<>(additionalEntries.size());
601
602 for (final DataDictionary.Entry entry : additionalEntries) {
603 additionalSamples.put(entry.getKey(), new ArrayList<>());
604 }
605 return additionalSamples;
606 }
607
608 /**
609 * Interpolate additional values.
610 * Double arrays are interpolated whereas objects values are kept identical until change.
611 *
612 * @param <T> type of the data
613 * @param interpolationDate interpolation date
614 * @param additionalSamples additional data samples
615 *
616 * @return interpolated additional data values
617 */
618 private <T> Optional<DataDictionary> interpolateAdditionalState(final AbsoluteDate interpolationDate,
619 final Map<String, List<Pair<AbsoluteDate, T>>> additionalSamples) {
620 final Optional<DataDictionary> interpolatedAdditional;
621
622 if (additionalSamples.isEmpty()) {
623 interpolatedAdditional = Optional.empty();
624 } else {
625 interpolatedAdditional = Optional.of(new DataDictionary(additionalSamples.size()));
626 for (final Map.Entry<String, List<Pair<AbsoluteDate, T>>> entry : additionalSamples.entrySet()) {
627 // Get current entry
628 final List<Pair<AbsoluteDate, T>> currentAdditionalSamples = entry.getValue();
629 final T currentInterpolatedAdditional;
630 if (currentAdditionalSamples.get(0).getValue() instanceof double[]) {
631 currentInterpolatedAdditional = (T) interpolateAdditionalSamples(interpolationDate, currentAdditionalSamples);
632 } else {
633 currentInterpolatedAdditional = currentAdditionalSamples.stream()
634 .filter(pair -> pair.getKey().isAfter(interpolationDate))
635 .min(Comparator.comparing(Pair::getKey))
636 .get()
637 .getValue();
638 }
639 interpolatedAdditional.get().put(entry.getKey(), currentInterpolatedAdditional);
640 }
641 }
642 return interpolatedAdditional;
643 }
644
645 private <T> double[] interpolateAdditionalSamples(final AbsoluteDate interpolationDate, final List<Pair<AbsoluteDate, T>> currentAdditionalSamples) {
646 // Extract number of values for this specific entry
647 final int nbOfValues = ((double[]) currentAdditionalSamples.get(0).getValue()).length;
648
649 // For each value of current additional state entry
650 final double[] currentInterpolatedAdditional = new double[nbOfValues];
651 for (int i = 0; i < nbOfValues; i++) {
652
653 // Create final index for lambda expression use
654 final int currentIndex = i;
655
656 // Create sample for specific value of current additional state values
657 final List<TimeStampedDouble> currentValueSample = new ArrayList<>();
658
659 currentAdditionalSamples.forEach(
660 currentSamples -> currentValueSample.add(new TimeStampedDouble(((double[]) currentSamples.getValue())[currentIndex], currentSamples.getFirst())));
661
662 // Interpolate
663 currentInterpolatedAdditional[i] = additionalStateInterpolator.interpolate(interpolationDate, currentValueSample).getValue();
664 }
665 return currentInterpolatedAdditional;
666 }
667
668 /**
669 * Interpolate attitude.
670 * <p>
671 * If no attitude interpolator were defined, create a default inertial provider with respect to the output frame.
672 *
673 * @param interpolationDate interpolation date
674 * @param attitudes attitudes sample
675 * @param pvProvider position-velocity-acceleration coordinates provider
676 *
677 * @return interpolated attitude if attitude interpolator is present, default attitude otherwise
678 */
679 private Attitude interpolateAttitude(final AbsoluteDate interpolationDate, final List<Attitude> attitudes,
680 final PVCoordinatesProvider pvProvider) {
681 if (attitudes.isEmpty()) {
682 final AttitudeProvider attitudeProvider = new FrameAlignedProvider(outputFrame);
683 return attitudeProvider.getAttitude(pvProvider, interpolationDate, outputFrame);
684 } else {
685 return attitudeInterpolator.interpolate(interpolationDate, attitudes);
686 }
687 }
688 }