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