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