1 /* Copyright 2002-2022 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 package org.orekit.forces.gravity.potential;
18
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.List;
23 import java.util.stream.Stream;
24
25 import org.hipparchus.analysis.interpolation.HermiteInterpolator;
26 import org.orekit.errors.OrekitException;
27 import org.orekit.errors.TimeStampedCacheException;
28 import org.orekit.time.AbsoluteDate;
29 import org.orekit.time.TimeStamped;
30 import org.orekit.utils.GenericTimeStampedCache;
31 import org.orekit.utils.TimeStampedCache;
32 import org.orekit.utils.TimeStampedGenerator;
33
34 /** Caching wrapper for {@link NormalizedSphericalHarmonicsProvider}.
35 * <p>
36 * This wrapper improves efficiency of {@link NormalizedSphericalHarmonicsProvider}
37 * by sampling the values at a user defined rate and using interpolation
38 * between samples. This is important with providers that have sub-daily
39 * frequencies and are computing intensive, such as tides fields.
40 * </p>
41 * @see NormalizedSphericalHarmonicsProvider
42 * @see org.orekit.forces.gravity.SolidTides
43 * @see TimeStampedCache
44 * @author Luc Maisonobe
45 * @since 6.1
46 */
47 public class CachedNormalizedSphericalHarmonicsProvider implements NormalizedSphericalHarmonicsProvider {
48
49 /** Underlying raw provider. */
50 private final NormalizedSphericalHarmonicsProvider rawProvider;
51
52 /** Number of coefficients in C<sub>n, m</sub> and S<sub>n, m</sub> arrays (counted separately). */
53 private final int size;
54
55 /** Cache. */
56 private final TimeStampedCache<TimeStampedSphericalHarmonics> cache;
57
58 /** Simple constructor.
59 * @param rawProvider underlying raw provider
60 * @param step time step between sample points for interpolation
61 * @param nbPoints number of points to use for interpolation, must be at least 2
62 * @param maxSlots maximum number of independent cached time slots
63 * @param maxSpan maximum duration span in seconds of one slot
64 * (can be set to {@code Double.POSITIVE_INFINITY} if desired)
65 * @param newSlotInterval time interval above which a new slot is created
66 * instead of extending an existing one
67 */
68 public CachedNormalizedSphericalHarmonicsProvider(final NormalizedSphericalHarmonicsProvider rawProvider,
69 final double step, final int nbPoints,
70 final int maxSlots, final double maxSpan,
71 final double newSlotInterval) {
72
73 this.rawProvider = rawProvider;
74 final int k = rawProvider.getMaxDegree() + 1;
75 this.size = (k * (k + 1)) / 2;
76
77 cache = new GenericTimeStampedCache<TimeStampedSphericalHarmonics>(nbPoints, maxSlots, maxSpan,
78 newSlotInterval, new Generator(step));
79 }
80
81 /** {@inheritDoc} */
82 @Override
83 public int getMaxDegree() {
84 return rawProvider.getMaxDegree();
85 }
86
87 /** {@inheritDoc} */
88 @Override
89 public int getMaxOrder() {
90 return rawProvider.getMaxOrder();
91 }
92
93 /** {@inheritDoc} */
94 @Override
95 public double getMu() {
96 return rawProvider.getMu();
97 }
98
99 /** {@inheritDoc} */
100 @Override
101 public double getAe() {
102 return rawProvider.getAe();
103 }
104
105 /** {@inheritDoc} */
106 @Override
107 public AbsoluteDate getReferenceDate() {
108 return rawProvider.getReferenceDate();
109 }
110
111 /** {@inheritDoc} */
112 @Deprecated
113 @Override
114 public double getOffset(final AbsoluteDate date) {
115 return rawProvider.getOffset(date);
116 }
117
118 /** {@inheritDoc} */
119 @Override
120 public TideSystem getTideSystem() {
121 return rawProvider.getTideSystem();
122 }
123
124 /** {@inheritDoc} */
125 @Override
126 public NormalizedSphericalHarmonics onDate(final AbsoluteDate date) {
127 return TimeStampedSphericalHarmonics.interpolate(date, cache.getNeighbors(date));
128 }
129
130 /** Generator for time-stamped spherical harmonics. */
131 private class Generator implements TimeStampedGenerator<TimeStampedSphericalHarmonics> {
132
133 /** Time step between generated sets. */
134 private final double step;
135
136 /** Simple constructor.
137 * @param step time step between generated sets
138 */
139 Generator(final double step) {
140 this.step = step;
141 }
142
143 /** {@inheritDoc} */
144 @Override
145 public List<TimeStampedSphericalHarmonics> generate(final AbsoluteDate existingDate,
146 final AbsoluteDate date) {
147 try {
148
149 final List<TimeStampedSphericalHarmonics> generated =
150 new ArrayList<TimeStampedSphericalHarmonics>();
151 final double[] cnmsnm = new double[2 * size];
152
153 if (existingDate == null) {
154
155 // no prior existing transforms, just generate a first set
156 for (int i = 0; i < cache.getNeighborsSize(); ++i) {
157 final AbsoluteDate t = date.shiftedBy((i - cache.getNeighborsSize() / 2) * step);
158 fillArray(rawProvider.onDate(t), cnmsnm);
159 generated.add(new TimeStampedSphericalHarmonics(t, cnmsnm));
160 }
161
162 } else {
163
164 // some coefficients have already been generated
165 // add the missing ones up to specified date
166
167 AbsoluteDate t = existingDate;
168 if (date.compareTo(t) > 0) {
169 // forward generation
170 do {
171 t = t.shiftedBy(step);
172 fillArray(rawProvider.onDate(t), cnmsnm);
173 generated.add(new TimeStampedSphericalHarmonics(t, cnmsnm));
174 } while (t.compareTo(date) <= 0);
175 } else {
176 // backward generation
177 do {
178 t = t.shiftedBy(-step);
179 fillArray(rawProvider.onDate(t), cnmsnm);
180 generated.add(new TimeStampedSphericalHarmonics(t, cnmsnm));
181 } while (t.compareTo(date) >= 0);
182 // ensure forward chronological order
183 Collections.reverse(generated);
184 }
185
186 }
187
188 // return the generated sample
189 return generated;
190
191 } catch (OrekitException oe) {
192 throw new TimeStampedCacheException(oe);
193 }
194 }
195
196 /** Fill coefficients array for one entry.
197 * @param raw the un-interpolated spherical harmonics
198 * @param cnmsnm arrays to fill in
199 */
200 private void fillArray(final NormalizedSphericalHarmonics raw,
201 final double[] cnmsnm) {
202 int index = 0;
203 for (int n = 0; n <= rawProvider.getMaxDegree(); ++n) {
204 for (int m = 0; m <= n; ++m) {
205 cnmsnm[index++] = raw.getNormalizedCnm(n, m);
206 }
207 }
208 for (int n = 0; n <= rawProvider.getMaxDegree(); ++n) {
209 for (int m = 0; m <= n; ++m) {
210 cnmsnm[index++] = raw.getNormalizedSnm(n, m);
211 }
212 }
213 }
214
215 }
216
217 /**
218 * Internal class for time-stamped spherical harmonics. Instances are created using
219 * {@link #interpolate(AbsoluteDate, Collection)}
220 */
221 private static class TimeStampedSphericalHarmonics
222 implements TimeStamped, NormalizedSphericalHarmonics {
223
224 /** Current date. */
225 private final AbsoluteDate date;
226
227 /** number of C or S coefficients. */
228 private final int size;
229
230 /** Flattened array for C<sub>n,m</sub> and S<sub>n,m</sub> coefficients. */
231 private final double[] cnmsnm;
232
233 /** Simple constructor.
234 * @param date current date
235 * @param cnmsnm flattened array for C<sub>n,m</sub> and S<sub>n,m</sub>
236 * coefficients. It is copied.
237 */
238 private TimeStampedSphericalHarmonics(final AbsoluteDate date,
239 final double[] cnmsnm) {
240 this.date = date;
241 this.cnmsnm = cnmsnm.clone();
242 this.size = cnmsnm.length / 2;
243 }
244
245 /** {@inheritDoc} */
246 @Override
247 public AbsoluteDate getDate() {
248 return date;
249 }
250
251 /** {@inheritDoc} */
252 @Override
253 public double getNormalizedCnm(final int n, final int m) {
254 return cnmsnm[(n * (n + 1)) / 2 + m];
255 }
256
257 /** {@inheritDoc} */
258 @Override
259 public double getNormalizedSnm(final int n, final int m) {
260 return cnmsnm[(n * (n + 1)) / 2 + m + size];
261 }
262
263 /** Interpolate spherical harmonics.
264 * <p>
265 * The interpolated instance is created by polynomial Hermite interpolation.
266 * </p>
267 * @param date interpolation date
268 * @param sample sample points on which interpolation should be done
269 * @return a new time-stamped spherical harmonics, interpolated at specified date
270 */
271 public static TimeStampedSphericalHarmonics interpolate(final AbsoluteDate date,
272 final Stream<TimeStampedSphericalHarmonics> sample) {
273
274 // set up an interpolator taking derivatives into account
275 final HermiteInterpolator interpolator = new HermiteInterpolator();
276
277 // add sample points
278 sample.forEach(tssh -> interpolator.addSamplePoint(tssh.date.durationFrom(date), tssh.cnmsnm));
279
280 // build a new interpolated instance
281 return new TimeStampedSphericalHarmonics(date, interpolator.value(0.0));
282
283 }
284
285 }
286
287 }