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