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