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 RawSphericalHarmonicsProvider provider;
252         synchronized (readers) {
253             final PotentialCoefficientsReader reader = readGravityField(degree, order);
254             provider = reader.getProvider(true, degree, order);
255         }
256         final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(provider.getReferenceDate(), provider);
257         return new WrappingNormalizedProvider(frozen);
258     }
259 
260     /**
261      * {@inheritDoc}
262      *
263      * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
264      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
265      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
266      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
267      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
268      * method will be called automatically.
269      */
270     @Override
271     public NormalizedSphericalHarmonicsProvider getNormalizedProvider(final int degree,
272                                                                       final int order) {
273         final RawSphericalHarmonicsProvider provider;
274         synchronized (readers) {
275             final PotentialCoefficientsReader reader = readGravityField(degree, order);
276             provider = reader.getProvider(true, degree, order);
277         }
278         return new WrappingNormalizedProvider(provider);
279     }
280 
281     /**
282      * {@inheritDoc}
283      *
284      * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
285      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
286      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
287      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
288      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
289      * method will be called automatically.
290      */
291     @Override
292     public UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree,
293                                                                                   final int order) {
294         final RawSphericalHarmonicsProvider provider;
295         synchronized (readers) {
296             final PotentialCoefficientsReader reader = readGravityField(degree, order);
297             provider = reader.getProvider(false, degree, order);
298         }
299         final ConstantSphericalHarmonics frozen = new ConstantSphericalHarmonics(provider.getReferenceDate(), provider);
300         return new WrappingUnnormalizedProvider(frozen);
301     }
302 
303     /**
304      * {@inheritDoc}
305      *
306      * <p>If no {@link PotentialCoefficientsReader} has been added by calling {@link
307      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
308      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
309      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
310      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
311      * method will be called automatically.
312      */
313     @Override
314     public UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final int degree,
315                                                                           final int order) {
316         final RawSphericalHarmonicsProvider provider;
317         synchronized (readers) {
318             final PotentialCoefficientsReader reader = readGravityField(degree, order);
319             provider = reader.getProvider(false, degree, order);
320         }
321         return new WrappingUnnormalizedProvider(provider);
322     }
323 
324     /**
325      * {@inheritDoc}
326      *
327      * <p>If no {@link OceanTidesReader} has been added by calling {@link
328      * #addOceanTidesReader(OceanTidesReader)
329      * addOceanTidesReader} or if {@link #clearOceanTidesReaders()
330      * clearOceanTidesReaders} has been called afterwards, the {@link
331      * #addDefaultOceanTidesReaders() addDefaultOceanTidesReaders}
332      * method will be called automatically.
333      */
334     @Override
335     public List<OceanTidesWave> getOceanTidesWaves(final int degree, final int order) {
336 
337         synchronized (oceanTidesReaders) {
338 
339             if (oceanTidesReaders.isEmpty()) {
340                 addDefaultOceanTidesReaders();
341             }
342 
343             // test the available readers
344             for (final OceanTidesReader reader : oceanTidesReaders) {
345                 reader.setMaxParseDegree(degree);
346                 reader.setMaxParseOrder(order);
347                 dataProvidersManager.feed(reader.getSupportedNames(), reader);
348                 if (!reader.stillAcceptsData()) {
349                     return reader.getWaves();
350                 }
351             }
352         }
353 
354         throw new OrekitException(OrekitMessages.NO_OCEAN_TIDE_DATA_LOADED);
355 
356     }
357 
358 }