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