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 }