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 }