1   /* Copyright 2002-2016 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  
18  package org.orekit.frames;
19  
20  import java.io.Serializable;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.orekit.errors.OrekitException;
25  import org.orekit.errors.OrekitExceptionWrapper;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.utils.AngularDerivativesFilter;
28  import org.orekit.utils.CartesianDerivativesFilter;
29  import org.orekit.utils.GenericTimeStampedCache;
30  import org.orekit.utils.TimeStampedGenerator;
31  
32  /** Transform provider using thread-safe interpolation on transforms sample.
33   * <p>
34   * The interpolation is a polynomial Hermite interpolation, which
35   * can either use or ignore the derivatives provided by the raw
36   * provider. This means that simple raw providers that do not compute
37   * derivatives can be used, the derivatives will be added appropriately
38   * by the interpolation process.
39   * </p>
40   * @see GenericTimeStampedCache
41   * @see ShiftingTransformProvider
42   * @author Luc Maisonobe
43   */
44  public class InterpolatingTransformProvider implements TransformProvider {
45  
46      /** Serializable UID. */
47      private static final long serialVersionUID = 20140723L;
48  
49      /** Provider for raw (non-interpolated) transforms. */
50      private final TransformProvider rawProvider;
51  
52      /** Filter for Cartesian derivatives to use in interpolation. */
53      private final CartesianDerivativesFilter cFilter;
54  
55      /** Filter for angular derivatives to use in interpolation. */
56      private final AngularDerivativesFilter aFilter;
57  
58      /** Earliest supported date. */
59      private final AbsoluteDate earliest;
60  
61      /** Latest supported date. */
62      private final AbsoluteDate latest;
63  
64      /** Grid points time step. */
65      private final double step;
66  
67      /** Cache for sample points. */
68      private final transient GenericTimeStampedCache<Transform> cache;
69  
70      /** Simple constructor.
71       * @param rawProvider provider for raw (non-interpolated) transforms
72       * @param cFilter filter for derivatives from the sample to use in interpolation
73       * @param aFilter filter for derivatives from the sample to use in interpolation
74       * @param earliest earliest supported date
75       * @param latest latest supported date
76       * @param gridPoints number of interpolation grid points
77       * @param step grid points time step
78       * @param maxSlots maximum number of independent cached time slots
79       * in the {@link GenericTimeStampedCache time-stamped cache}
80       * @param maxSpan maximum duration span in seconds of one slot
81       * in the {@link GenericTimeStampedCache time-stamped cache}
82       * @param newSlotInterval time interval above which a new slot is created
83       * in the {@link GenericTimeStampedCache time-stamped cache}
84       */
85      public InterpolatingTransformProvider(final TransformProvider rawProvider,
86                                            final CartesianDerivativesFilter cFilter,
87                                            final AngularDerivativesFilter aFilter,
88                                            final AbsoluteDate earliest, final AbsoluteDate latest,
89                                            final int gridPoints, final double step,
90                                            final int maxSlots, final double maxSpan, final double newSlotInterval) {
91          this.rawProvider = rawProvider;
92          this.cFilter     = cFilter;
93          this.aFilter     = aFilter;
94          this.earliest    = earliest;
95          this.latest      = latest;
96          this.step        = step;
97          this.cache       = new GenericTimeStampedCache<Transform>(gridPoints, maxSlots, maxSpan, newSlotInterval,
98                                                                    new Generator(), Transform.class);
99      }
100 
101     /** Get the underlying provider for raw (non-interpolated) transforms.
102      * @return provider for raw (non-interpolated) transforms
103      */
104     public TransformProvider getRawProvider() {
105         return rawProvider;
106     }
107 
108     /** Get the number of interpolation grid points.
109      * @return number of interpolation grid points
110      */
111     public int getGridPoints() {
112         return cache.getNeighborsSize();
113     }
114 
115     /** Get the grid points time step.
116      * @return grid points time step
117      */
118     public double getStep() {
119         return step;
120     }
121 
122     /** {@inheritDoc} */
123     public Transform getTransform(final AbsoluteDate date) throws OrekitException {
124         try {
125 
126             // retrieve a sample from the thread-safe cache
127             final List<Transform> sample = cache.getNeighbors(date);
128 
129             // interpolate to specified date
130             return Transform.interpolate(date, cFilter, aFilter, sample);
131 
132         } catch (OrekitExceptionWrapper oew) {
133             // something went wrong while generating the sample,
134             // we just forward the exception up
135             throw oew.getException();
136         }
137     }
138 
139     /** Replace the instance with a data transfer object for serialization.
140      * <p>
141      * This intermediate class serializes only the data needed for generation,
142      * but does <em>not</em> serializes the cache itself (in fact the cache is
143      * not serializable).
144      * </p>
145      * @return data transfer object that will be serialized
146      */
147     private Object writeReplace() {
148         return new DTO(rawProvider, cFilter.getMaxOrder(), aFilter.getMaxOrder(),
149                        earliest, latest, cache.getNeighborsSize(), step,
150                        cache.getMaxSlots(), cache.getMaxSpan(), cache.getNewSlotQuantumGap());
151     }
152 
153     /** Internal class used only for serialization. */
154     private static class DTO implements Serializable {
155 
156         /** Serializable UID. */
157         private static final long serialVersionUID = 20140723L;
158 
159         /** Provider for raw (non-interpolated) transforms. */
160         private final TransformProvider rawProvider;
161 
162         /** Cartesian derivatives to use in interpolation. */
163         private final int cDerivatives;
164 
165         /** Angular derivatives to use in interpolation. */
166         private final int aDerivatives;
167 
168         /** Earliest supported date. */
169         private final AbsoluteDate earliest;
170 
171         /** Latest supported date. */
172         private final AbsoluteDate latest;
173 
174         /** Number of grid points. */
175         private final int gridPoints;
176 
177         /** Grid points time step. */
178         private final double step;
179 
180         /** Maximum number of independent cached time slots. */
181         private final int maxSlots;
182 
183         /** Maximum duration span in seconds of one slot. */
184         private final double maxSpan;
185 
186         /** Time interval above which a new slot is created. */
187         private final double newSlotInterval;
188 
189         /** Simple constructor.
190          * @param rawProvider provider for raw (non-interpolated) transforms
191          * @param cDerivatives derivation order for Cartesian coordinates
192          * @param aDerivatives derivation order for angular coordinates
193          * @param earliest earliest supported date
194          * @param latest latest supported date
195          * @param gridPoints number of interpolation grid points
196          * @param step grid points time step
197          * @param maxSlots maximum number of independent cached time slots
198          * in the {@link GenericTimeStampedCache time-stamped cache}
199          * @param maxSpan maximum duration span in seconds of one slot
200          * in the {@link GenericTimeStampedCache time-stamped cache}
201          * @param newSlotInterval time interval above which a new slot is created
202          * in the {@link GenericTimeStampedCache time-stamped cache}
203          */
204         private DTO(final TransformProvider rawProvider, final int cDerivatives, final int aDerivatives,
205                     final AbsoluteDate earliest, final AbsoluteDate latest,
206                     final int gridPoints, final double step,
207                     final int maxSlots, final double maxSpan, final double newSlotInterval) {
208             this.rawProvider      = rawProvider;
209             this.cDerivatives     = cDerivatives;
210             this.aDerivatives     = aDerivatives;
211             this.earliest         = earliest;
212             this.latest           = latest;
213             this.gridPoints       = gridPoints;
214             this.step             = step;
215             this.maxSlots         = maxSlots;
216             this.maxSpan          = maxSpan;
217             this.newSlotInterval  = newSlotInterval;
218         }
219 
220         /** Replace the deserialized data transfer object with a {@link InterpolatingTransformProvider}.
221          * @return replacement {@link InterpolatingTransformProvider}
222          */
223         private Object readResolve() {
224             // build a new provider, with an empty cache
225             return new InterpolatingTransformProvider(rawProvider,
226                                                       CartesianDerivativesFilter.getFilter(cDerivatives),
227                                                       AngularDerivativesFilter.getFilter(aDerivatives),
228                                                       earliest, latest, gridPoints, step,
229                                                       maxSlots, maxSpan, newSlotInterval);
230         }
231 
232     }
233 
234     /** Local generator for thread-safe cache. */
235     private class Generator implements TimeStampedGenerator<Transform> {
236 
237         /** {@inheritDoc} */
238         public List<Transform> generate(final Transform existing, final AbsoluteDate date) {
239 
240             try {
241                 final List<Transform> generated = new ArrayList<Transform>();
242 
243                 if (existing == null) {
244 
245                     // no prior existing transforms, just generate a first set
246                     for (int i = 0; i < cache.getNeighborsSize(); ++i) {
247                         generated.add(rawProvider.getTransform(date.shiftedBy(i * step)));
248                     }
249 
250                 } else {
251 
252                     // some transforms have already been generated
253                     // add the missing ones up to specified date
254 
255                     AbsoluteDate t = existing.getDate();
256                     if (date.compareTo(t) > 0) {
257                         // forward generation
258                         do {
259                             t = t.shiftedBy(step);
260                             generated.add(generated.size(), rawProvider.getTransform(t));
261                         } while (t.compareTo(date) <= 0);
262                     } else {
263                         // backward generation
264                         do {
265                             t = t.shiftedBy(-step);
266                             generated.add(0, rawProvider.getTransform(t));
267                         } while (t.compareTo(date) >= 0);
268                     }
269                 }
270 
271                 // return the generated transforms
272                 return generated;
273             } catch (OrekitException oe) {
274                 throw new OrekitExceptionWrapper(oe);
275             }
276 
277         }
278 
279     }
280 
281 }