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