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.utils;
18
19 import org.hipparchus.CalculusFieldElement;
20 import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator;
21 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
22 import org.orekit.errors.OrekitInternalError;
23 import org.orekit.time.AbstractFieldTimeInterpolator;
24 import org.orekit.time.FieldAbsoluteDate;
25
26 import java.util.stream.Stream;
27
28 /**
29 * Class using a Hermite interpolator to interpolate time stamped position-velocity-acceleration coordinates.
30 * <p>
31 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points
32 * (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
33 * and numerical problems (including NaN appearing).
34 *
35 * @param <KK> type of the field element
36 *
37 * @author Luc Maisonobe
38 * @author Vincent Cucchietti
39 * @see FieldHermiteInterpolator
40 * @see TimeStampedFieldPVCoordinates
41 */
42 public class TimeStampedFieldPVCoordinatesHermiteInterpolator<KK extends CalculusFieldElement<KK>>
43 extends AbstractFieldTimeInterpolator<TimeStampedFieldPVCoordinates<KK>, KK> {
44
45 /** Filter for derivatives from the sample to use in interpolation. */
46 private final CartesianDerivativesFilter filter;
47
48 /**
49 * Constructor with :
50 * <ul>
51 * <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
52 * <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
53 * <li>Use of angular and first time derivative for attitude interpolation</li>
54 * </ul>
55 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
56 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
57 * phenomenon</a> and numerical problems (including NaN appearing).
58 */
59 public TimeStampedFieldPVCoordinatesHermiteInterpolator() {
60 this(DEFAULT_INTERPOLATION_POINTS);
61 }
62
63 /**
64 * Constructor with :
65 * <ul>
66 * <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
67 * <li>Use of position and both time derivatives for attitude interpolation</li>
68 * </ul>
69 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
70 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
71 * phenomenon</a> and numerical problems (including NaN appearing).
72 *
73 * @param interpolationPoints number of interpolation points
74 */
75 public TimeStampedFieldPVCoordinatesHermiteInterpolator(final int interpolationPoints) {
76
77 this(interpolationPoints, CartesianDerivativesFilter.USE_PVA);
78 }
79
80 /**
81 * Constructor with :
82 * <ul>
83 * <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
84 * </ul>
85 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
86 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
87 * phenomenon</a> and numerical problems (including NaN appearing).
88 *
89 * @param interpolationPoints number of interpolation points
90 * @param filter filter for derivatives from the sample to use in interpolation
91 */
92 public TimeStampedFieldPVCoordinatesHermiteInterpolator(final int interpolationPoints,
93 final CartesianDerivativesFilter filter) {
94 this(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC, filter);
95 }
96
97 /**
98 * Constructor.
99 * <p>
100 * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
101 * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
102 * phenomenon</a> and numerical problems (including NaN appearing).
103 *
104 * @param interpolationPoints number of interpolation points
105 * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
106 * @param filter filter for derivatives from the sample to use in interpolation
107 */
108 public TimeStampedFieldPVCoordinatesHermiteInterpolator(final int interpolationPoints,
109 final double extrapolationThreshold,
110 final CartesianDerivativesFilter filter) {
111 super(interpolationPoints, extrapolationThreshold);
112 this.filter = filter;
113 }
114
115 /** filter for derivatives from the sample to use in interpolation.
116 * @return filter for derivatives from the sample to use in interpolation
117 */
118 public CartesianDerivativesFilter getFilter() {
119 return filter;
120 }
121
122 /**
123 * {@inheritDoc}
124 * <p>
125 * The interpolated instance is created by polynomial Hermite interpolation ensuring velocity remains the exact
126 * derivative of position.
127 * <p>
128 * Note that even if first time derivatives (velocities) from sample can be ignored, the interpolated instance always
129 * includes interpolated derivatives. This feature can be used explicitly to compute these derivatives when it would be
130 * too complex to compute them from an analytical formula: just compute a few sample points from the explicit formula and
131 * set the derivatives to zero in these sample points, then use interpolation to add derivatives consistent with the
132 * positions.
133 */
134 @Override
135 protected TimeStampedFieldPVCoordinates<KK> interpolate(final InterpolationData interpolationData) {
136
137 // Get interpolation date
138 final FieldAbsoluteDate<KK> interpolationDate = interpolationData.getInterpolationDate();
139
140 // Convert sample to stream
141 final Stream<TimeStampedFieldPVCoordinates<KK>> sample = interpolationData.getNeighborList().stream();
142
143 // Set up an interpolator taking derivatives into account
144 final FieldHermiteInterpolator<KK> interpolator = new FieldHermiteInterpolator<>();
145
146 // Add sample points
147 switch (filter) {
148 case USE_P:
149 // populate sample with position data, ignoring velocity
150 sample.forEach(pv -> {
151 final FieldVector3D<KK> position = pv.getPosition();
152 interpolator.addSamplePoint(pv.getDate().durationFrom(interpolationDate),
153 position.toArray());
154 });
155 break;
156 case USE_PV:
157 // populate sample with position and velocity data
158 sample.forEach(pv -> {
159 final FieldVector3D<KK> position = pv.getPosition();
160 final FieldVector3D<KK> velocity = pv.getVelocity();
161 interpolator.addSamplePoint(pv.getDate().durationFrom(interpolationDate),
162 position.toArray(), velocity.toArray());
163 });
164 break;
165 case USE_PVA:
166 // populate sample with position, velocity and acceleration data
167 sample.forEach(pv -> {
168 final FieldVector3D<KK> position = pv.getPosition();
169 final FieldVector3D<KK> velocity = pv.getVelocity();
170 final FieldVector3D<KK> acceleration = pv.getAcceleration();
171 interpolator.addSamplePoint(pv.getDate().durationFrom(interpolationDate),
172 position.toArray(), velocity.toArray(), acceleration.toArray());
173 });
174 break;
175 default:
176 // this should never happen
177 throw new OrekitInternalError(null);
178 }
179
180 // Interpolate
181 final KK[][] pva = interpolator.derivatives(interpolationDate.getField().getZero(), 2);
182
183 // Build a new interpolated instance
184 return new TimeStampedFieldPVCoordinates<>(interpolationDate, new FieldVector3D<>(pva[0]), new FieldVector3D<>(pva[1]),
185 new FieldVector3D<>(pva[2]));
186 }
187 }