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 }