1 /* Contributed in the public domain.
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.List;
21 import java.util.Map;
22
23 import org.hipparchus.util.FastMath;
24 import org.orekit.data.DataProvidersManager;
25 import org.orekit.errors.OrekitException;
26 import org.orekit.errors.OrekitMessages;
27 import org.orekit.time.AbsoluteDate;
28 import org.orekit.time.TimeScale;
29
30 /**
31 * Loads gravity fields when first requested and can be configured until then. Designed to
32 * match the behavior of {@link GravityFieldFactory} in Orekit 10.0.
33 *
34 * @author Evan Ward
35 * @author Fabien Maussion
36 * @author Pascal Parraud
37 * @author Luc Maisonobe
38 * @see GravityFieldFactory
39 * @since 10.1
40 */
41 public class LazyLoadedGravityFields implements GravityFields {
42
43 /** Potential readers. */
44 private final List<PotentialCoefficientsReader> readers = new ArrayList<>();
45
46 /** Ocean tides readers. */
47 private final List<OceanTidesReader> oceanTidesReaders =
48 new ArrayList<>();
49
50 /** Ocean load deformation coefficients. */
51 private OceanLoadDeformationCoefficients oceanLoadDeformationCoefficients =
52 OceanLoadDeformationCoefficients.IERS_2010;
53
54 /** Provides access to auxiliary data files for loading gravity field files. */
55 private final DataProvidersManager dataProvidersManager;
56
57 /** Time scale for parsing dates. */
58 private final TimeScale timeScale;
59
60 /**
61 * Create a factory for gravity fields that uses the given data manager to load the
62 * gravity field files.
63 *
64 * @param dataProvidersManager provides access to auxiliary data files.
65 * @param timeScale use to parse dates for the {@link #addDefaultPotentialCoefficientsReaders()}.
66 * In Orekit 10.0 it is TT.
67 */
68 public LazyLoadedGravityFields(final DataProvidersManager dataProvidersManager,
69 final TimeScale timeScale) {
70 this.dataProvidersManager = dataProvidersManager;
71 this.timeScale = timeScale;
72 }
73
74 /** Add a reader for gravity fields.
75 * @param reader custom reader to add for the gravity field
76 * @see #addDefaultPotentialCoefficientsReaders()
77 * @see #clearPotentialCoefficientsReaders()
78 */
79 public void addPotentialCoefficientsReader(final PotentialCoefficientsReader reader) {
80 synchronized (readers) {
81 readers.add(reader);
82 }
83 }
84
85 /**
86 * Add the default readers for gravity fields.
87 *
88 * <p> The default readers support ICGEM, SHM, EGM and GRGS formats with the
89 * default names {@link GravityFieldFactory#ICGEM_FILENAME}, {@link
90 * GravityFieldFactory#SHM_FILENAME}, {@link GravityFieldFactory#EGM_FILENAME}, {@link
91 * GravityFieldFactory#GRGS_FILENAME} and don't allow missing coefficients.
92 *
93 * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
94 * @see #clearPotentialCoefficientsReaders()
95 */
96 public void addDefaultPotentialCoefficientsReaders() {
97 synchronized (readers) {
98 readers.add(new ICGEMFormatReader(GravityFieldFactory.ICGEM_FILENAME, false, timeScale));
99 readers.add(new SHMFormatReader(GravityFieldFactory.SHM_FILENAME, false, timeScale));
100 readers.add(new EGMFormatReader(GravityFieldFactory.EGM_FILENAME, false));
101 readers.add(new GRGSFormatReader(GravityFieldFactory.GRGS_FILENAME, false, timeScale));
102 }
103 }
104
105 /** Clear gravity field readers.
106 * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
107 * @see #addDefaultPotentialCoefficientsReaders()
108 */
109 public void clearPotentialCoefficientsReaders() {
110 synchronized (readers) {
111 readers.clear();
112 }
113 }
114
115 /** Add a reader for ocean tides.
116 * @param reader custom reader to add for the gravity field
117 * @see #addDefaultPotentialCoefficientsReaders()
118 * @see #clearPotentialCoefficientsReaders()
119 */
120 public void addOceanTidesReader(final OceanTidesReader reader) {
121 synchronized (oceanTidesReaders) {
122 oceanTidesReaders.add(reader);
123 }
124 }
125
126 /** Configure ocean load deformation coefficients.
127 * @param oldc ocean load deformation coefficients
128 * @see #getOceanLoadDeformationCoefficients()
129 */
130 public void configureOceanLoadDeformationCoefficients(final OceanLoadDeformationCoefficients oldc) {
131 oceanLoadDeformationCoefficients = oldc;
132 }
133
134 /** Get the configured ocean load deformation coefficients.
135 * <p>
136 * If {@link #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
137 * configureOceanLoadDeformationCoefficients} has never been called, the default
138 * value will be the {@link OceanLoadDeformationCoefficients#IERS_2010 IERS 2010}
139 * coefficients.
140 * </p>
141 * @return ocean load deformation coefficients
142 * @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
143 */
144 public OceanLoadDeformationCoefficients getOceanLoadDeformationCoefficients() {
145 return oceanLoadDeformationCoefficients;
146 }
147
148 /** Add the default readers for ocean tides.
149 * <p>
150 * The default readers support files similar to the fes2004_Cnm-Snm.dat and
151 * fes2004.dat as published by IERS, using the {@link
152 * #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
153 * configured} ocean load deformation coefficients, which by default are the
154 * IERS 2010 coefficients, which are limited to degree 6. If higher degree
155 * coefficients are needed, the {@link
156 * #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
157 * configureOceanLoadDeformationCoefficients} method can be called prior to
158 * loading the ocean tides model with the {@link
159 * OceanLoadDeformationCoefficients#GEGOUT high degree coefficients} computed
160 * by Pascal Gégout.
161 * </p>
162 * <p>
163 * WARNING: the files referenced in the published conventions have some errors.
164 * These errors have been corrected and the updated files can be found here:
165 * <a href="http://tai.bipm.org/iers/convupdt/convupdt_c6.html">
166 * http://tai.bipm.org/iers/convupdt/convupdt_c6.html</a>.
167 * </p>
168 * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
169 * @see #clearPotentialCoefficientsReaders()
170 * @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
171 * @see #getOceanLoadDeformationCoefficients()
172 */
173 public void addDefaultOceanTidesReaders() {
174 synchronized (oceanTidesReaders) {
175
176 oceanTidesReaders.add(new FESCnmSnmReader(GravityFieldFactory.FES_CNM_SNM_FILENAME, 1.0e-11));
177
178 final AstronomicalAmplitudeReader aaReader =
179 new AstronomicalAmplitudeReader(GravityFieldFactory.FES_HF_FILENAME, 5, 2, 3, 1.0);
180 dataProvidersManager.feed(aaReader.getSupportedNames(), aaReader);
181 final Map<Integer, Double> map = aaReader.getAstronomicalAmplitudesMap();
182 oceanTidesReaders.add(new FESCHatEpsilonReader(GravityFieldFactory.FES_CHAT_EPSILON_FILENAME,
183 0.01, FastMath.toRadians(1.0),
184 getOceanLoadDeformationCoefficients(),
185 map));
186
187
188 }
189 }
190
191 /** Clear ocean tides readers.
192 * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
193 * @see #addDefaultPotentialCoefficientsReaders()
194 */
195 public void clearOceanTidesReaders() {
196 synchronized (oceanTidesReaders) {
197 oceanTidesReaders.clear();
198 }
199 }
200
201 /** Read a gravity field coefficients provider from the first supported file.
202 * <p>
203 * If no {@link PotentialCoefficientsReader} has been added by calling {@link
204 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
205 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
206 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
207 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
208 * method will be called automatically.
209 * </p>
210 * @param maxParseDegree maximal degree to parse
211 * @param maxParseOrder maximal order to parse
212 * @return a reader containing already loaded data
213 * @since 6.0
214 */
215 public PotentialCoefficientsReader readGravityField(final int maxParseDegree,
216 final int maxParseOrder) {
217
218 synchronized (readers) {
219
220 if (readers.isEmpty()) {
221 addDefaultPotentialCoefficientsReaders();
222 }
223
224 // test the available readers
225 for (final PotentialCoefficientsReader reader : readers) {
226 reader.setMaxParseDegree(maxParseDegree);
227 reader.setMaxParseOrder(maxParseOrder);
228 dataProvidersManager.feed(reader.getSupportedNames(), reader);
229 if (!reader.stillAcceptsData()) {
230 return reader;
231 }
232 }
233 }
234
235 throw new OrekitException(OrekitMessages.NO_GRAVITY_FIELD_DATA_LOADED);
236
237 }
238
239 /**
240 * {@inheritDoc}
241 *
242 * <p> If no {@link PotentialCoefficientsReader} has been added by calling {@link
243 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
244 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
245 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
246 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
247 * method will be called automatically.
248 */
249 @Override
250 public NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree, final int order,
251 final AbsoluteDate freezingDate) {
252 final RawSphericalHarmonicsProvider provider;
253 synchronized (readers) {
254 final PotentialCoefficientsReader reader = readGravityField(degree, order);
255 provider = reader.getProvider(true, degree, order);
256 }
257 final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(freezingDate, provider);
258 return new WrappingNormalizedProvider(frozen);
259 }
260
261 /**
262 * {@inheritDoc}
263 *
264 * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
265 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
266 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
267 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
268 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
269 * method will be called automatically.
270 */
271 @Override
272 public NormalizedSphericalHarmonicsProvider getNormalizedProvider(final int degree,
273 final int order) {
274 final RawSphericalHarmonicsProvider provider;
275 synchronized (readers) {
276 final PotentialCoefficientsReader reader = readGravityField(degree, order);
277 provider = reader.getProvider(true, degree, order);
278 }
279 return new WrappingNormalizedProvider(provider);
280 }
281
282 /**
283 * {@inheritDoc}
284 *
285 * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
286 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
287 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
288 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
289 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
290 * method will be called automatically.
291 */
292 @Override
293 public UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree, final int order,
294 final AbsoluteDate freezingDate) {
295 final RawSphericalHarmonicsProvider provider;
296 synchronized (readers) {
297 final PotentialCoefficientsReader reader = readGravityField(degree, order);
298 provider = reader.getProvider(false, degree, order);
299 }
300 final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(freezingDate, provider);
301 return new WrappingUnnormalizedProvider(frozen);
302 }
303
304 /**
305 * {@inheritDoc}
306 *
307 * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
308 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
309 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
310 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
311 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
312 * method will be called automatically.
313 */
314 @Override
315 public UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final int degree,
316 final int order) {
317 final RawSphericalHarmonicsProvider provider;
318 synchronized (readers) {
319 final PotentialCoefficientsReader reader = readGravityField(degree, order);
320 provider = reader.getProvider(false, degree, order);
321 }
322 return new WrappingUnnormalizedProvider(provider);
323 }
324
325 /**
326 * {@inheritDoc}
327 *
328 * <p>If no {@link OceanTidesReader} has been added by calling {@link
329 * #addOceanTidesReader(OceanTidesReader)
330 * addOceanTidesReader} or if {@link #clearOceanTidesReaders()
331 * clearOceanTidesReaders} has been called afterwards, the {@link
332 * #addDefaultOceanTidesReaders() addDefaultOceanTidesReaders}
333 * method will be called automatically.
334 */
335 @Override
336 public List<OceanTidesWave> getOceanTidesWaves(final int degree, final int order) {
337
338 synchronized (oceanTidesReaders) {
339
340 if (oceanTidesReaders.isEmpty()) {
341 addDefaultOceanTidesReaders();
342 }
343
344 // test the available readers
345 for (final OceanTidesReader reader : oceanTidesReaders) {
346 reader.setMaxParseDegree(degree);
347 reader.setMaxParseOrder(order);
348 dataProvidersManager.feed(reader.getSupportedNames(), reader);
349 if (!reader.stillAcceptsData()) {
350 return reader.getWaves();
351 }
352 }
353 }
354
355 throw new OrekitException(OrekitMessages.NO_OCEAN_TIDE_DATA_LOADED);
356
357 }
358
359 }