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.propagation.semianalytical.dsst.utilities;
18
19 import org.hipparchus.analysis.interpolation.HermiteInterpolator;
20 import org.hipparchus.util.FastMath;
21 import org.orekit.time.AbsoluteDate;
22
23 import java.util.ArrayList;
24
25 /** Interpolated short periodics coefficients.
26 * <p>
27 * Representation of a coefficient that need to be interpolated over time.
28 * </p><p>
29 * The short periodics coefficients can be interpolated for faster computation.
30 * This class stores computed values of the coefficients through the method
31 * {@link #addGridPoint} and gives an interpolated result through the method
32 * {@link #value}.
33 * </p>
34 * @author Nicolas Bernard
35 *
36 */
37 public class ShortPeriodicsInterpolatedCoefficient {
38
39 /**Values of the already computed coefficients.*/
40 private ArrayList<double[]> values;
41
42 /**Grid points.*/
43 private ArrayList<AbsoluteDate> abscissae;
44
45 /**Number of points used in the interpolation.*/
46 private int interpolationPoints;
47
48 /**Index of the latest closest neighbor.*/
49 private int latestClosestNeighbor;
50
51 /**Simple constructor.
52 * @param interpolationPoints number of points used in the interpolation
53 */
54 public ShortPeriodicsInterpolatedCoefficient(final int interpolationPoints) {
55 this.interpolationPoints = interpolationPoints;
56 this.abscissae = new ArrayList<>();
57 this.values = new ArrayList<>();
58 this.latestClosestNeighbor = 0;
59 }
60
61 /**Compute the value of the coefficient.
62 * @param date date at which the coefficient should be computed
63 * @return value of the coefficient
64 */
65 public double[] value(final AbsoluteDate date) {
66 //Get the closest points from the input date
67 final int[] neighbors = getNeighborsIndices(date);
68
69 //Creation and set up of the interpolator
70 final HermiteInterpolator interpolator = new HermiteInterpolator();
71 for (int i : neighbors) {
72 interpolator.addSamplePoint(abscissae.get(i).durationFrom(date), values.get(i));
73 }
74
75 //interpolation
76 return interpolator.value(0.0);
77
78 }
79
80 /**Find the closest available points from the specified date.
81 * @param date date of interest
82 * @return indices corresponding to the closest points on the time scale
83 */
84 private int[] getNeighborsIndices(final AbsoluteDate date) {
85 final int sizeofNeighborhood = FastMath.min(interpolationPoints, abscissae.size());
86 final int[] neighborsIndices = new int[sizeofNeighborhood];
87
88 //If the size of the complete sample is less than
89 //the desired number of interpolation points,
90 //then the entire sample is considered as the neighborhood
91 if (interpolationPoints >= abscissae.size()) {
92 for (int i = 0; i < sizeofNeighborhood; i++) {
93 neighborsIndices[i] = i;
94 }
95 } else {
96 // get indices around closest neighbor
97 int inf = getClosestNeighbor(date);
98 int sup = inf + 1;
99
100 while (sup - inf < interpolationPoints) {
101 if (inf == 0) { //This means that we have reached the earliest date
102 sup++;
103 } else if (sup >= abscissae.size()) { //This means that we have reached the latest date
104 inf--;
105 } else { //the choice is made between the two next neighbors
106 final double lowerNeighborDistance = FastMath.abs(abscissae.get(inf - 1).durationFrom(date));
107 final double upperNeighborDistance = FastMath.abs(abscissae.get(sup).durationFrom(date));
108
109 if (lowerNeighborDistance <= upperNeighborDistance) {
110 inf--;
111 } else {
112 sup++;
113 }
114 }
115 }
116
117 for (int i = 0; i < interpolationPoints; ++i) {
118 neighborsIndices[i] = inf + i;
119 }
120
121 }
122
123 return neighborsIndices;
124 }
125
126 /**Find the closest point from a specific date amongst the available points.
127 * @param date date of interest
128 * @return index of the closest abscissa from the date of interest
129 */
130 private int getClosestNeighbor(final AbsoluteDate date) {
131 //the starting point is the latest result of a call to this method.
132 //Indeed, as this class is meant to be called during an integration process
133 //with an input date evolving often continuously in time, there is a high
134 //probability that the result will be the same as for last call of
135 //this method.
136 final int closestNeighbor;
137
138 //case where the date is before the available points
139 if (date.compareTo(abscissae.get(0)) <= 0) {
140 closestNeighbor = 0;
141 }
142 //case where the date is after the available points
143 else if (date.compareTo(abscissae.get(abscissae.size() - 1)) >= 0) {
144 closestNeighbor = abscissae.size() - 1;
145 }
146 //general case: one is looking for the two consecutives entries that surround the input date
147 //then one choose the closest one
148 else {
149 int lowerBorder = latestClosestNeighbor;
150 int upperBorder = latestClosestNeighbor;
151
152 final int searchDirection = date.compareTo(abscissae.get(latestClosestNeighbor));
153 if (searchDirection > 0) {
154 upperBorder++;
155 while (date.compareTo(abscissae.get(upperBorder)) > 0) {
156 upperBorder++;
157 lowerBorder++;
158 }
159 }
160 else {
161 lowerBorder--;
162 while (date.compareTo(abscissae.get(lowerBorder)) < 0) {
163 upperBorder--;
164 lowerBorder--;
165 }
166 }
167
168 final double lowerDistance = FastMath.abs(date.durationFrom(abscissae.get(lowerBorder)));
169 final double upperDistance = FastMath.abs(date.durationFrom(abscissae.get(upperBorder)));
170
171 closestNeighbor = (lowerDistance < upperDistance) ? lowerBorder : upperBorder;
172 }
173
174 //The result is stored in order to speed up the next call to the function
175 //Indeed, it is highly likely that the requested result will be the same
176 this.latestClosestNeighbor = closestNeighbor;
177 return closestNeighbor;
178 }
179
180 /** Clear the recorded values from the interpolation grid.
181 */
182 public void clearHistory() {
183 abscissae.clear();
184 values.clear();
185 }
186
187 /** Add a point to the interpolation grid.
188 * @param date abscissa of the point
189 * @param value value of the element
190 */
191 public void addGridPoint(final AbsoluteDate date, final double[] value) {
192 //If the grid is empty, the value is directly added to both arrays
193 if (abscissae.isEmpty()) {
194 abscissae.add(date);
195 values.add(value);
196 }
197 //If the grid already contains this point, only its value is changed
198 else if (abscissae.contains(date)) {
199 values.set(abscissae.indexOf(date), value);
200 }
201 //If the grid does not contain this point, the position of the point
202 //in the grid is computed first
203 else {
204 final int closestNeighbor = getClosestNeighbor(date);
205 final int index = (date.compareTo(abscissae.get(closestNeighbor)) < 0) ? closestNeighbor : closestNeighbor + 1;
206 abscissae.add(index, date);
207 values.add(index, value);
208 }
209 }
210 }