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.frames.Frame;
24  import org.orekit.time.AbstractFieldTimeInterpolator;
25  import org.orekit.time.FieldAbsoluteDate;
26  
27  import java.util.List;
28  
29  /**
30   * Class using a Hermite interpolator to interpolate absolute position-velocity-acceleration coordinates.
31   * <p>
32   * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation points
33   * (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's phenomenon</a>
34   * and numerical problems (including NaN appearing).
35   *
36   * @author Luc Maisonobe
37   * @author Vincent Cucchietti
38   * @see FieldHermiteInterpolator
39   * @see FieldAbsolutePVCoordinates
40   * @param <KK> type of the field elements
41   */
42  public class FieldAbsolutePVCoordinatesHermiteInterpolator<KK extends CalculusFieldElement<KK>>
43          extends AbstractFieldTimeInterpolator<FieldAbsolutePVCoordinates<KK>, KK> {
44  
45      /** Filter for derivatives from the sample to use in interpolation. */
46      private final CartesianDerivativesFilter filter;
47  
48      /** Output frame for the interpolated instance. */
49      private final Frame outputFrame;
50  
51      /**
52       * Constructor with :
53       * <ul>
54       *     <li>Default number of interpolation points of {@code DEFAULT_INTERPOLATION_POINTS}</li>
55       *     <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
56       *     <li>Use of position and two time derivatives during interpolation</li>
57       * </ul>
58       * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
59       * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
60       * phenomenon</a> and numerical problems (including NaN appearing).
61       *
62       * @param outputFrame frame for the interpolated instance
63       */
64      public FieldAbsolutePVCoordinatesHermiteInterpolator(final Frame outputFrame) {
65          this(DEFAULT_INTERPOLATION_POINTS, outputFrame);
66      }
67  
68      /**
69       * Constructor with :
70       * <ul>
71       *     <li>Default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s)</li>
72       *     <li>Use of position and two time derivatives during interpolation</li>
73       * </ul>
74       * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
75       * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
76       * phenomenon</a> and numerical problems (including NaN appearing).
77       *
78       * @param interpolationPoints number of interpolation points
79       * @param outputFrame frame for the interpolated instance
80       */
81      public FieldAbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final Frame outputFrame) {
82          this(interpolationPoints, outputFrame, CartesianDerivativesFilter.USE_PVA);
83      }
84  
85      /**
86       * Constructor with default extrapolation threshold value ({@code DEFAULT_EXTRAPOLATION_THRESHOLD_SEC} s).
87       * <p>
88       * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
89       * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
90       * phenomenon</a> and numerical problems (including NaN appearing).
91       *
92       * @param interpolationPoints number of interpolation points
93       * @param outputFrame frame for the interpolated instance
94       * @param filter filter for derivatives from the sample to use in interpolation
95       */
96      public FieldAbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final Frame outputFrame,
97                                                           final CartesianDerivativesFilter filter) {
98          super(interpolationPoints, DEFAULT_EXTRAPOLATION_THRESHOLD_SEC);
99          this.outputFrame = outputFrame;
100         this.filter      = filter;
101     }
102 
103     /**
104      * Constructor.
105      * <p>
106      * As this implementation of interpolation is polynomial, it should be used only with small number of interpolation
107      * points (about 10-20 points) in order to avoid <a href="http://en.wikipedia.org/wiki/Runge%27s_phenomenon">Runge's
108      * phenomenon</a> and numerical problems (including NaN appearing).
109      *
110      * @param interpolationPoints number of interpolation points
111      * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
112      * @param outputFrame frame for the interpolated instance
113      * @param filter filter for derivatives from the sample to use in interpolation
114      */
115     public FieldAbsolutePVCoordinatesHermiteInterpolator(final int interpolationPoints, final double extrapolationThreshold,
116                                                          final Frame outputFrame, final CartesianDerivativesFilter filter) {
117         super(interpolationPoints, extrapolationThreshold);
118         this.outputFrame = outputFrame;
119         this.filter      = filter;
120     }
121 
122     /** Get filter for derivatives from the sample to use in interpolation.
123      * @return filter for derivatives from the sample to use in interpolation
124      */
125     public CartesianDerivativesFilter getFilter() {
126         return filter;
127     }
128 
129     /** Get output frame for the interpolated instance.
130      * @return output frame for the interpolated instance
131      */
132     public Frame getOutputFrame() {
133         return outputFrame;
134     }
135 
136     /**
137      * {@inheritDoc}
138      * <p>
139      * The interpolated instance is created by polynomial Hermite interpolation ensuring velocity remains the exact
140      * derivative of position.
141      * <p>
142      * Note that even if first time derivatives (velocities) from sample can be ignored, the interpolated instance always
143      * includes interpolated derivatives. This feature can be used explicitly to compute these derivatives when it would be
144      * too complex to compute them from an analytical formula: just compute a few sample points from the explicit formula and
145      * set the derivatives to zero in these sample points, then use interpolation to add derivatives consistent with the
146      * positions.
147      */
148     @Override
149     protected FieldAbsolutePVCoordinates<KK> interpolate(final InterpolationData interpolationData) {
150 
151         // Get interpolation date
152         final FieldAbsoluteDate<KK> date = interpolationData.getInterpolationDate();
153 
154         // Get sample
155         final List<FieldAbsolutePVCoordinates<KK>> sample = interpolationData.getNeighborList();
156 
157         // Set up an interpolator taking derivatives into account
158         final FieldHermiteInterpolator<KK> interpolator = new FieldHermiteInterpolator<>();
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 FieldVector3D<KK> 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 FieldVector3D<KK> position = pv.getPosition();
174                     final FieldVector3D<KK> 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 FieldVector3D<KK> position     = pv.getPosition();
183                     final FieldVector3D<KK> velocity     = pv.getVelocity();
184                     final FieldVector3D<KK> 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 KK     zero = interpolationData.getZero();
196         final KK[][] p    = interpolator.derivatives(zero, 2);
197 
198         // build a new interpolated instance
199         return new FieldAbsolutePVCoordinates<>(outputFrame, date, new FieldVector3D<>(p[0]), new FieldVector3D<>(p[1]),
200                                                 new FieldVector3D<>(p[2]));
201     }
202 }