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.Map;
23  
24  import org.hipparchus.Field;
25  import org.hipparchus.RealFieldElement;
26  import org.hipparchus.util.FastMath;
27  import org.orekit.errors.OrekitException;
28  import org.orekit.errors.OrekitExceptionWrapper;
29  import org.orekit.time.AbsoluteDate;
30  import org.orekit.time.FieldAbsoluteDate;
31  import org.orekit.utils.AngularDerivativesFilter;
32  import org.orekit.utils.CartesianDerivativesFilter;
33  import org.orekit.utils.GenericTimeStampedCache;
34  
35  /** Transform provider using thread-safe shifts on transforms sample.
36   * <p>
37   * The shifts take derivatives into account, up to user specified order.
38   * </p>
39   * @see GenericTimeStampedCache
40   * @see InterpolatingTransformProvider
41   * @since 7.1
42   * @author Luc Maisonobe
43   */
44  public class ShiftingTransformProvider implements TransformProvider {
45  
46      /** Serializable UID. */
47      private static final long serialVersionUID = 20150601L;
48  
49      /** First level cache. */
50      private final InterpolatingTransformProvider interpolatingProvider;
51  
52      /** Cache for sample points. */
53      private final transient GenericTimeStampedCache<Transform> cache;
54  
55      /** Field caches for sample points. */
56      // we use Object as the value of fieldCaches because despite numerous attempts,
57      // we could not find a way to use GenericTimeStampedCache<FieldTransform<? extends RealFieldElement<?>>
58      // without the compiler complaining
59      private final transient Map<Field<? extends RealFieldElement<?>>, Object> fieldCaches;
60  
61      /** Simple constructor.
62       * @param rawProvider provider for raw (non-interpolated) transforms
63       * @param cFilter filter for derivatives from the sample to use in interpolation
64       * @param aFilter filter for derivatives from the sample to use in interpolation
65       * @param earliest earliest supported date
66       * @param latest latest supported date
67       * @param gridPoints number of interpolation grid points
68       * @param step grid points time step
69       * @param maxSlots maximum number of independent cached time slots
70       * in the {@link GenericTimeStampedCache time-stamped cache}
71       * @param maxSpan maximum duration span in seconds of one slot
72       * in the {@link GenericTimeStampedCache time-stamped cache}
73       * @param newSlotInterval time interval above which a new slot is created
74       * in the {@link GenericTimeStampedCache time-stamped cache}
75       */
76      public ShiftingTransformProvider(final TransformProvider rawProvider,
77                                       final CartesianDerivativesFilter cFilter,
78                                       final AngularDerivativesFilter aFilter,
79                                       final AbsoluteDate earliest, final AbsoluteDate latest,
80                                       final int gridPoints, final double step,
81                                       final int maxSlots, final double maxSpan, final double newSlotInterval) {
82          this(new InterpolatingTransformProvider(rawProvider, cFilter, aFilter,
83                                                  earliest, latest, gridPoints, step,
84                                                  maxSlots, maxSpan, newSlotInterval),
85               maxSlots, maxSpan, newSlotInterval);
86      }
87  
88      /** Simple constructor.
89       * @param interpolatingProvider first level cache provider
90       * @param maxSlots maximum number of independent cached time slots
91       * in the {@link GenericTimeStampedCache time-stamped cache}
92       * @param maxSpan maximum duration span in seconds of one slot
93       * in the {@link GenericTimeStampedCache time-stamped cache}
94       * @param newSlotInterval time interval above which a new slot is created
95       * in the {@link GenericTimeStampedCache time-stamped cache}
96       */
97      private ShiftingTransformProvider(final InterpolatingTransformProvider interpolatingProvider,
98                                       final int maxSlots, final double maxSpan, final double newSlotInterval) {
99          this.interpolatingProvider = interpolatingProvider;
100         this.cache = new GenericTimeStampedCache<Transform>(2, maxSlots, maxSpan, newSlotInterval,
101                                                             new TransformGenerator(2,
102                                                                                    interpolatingProvider,
103                                                                                    interpolatingProvider.getStep()));
104         this.fieldCaches = new HashMap<>();
105     }
106 
107     /** Get the underlying provider for raw (non-interpolated) transforms.
108      * @return provider for raw (non-interpolated) transforms
109      */
110     public TransformProvider getRawProvider() {
111         return interpolatingProvider.getRawProvider();
112     }
113 
114     /** Get the number of interpolation grid points.
115      * @return number of interpolation grid points
116      */
117     public int getGridPoints() {
118         return interpolatingProvider.getGridPoints();
119     }
120 
121     /** Get the grid points time step.
122      * @return grid points time step
123      */
124     public double getStep() {
125         return interpolatingProvider.getStep();
126     }
127 
128     /** {@inheritDoc} */
129     public Transform getTransform(final AbsoluteDate date) throws OrekitException {
130         try {
131 
132             // retrieve a sample from the thread-safe cache
133             final Transform closest = cache.getNeighbors(date).reduce((t0, t1) ->
134                 FastMath.abs(date.durationFrom(t0.getDate())) < FastMath.abs(date.durationFrom(t1.getDate())) ? t0 : t1
135             ).get();
136             return closest.shiftedBy(date.durationFrom(closest.getDate()));
137 
138         } catch (OrekitExceptionWrapper oew) {
139             // something went wrong while generating the sample,
140             // we just forward the exception up
141             throw oew.getException();
142         }
143     }
144 
145     /** {@inheritDoc} */
146     public <T extends RealFieldElement<T>> FieldTransform<T> getTransform(final FieldAbsoluteDate<T> date)
147         throws OrekitException {
148         try {
149 
150             @SuppressWarnings("unchecked")
151             GenericTimeStampedCache<FieldTransform<T>> fieldCache =
152                 (GenericTimeStampedCache<FieldTransform<T>>) fieldCaches.get(date.getField());
153             if (fieldCache == null) {
154                 fieldCache =
155                     new GenericTimeStampedCache<FieldTransform<T>>(cache.getNeighborsSize(),
156                                                                    cache.getMaxSlots(),
157                                                                    cache.getMaxSpan(),
158                                                                    cache.getNewSlotQuantumGap(),
159                                                                    new FieldTransformGenerator<>(date.getField(),
160                                                                                                  cache.getNeighborsSize(),
161                                                                                                  interpolatingProvider,
162                                                                                                  interpolatingProvider.getStep()));
163                 fieldCaches.put(date.getField(), fieldCache);
164             }
165 
166             // retrieve a sample from the thread-safe cache
167             final FieldTransform<T> closest = fieldCache.getNeighbors(date.toAbsoluteDate()).reduce((t0, t1) ->
168                 date.durationFrom(t0.getDate()).abs().getReal() < date.durationFrom(t1.getDate()).abs().getReal() ?
169                 t0 : t1
170             ).get();
171             return closest.shiftedBy(date.durationFrom(closest.getDate()));
172 
173         } catch (OrekitExceptionWrapper oew) {
174             // something went wrong while generating the sample,
175             // we just forward the exception up
176             throw oew.getException();
177         }
178     }
179 
180     /** Replace the instance with a data transfer object for serialization.
181      * <p>
182      * This intermediate class serializes only the data needed for generation,
183      * but does <em>not</em> serializes the cache itself (in fact the cache is
184      * not serializable).
185      * </p>
186      * @return data transfer object that will be serialized
187      */
188     private Object writeReplace() {
189         return new DTO(interpolatingProvider,
190                        cache.getMaxSlots(), cache.getMaxSpan(), cache.getNewSlotQuantumGap());
191     }
192 
193     /** Internal class used only for serialization. */
194     private static class DTO implements Serializable {
195 
196         /** Serializable UID. */
197         private static final long serialVersionUID = 20150601L;
198 
199         /** Provider for raw (non-interpolated) transforms. */
200         private final InterpolatingTransformProvider interpolatingProvider;
201 
202         /** Maximum number of independent cached time slots. */
203         private final int maxSlots;
204 
205         /** Maximum duration span in seconds of one slot. */
206         private final double maxSpan;
207 
208         /** Time interval above which a new slot is created. */
209         private final double newSlotInterval;
210 
211         /** Simple constructor.
212          * @param interpolatingProvider first level cache provider
213          * @param maxSlots maximum number of independent cached time slots
214          * in the {@link GenericTimeStampedCache time-stamped cache}
215          * @param maxSpan maximum duration span in seconds of one slot
216          * in the {@link GenericTimeStampedCache time-stamped cache}
217          * @param newSlotInterval time interval above which a new slot is created
218          * in the {@link GenericTimeStampedCache time-stamped cache}
219          */
220         private DTO(final InterpolatingTransformProvider interpolatingProvider,
221                     final int maxSlots, final double maxSpan, final double newSlotInterval) {
222             this.interpolatingProvider = interpolatingProvider;
223             this.maxSlots              = maxSlots;
224             this.maxSpan               = maxSpan;
225             this.newSlotInterval       = newSlotInterval;
226         }
227 
228         /** Replace the deserialized data transfer object with a {@link ShiftingTransformProvider}.
229          * @return replacement {@link ShiftingTransformProvider}
230          */
231         private Object readResolve() {
232             // build a new provider, with an empty cache
233             return new ShiftingTransformProvider(interpolatingProvider,
234                                                  maxSlots, maxSpan, newSlotInterval);
235         }
236 
237     }
238 
239 }