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, GRGS and SHA formats with the
89 * default names {@link GravityFieldFactory#ICGEM_FILENAME}, {@link
90 * GravityFieldFactory#SHM_FILENAME}, {@link GravityFieldFactory#EGM_FILENAME}, {@link
91 * GravityFieldFactory#GRGS_FILENAME}, {@link GravityFieldFactory#SHA_FILENAME}
92 * and don't allow missing coefficients.
93 *
94 * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
95 * @see #clearPotentialCoefficientsReaders()
96 */
97 public void addDefaultPotentialCoefficientsReaders() {
98 synchronized (readers) {
99 readers.add(new ICGEMFormatReader(GravityFieldFactory.ICGEM_FILENAME, false, timeScale));
100 readers.add(new SHMFormatReader(GravityFieldFactory.SHM_FILENAME, false, timeScale));
101 readers.add(new EGMFormatReader(GravityFieldFactory.EGM_FILENAME, false));
102 readers.add(new GRGSFormatReader(GravityFieldFactory.GRGS_FILENAME, false, timeScale));
103 readers.add(new SHAFormatReader(GravityFieldFactory.SHA_FILENAME, false));
104 }
105 }
106
107 /** Clear gravity field readers.
108 * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
109 * @see #addDefaultPotentialCoefficientsReaders()
110 */
111 public void clearPotentialCoefficientsReaders() {
112 synchronized (readers) {
113 readers.clear();
114 }
115 }
116
117 /** Add a reader for ocean tides.
118 * @param reader custom reader to add for the gravity field
119 * @see #addDefaultPotentialCoefficientsReaders()
120 * @see #clearPotentialCoefficientsReaders()
121 */
122 public void addOceanTidesReader(final OceanTidesReader reader) {
123 synchronized (oceanTidesReaders) {
124 oceanTidesReaders.add(reader);
125 }
126 }
127
128 /** Configure ocean load deformation coefficients.
129 * @param oldc ocean load deformation coefficients
130 * @see #getOceanLoadDeformationCoefficients()
131 */
132 public void configureOceanLoadDeformationCoefficients(final OceanLoadDeformationCoefficients oldc) {
133 oceanLoadDeformationCoefficients = oldc;
134 }
135
136 /** Get the configured ocean load deformation coefficients.
137 * <p>
138 * If {@link #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
139 * configureOceanLoadDeformationCoefficients} has never been called, the default
140 * value will be the {@link OceanLoadDeformationCoefficients#IERS_2010 IERS 2010}
141 * coefficients.
142 * </p>
143 * @return ocean load deformation coefficients
144 * @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
145 */
146 public OceanLoadDeformationCoefficients getOceanLoadDeformationCoefficients() {
147 return oceanLoadDeformationCoefficients;
148 }
149
150 /** Add the default readers for ocean tides.
151 * <p>
152 * The default readers support files similar to the fes2004_Cnm-Snm.dat and
153 * fes2004.dat as published by IERS, using the {@link
154 * #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
155 * configured} ocean load deformation coefficients, which by default are the
156 * IERS 2010 coefficients, which are limited to degree 6. If higher degree
157 * coefficients are needed, the {@link
158 * #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
159 * configureOceanLoadDeformationCoefficients} method can be called prior to
160 * loading the ocean tides model with the {@link
161 * OceanLoadDeformationCoefficients#GEGOUT high degree coefficients} computed
162 * by Pascal Gégout.
163 * </p>
164 * <p>
165 * WARNING: the files referenced in the published conventions have some errors.
166 * These errors have been corrected and the updated files can be found here:
167 * <a href="http://tai.bipm.org/iers/convupdt/convupdt_c6.html">
168 * http://tai.bipm.org/iers/convupdt/convupdt_c6.html</a>.
169 * </p>
170 * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
171 * @see #clearPotentialCoefficientsReaders()
172 * @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
173 * @see #getOceanLoadDeformationCoefficients()
174 */
175 public void addDefaultOceanTidesReaders() {
176 synchronized (oceanTidesReaders) {
177
178 oceanTidesReaders.add(new FESCnmSnmReader(GravityFieldFactory.FES_CNM_SNM_FILENAME, 1.0e-11));
179
180 final AstronomicalAmplitudeReader aaReader =
181 new AstronomicalAmplitudeReader(GravityFieldFactory.FES_HF_FILENAME, 5, 2, 3, 1.0);
182 dataProvidersManager.feed(aaReader.getSupportedNames(), aaReader);
183 final Map<Integer, Double> map = aaReader.getAstronomicalAmplitudesMap();
184 oceanTidesReaders.add(new FESCHatEpsilonReader(GravityFieldFactory.FES_CHAT_EPSILON_FILENAME,
185 0.01, FastMath.toRadians(1.0),
186 getOceanLoadDeformationCoefficients(),
187 map));
188
189
190 }
191 }
192
193 /** Clear ocean tides readers.
194 * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
195 * @see #addDefaultPotentialCoefficientsReaders()
196 */
197 public void clearOceanTidesReaders() {
198 synchronized (oceanTidesReaders) {
199 oceanTidesReaders.clear();
200 }
201 }
202
203 /** Read a gravity field coefficients provider from the first supported file.
204 * <p>
205 * If no {@link PotentialCoefficientsReader} has been added by calling {@link
206 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
207 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
208 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
209 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
210 * method will be called automatically.
211 * </p>
212 * @param maxParseDegree maximal degree to parse
213 * @param maxParseOrder maximal order to parse
214 * @return a reader containing already loaded data
215 * @since 6.0
216 */
217 public PotentialCoefficientsReader readGravityField(final int maxParseDegree,
218 final int maxParseOrder) {
219
220 synchronized (readers) {
221
222 if (readers.isEmpty()) {
223 addDefaultPotentialCoefficientsReaders();
224 }
225
226 // test the available readers
227 for (final PotentialCoefficientsReader reader : readers) {
228 reader.setMaxParseDegree(maxParseDegree);
229 reader.setMaxParseOrder(maxParseOrder);
230 dataProvidersManager.feed(reader.getSupportedNames(), reader);
231 if (!reader.stillAcceptsData()) {
232 return reader;
233 }
234 }
235 }
236
237 throw new OrekitException(OrekitMessages.NO_GRAVITY_FIELD_DATA_LOADED);
238
239 }
240
241 /**
242 * {@inheritDoc}
243 *
244 * <p> If no {@link PotentialCoefficientsReader} has been added by calling {@link
245 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
246 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
247 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
248 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
249 * method will be called automatically.
250 */
251 @Override
252 public NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree, final int order,
253 final AbsoluteDate freezingDate) {
254 final RawSphericalHarmonicsProvider provider;
255 synchronized (readers) {
256 final PotentialCoefficientsReader reader = readGravityField(degree, order);
257 provider = reader.getProvider(true, degree, order);
258 }
259 final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(freezingDate, provider);
260 return new WrappingNormalizedProvider(frozen);
261 }
262
263 /**
264 * {@inheritDoc}
265 *
266 * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
267 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
268 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
269 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
270 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
271 * method will be called automatically.
272 */
273 @Override
274 public NormalizedSphericalHarmonicsProvider getNormalizedProvider(final int degree,
275 final int order) {
276 final RawSphericalHarmonicsProvider provider;
277 synchronized (readers) {
278 final PotentialCoefficientsReader reader = readGravityField(degree, order);
279 provider = reader.getProvider(true, degree, order);
280 }
281 return new WrappingNormalizedProvider(provider);
282 }
283
284 /**
285 * {@inheritDoc}
286 *
287 * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
288 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
289 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
290 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
291 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
292 * method will be called automatically.
293 */
294 @Override
295 public UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree, final int order,
296 final AbsoluteDate freezingDate) {
297 final RawSphericalHarmonicsProvider provider;
298 synchronized (readers) {
299 final PotentialCoefficientsReader reader = readGravityField(degree, order);
300 provider = reader.getProvider(false, degree, order);
301 }
302 final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(freezingDate, provider);
303 return new WrappingUnnormalizedProvider(frozen);
304 }
305
306 /**
307 * {@inheritDoc}
308 *
309 * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
310 * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
311 * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
312 * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
313 * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
314 * method will be called automatically.
315 */
316 @Override
317 public UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final int degree,
318 final int order) {
319 final RawSphericalHarmonicsProvider provider;
320 synchronized (readers) {
321 final PotentialCoefficientsReader reader = readGravityField(degree, order);
322 provider = reader.getProvider(false, degree, order);
323 }
324 return new WrappingUnnormalizedProvider(provider);
325 }
326
327 /**
328 * {@inheritDoc}
329 *
330 * <p>If no {@link OceanTidesReader} has been added by calling {@link
331 * #addOceanTidesReader(OceanTidesReader)
332 * addOceanTidesReader} or if {@link #clearOceanTidesReaders()
333 * clearOceanTidesReaders} has been called afterwards, the {@link
334 * #addDefaultOceanTidesReaders() addDefaultOceanTidesReaders}
335 * method will be called automatically.
336 */
337 @Override
338 public List<OceanTidesWave> getOceanTidesWaves(final int degree, final int order) {
339
340 synchronized (oceanTidesReaders) {
341
342 if (oceanTidesReaders.isEmpty()) {
343 addDefaultOceanTidesReaders();
344 }
345
346 // test the available readers
347 for (final OceanTidesReader reader : oceanTidesReaders) {
348 reader.setMaxParseDegree(degree);
349 reader.setMaxParseOrder(order);
350 dataProvidersManager.feed(reader.getSupportedNames(), reader);
351 if (!reader.stillAcceptsData()) {
352 return reader.getWaves();
353 }
354 }
355 }
356
357 throw new OrekitException(OrekitMessages.NO_OCEAN_TIDE_DATA_LOADED);
358
359 }
360
361 }