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