1 /* Copyright 2002-2015 CS Systèmes d'Information
2 * Licensed to CS Systèmes d'Information (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 java.util.ArrayList;
20
21 import org.apache.commons.math3.analysis.interpolation.HermiteInterpolator;
22 import org.apache.commons.math3.util.FastMath;
23 import org.orekit.time.AbsoluteDate;
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<AbsoluteDate>();
57 this.values = new ArrayList<Double>();
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 final double abscissa = abscissae.get(i).durationFrom(date);
73 final double value = values.get(i);
74
75 interpolator.addSamplePoint(abscissa, new double[]{value});
76 }
77
78 //interpolation
79 return interpolator.value(0.0)[0];
80 }
81
82 /**Find the closest available points from the specified date.
83 * @param date date of interest
84 * @return indices corresponding to the closest points on the time scale
85 */
86 private int[] getNeighborsIndices(final AbsoluteDate date) {
87 final int sizeofNeighborhood = FastMath.min(interpolationPoints, abscissae.size());
88 final int[] neighborsIndices = new int[sizeofNeighborhood];
89
90 //If the size of the complete sample is less than
91 //the desired number of interpolation points,
92 //then the entire sample is considered as the neighborhood
93 if (interpolationPoints >= abscissae.size()) {
94 for (int i = 0; i < sizeofNeighborhood; i++) {
95 neighborsIndices[i] = i;
96 }
97 }
98
99 //Else the neighborsIndices array is completed step-by-step,
100 //starting with its closest neighbor
101 else {
102 final int closestNeighbor = getClosestNeighbor(date);
103
104 neighborsIndices[0] = closestNeighbor;
105
106 int i = 1;
107 int lowerNeighbor = closestNeighbor - 1;
108 int upperNeighbor = closestNeighbor + 1;
109
110 while (i < interpolationPoints) {
111 if (lowerNeighbor < 0) { //This means that we have reached the earliest date
112 neighborsIndices[i] = upperNeighbor;
113 upperNeighbor++;
114 }
115 else if (upperNeighbor >= abscissae.size()) { //This means that we have reached the latest date
116 neighborsIndices[i] = lowerNeighbor;
117 lowerNeighbor--;
118 }
119 else { //the choice is made between the two next neighbors
120 final double lowerNeighborDistance = FastMath.abs(abscissae.get(lowerNeighbor).durationFrom(date));
121 final double upperNeighborDistance = FastMath.abs(abscissae.get(upperNeighbor).durationFrom(date));
122
123 if (lowerNeighborDistance <= upperNeighborDistance) {
124 neighborsIndices[i] = lowerNeighbor;
125 lowerNeighbor--;
126 }
127 else {
128 neighborsIndices[i] = upperNeighbor;
129 upperNeighbor++;
130 }
131 }
132
133 i++;
134 }
135 }
136
137 return neighborsIndices;
138 }
139
140 /**Find the closest point from a specific date amongst the available points.
141 * @param date date of interest
142 * @return index of the closest abscissa from the date of interest
143 */
144 private int getClosestNeighbor(final AbsoluteDate date) {
145 //the starting point is the latest result of a call to this method.
146 //Indeed, as this class is meant to be called during an integration process
147 //with an input date evolving often continuously in time, there is a high
148 //probability that the result will be the same as for last call of
149 //this method.
150 int closestNeighbor = latestClosestNeighbor;
151
152 //case where the date is before the available points
153 if (date.compareTo(abscissae.get(0)) <= 0) {
154 closestNeighbor = 0;
155 }
156 //case where the date is after the available points
157 else if (date.compareTo(abscissae.get(abscissae.size() - 1)) >= 0) {
158 closestNeighbor = abscissae.size() - 1;
159 }
160 //general case: one is looking for the two consecutives entries that surround the input date
161 //then one choose the closest one
162 else {
163 int lowerBorder = latestClosestNeighbor;
164 int upperBorder = latestClosestNeighbor;
165
166 final int searchDirection = date.compareTo(abscissae.get(latestClosestNeighbor));
167 if (searchDirection > 0) {
168 upperBorder++;
169 while (date.compareTo(abscissae.get(upperBorder)) > 0) {
170 upperBorder++;
171 lowerBorder++;
172 }
173 }
174 else {
175 lowerBorder--;
176 while (date.compareTo(abscissae.get(lowerBorder)) < 0) {
177 upperBorder--;
178 lowerBorder--;
179 }
180 }
181
182 final double lowerDistance = FastMath.abs(date.durationFrom(abscissae.get(lowerBorder)));
183 final double upperDistance = FastMath.abs(date.durationFrom(abscissae.get(upperBorder)));
184
185 closestNeighbor = (lowerDistance < upperDistance) ? lowerBorder : upperBorder;
186 }
187
188 //The result is stored in order to speed up the next call to the function
189 //Indeed, it is highly likely that the requested result will be the same
190 this.latestClosestNeighbor = closestNeighbor;
191 return closestNeighbor;
192 }
193
194 /** Clear the recorded values from the interpolation grid.
195 */
196 public void clearHistory() {
197 abscissae.clear();
198 values.clear();
199 }
200
201 /** Add a point to the interpolation grid.
202 * @param date abscissa of the point
203 * @param value value of the element
204 */
205 public void addGridPoint(final AbsoluteDate date, final double value) {
206 //If the grid is empty, the value is directly added to both arrays
207 if (abscissae.isEmpty()) {
208 abscissae.add(date);
209 values.add(value);
210 }
211 //If the grid already contains this point, only its value is changed
212 else if (abscissae.contains(date)) {
213 values.set(abscissae.indexOf(date), value);
214 }
215 //If the grid does not contain this point, the position of the point
216 //in the grid is computed first
217 else {
218 final int closestNeighbor = getClosestNeighbor(date);
219 final int index = (date.compareTo(abscissae.get(closestNeighbor)) < 0) ? closestNeighbor : closestNeighbor + 1;
220 abscissae.add(index, date);
221 values.add(index, value);
222 }
223 }
224 }