1 /* Copyright 2002-2023 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 }