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 }