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