GravityFieldFactory.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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. import java.util.ArrayList;
  19. import java.util.List;
  20. import java.util.Map;

  21. import org.hipparchus.util.FastMath;
  22. import org.hipparchus.util.Precision;
  23. import org.orekit.data.DataProvidersManager;
  24. import org.orekit.errors.OrekitException;
  25. import org.orekit.errors.OrekitMessages;

  26. /** Factory used to read gravity field files in several supported formats.
  27.  * @author Fabien Maussion
  28.  * @author Pascal Parraud
  29.  * @author Luc Maisonobe
  30.  */
  31. public class GravityFieldFactory {

  32.     /** Default regular expression for ICGEM files. */
  33.     public static final String ICGEM_FILENAME = "^(.*\\.gfc)|(g(\\d)+_eigen[-_](\\w)+_coef)$";

  34.     /** Default regular expression for SHM files. */
  35.     public static final String SHM_FILENAME = "^eigen[-_](\\w)+_coef$";

  36.     /** Default regular expression for EGM files. */
  37.     public static final String EGM_FILENAME = "^egm\\d\\d_to\\d.*$";

  38.     /** Default regular expression for GRGS files. */
  39.     public static final String GRGS_FILENAME = "^grim\\d_.*$";

  40.     /** Default regular expression for FES Cnm, Snm tides files. */
  41.     public static final String FES_CNM_SNM_FILENAME = "^fes(\\d)+_Cnm-Snm.dat$";

  42.     /** Default regular expression for FES C hat and epsilon tides files. */
  43.     public static final String FES_CHAT_EPSILON_FILENAME = "^fes(\\d)+.dat$";

  44.     /** Default regular expression for FES Hf tides files. */
  45.     public static final String FES_HF_FILENAME = "^hf-fes(\\d)+.dat$";

  46.     /** Potential readers. */
  47.     private static final List<PotentialCoefficientsReader> READERS =
  48.         new ArrayList<PotentialCoefficientsReader>();

  49.     /** Ocean tides readers. */
  50.     private static final List<OceanTidesReader> OCEAN_TIDES_READERS =
  51.         new ArrayList<OceanTidesReader>();

  52.     /** Ocean load deformation coefficients. */
  53.     private static OceanLoadDeformationCoefficients OCEAN_LOAD_DEFORMATION_COEFFICIENTS =
  54.         OceanLoadDeformationCoefficients.IERS_2010;

  55.     /** Private constructor.
  56.      * <p>This class is a utility class, it should neither have a public
  57.      * nor a default constructor. This private constructor prevents
  58.      * the compiler from generating one automatically.</p>
  59.      */
  60.     private GravityFieldFactory() {
  61.     }

  62.     /** Add a reader for gravity fields.
  63.      * @param reader custom reader to add for the gravity field
  64.      * @see #addDefaultPotentialCoefficientsReaders()
  65.      * @see #clearPotentialCoefficientsReaders()
  66.      */
  67.     public static void addPotentialCoefficientsReader(final PotentialCoefficientsReader reader) {
  68.         synchronized (READERS) {
  69.             READERS.add(reader);
  70.         }
  71.     }

  72.     /** Add the default readers for gravity fields.
  73.      * <p>
  74.      * The default READERS supports ICGEM, SHM, EGM and GRGS formats with the
  75.      * default names {@link #ICGEM_FILENAME}, {@link #SHM_FILENAME}, {@link
  76.      * #EGM_FILENAME}, {@link #GRGS_FILENAME} and don't allow missing coefficients.
  77.      * </p>
  78.      * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  79.      * @see #clearPotentialCoefficientsReaders()
  80.      */
  81.     public static void addDefaultPotentialCoefficientsReaders() {
  82.         synchronized (READERS) {
  83.             READERS.add(new ICGEMFormatReader(ICGEM_FILENAME, false));
  84.             READERS.add(new SHMFormatReader(SHM_FILENAME, false));
  85.             READERS.add(new EGMFormatReader(EGM_FILENAME, false));
  86.             READERS.add(new GRGSFormatReader(GRGS_FILENAME, false));
  87.         }
  88.     }

  89.     /** Clear gravity field readers.
  90.      * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  91.      * @see #addDefaultPotentialCoefficientsReaders()
  92.      */
  93.     public static void clearPotentialCoefficientsReaders() {
  94.         synchronized (READERS) {
  95.             READERS.clear();
  96.         }
  97.     }

  98.     /** Add a reader for ocean tides.
  99.      * @param reader custom reader to add for the gravity field
  100.      * @see #addDefaultPotentialCoefficientsReaders()
  101.      * @see #clearPotentialCoefficientsReaders()
  102.      */
  103.     public static void addOceanTidesReader(final OceanTidesReader reader) {
  104.         synchronized (OCEAN_TIDES_READERS) {
  105.             OCEAN_TIDES_READERS.add(reader);
  106.         }
  107.     }

  108.     /** Configure ocean load deformation coefficients.
  109.      * @param oldc ocean load deformation coefficients
  110.      * @see #getOceanLoadDeformationCoefficients()
  111.      */
  112.     public static void configureOceanLoadDeformationCoefficients(final OceanLoadDeformationCoefficients oldc) {
  113.         OCEAN_LOAD_DEFORMATION_COEFFICIENTS = oldc;
  114.     }

  115.     /** Get the configured ocean load deformation coefficients.
  116.      * <p>
  117.      * If {@link #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  118.      * configureOceanLoadDeformationCoefficients} has never been called, the default
  119.      * value will be the {@link OceanLoadDeformationCoefficients#IERS_2010 IERS 2010}
  120.      * coefficients.
  121.      * </p>
  122.      * @return ocean load deformation coefficients
  123.      * @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  124.      */
  125.     public static OceanLoadDeformationCoefficients getOceanLoadDeformationCoefficients() {
  126.         return OCEAN_LOAD_DEFORMATION_COEFFICIENTS;
  127.     }

  128.     /** Add the default READERS for ocean tides.
  129.      * <p>
  130.      * The default READERS supports files similar to the fes2004_Cnm-Snm.dat and
  131.      * fes2004.dat as published by IERS, using the {@link
  132.      * #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  133.      * configured} ocean load deformation coefficients, which by default are the
  134.      * IERS 2010 coefficients, which are limited to degree 6. If higher degree
  135.      * coefficients are needed, the {@link
  136.      * #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  137.      * configureOceanLoadDeformationCoefficients} method can be called prior to
  138.      * loading the ocean tides model with the {@link
  139.      * OceanLoadDeformationCoefficients#GEGOUT high degree coefficients} computed
  140.      * by Pascal Gégout.
  141.      * </p>
  142.      * <p>
  143.      * WARNING: the files referenced in the published conventions have some errors.
  144.      * These errors have been corrected and the updated files can be found here:
  145.      * <a href="http://tai.bipm.org/iers/convupdt/convupdt_c6.html">
  146.      * http://tai.bipm.org/iers/convupdt/convupdt_c6.html</a>.
  147.      * </p>
  148.      * @exception OrekitException if astronomical amplitudes cannot be read
  149.      * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  150.      * @see #clearPotentialCoefficientsReaders()
  151.      * @see #configureOceanLoadDeformationCoefficients(OceanLoadDeformationCoefficients)
  152.      * @see #getOceanLoadDeformationCoefficients()
  153.      */
  154.     public static void addDefaultOceanTidesReaders()
  155.         throws OrekitException {
  156.         synchronized (OCEAN_TIDES_READERS) {

  157.             OCEAN_TIDES_READERS.add(new FESCnmSnmReader(FES_CNM_SNM_FILENAME, 1.0e-11));

  158.             final AstronomicalAmplitudeReader aaReader =
  159.                     new AstronomicalAmplitudeReader(FES_HF_FILENAME, 5, 2, 3, 1.0);
  160.             DataProvidersManager.getInstance().feed(aaReader.getSupportedNames(), aaReader);
  161.             final Map<Integer, Double> map = aaReader.getAstronomicalAmplitudesMap();
  162.             OCEAN_TIDES_READERS.add(new FESCHatEpsilonReader(FES_CHAT_EPSILON_FILENAME,
  163.                                                              0.01, FastMath.toRadians(1.0),
  164.                                                              getOceanLoadDeformationCoefficients(),
  165.                                                              map));


  166.         }
  167.     }

  168.     /** Clear ocean tides readers.
  169.      * @see #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  170.      * @see #addDefaultPotentialCoefficientsReaders()
  171.      */
  172.     public static void clearOceanTidesReaders() {
  173.         synchronized (OCEAN_TIDES_READERS) {
  174.             OCEAN_TIDES_READERS.clear();
  175.         }
  176.     }

  177.     /** Get the constant gravity field coefficients provider from the first supported file.
  178.      * <p>
  179.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  180.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  181.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  182.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  183.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  184.      * method will be called automatically.
  185.      * </p>
  186.      * @param degree maximal degree
  187.      * @param order maximal order
  188.      * @return a gravity field coefficients provider containing already loaded data
  189.      * @exception OrekitException if some data can't be read (missing or read error)
  190.      * or if some loader specific error occurs
  191.      * @since 6.0
  192.      * @see #getNormalizedProvider(int, int)
  193.      */
  194.     public static NormalizedSphericalHarmonicsProvider getConstantNormalizedProvider(final int degree,
  195.                                                                                      final int order)
  196.         throws OrekitException {
  197.         final PotentialCoefficientsReader reader = readGravityField(degree, order);
  198.         final RawSphericalHarmonicsProvider provider = reader.getConstantProvider(true, degree, order);
  199.         return new WrappingNormalizedProvider(provider);
  200.     }

  201.     /** Get the 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 degree maximal degree
  211.      * @param order maximal order
  212.      * @return a gravity field coefficients provider containing already loaded data
  213.      * @exception OrekitException if some data can't be read (missing or read error)
  214.      * or if some loader specific error occurs
  215.      * @since 6.0
  216.      * @see #getConstantNormalizedProvider(int, int)
  217.      */
  218.     public static NormalizedSphericalHarmonicsProvider getNormalizedProvider(final int degree,
  219.                                                                              final int order)
  220.         throws OrekitException {
  221.         final PotentialCoefficientsReader reader = readGravityField(degree, order);
  222.         final RawSphericalHarmonicsProvider provider = reader.getProvider(true, degree, order);
  223.         return new WrappingNormalizedProvider(provider);
  224.     }

  225.     /** Create a time-independent {@link NormalizedSphericalHarmonicsProvider} from canonical coefficients.
  226.      * <p>
  227.      * Note that contrary to the other factory method, this one does not read any data, it simply uses
  228.      * the provided data
  229.      * </p>
  230.      * @param ae central body reference radius
  231.      * @param mu central body attraction coefficient
  232.      * @param tideSystem tide system
  233.      * @param normalizedC normalized tesseral-sectorial coefficients (cosine part)
  234.      * @param normalizedS normalized tesseral-sectorial coefficients (sine part)
  235.      * @return provider for normalized coefficients
  236.      * @since 6.0
  237.      */
  238.     public static NormalizedSphericalHarmonicsProvider getNormalizedProvider(final double ae, final double mu,
  239.                                                                              final TideSystem tideSystem,
  240.                                                                              final double[][] normalizedC,
  241.                                                                              final double[][] normalizedS) {
  242.         return new WrappingNormalizedProvider(new ConstantSphericalHarmonics(ae, mu, tideSystem,
  243.                                                                              normalizedC, normalizedS));
  244.     }

  245.     /** Create a {@link NormalizedSphericalHarmonicsProvider} from an {@link UnnormalizedSphericalHarmonicsProvider}.
  246.      * <p>
  247.      * Note that contrary to the other factory method, this one does not read any data, it simply uses
  248.      * the provided data.
  249.      * </p>
  250.      * @param unnormalized provider to normalize
  251.      * @return provider for normalized coefficients
  252.      * @exception OrekitException if degree and order are too large
  253.      * and the normalization coefficients underflow
  254.      * @since 6.0
  255.      */
  256.     public static NormalizedSphericalHarmonicsProvider getNormalizedProvider(final UnnormalizedSphericalHarmonicsProvider unnormalized)
  257.         throws OrekitException {
  258.         return new Normalizer(unnormalized);
  259.     }

  260.     /** Get the constant gravity field coefficients provider from the first supported file.
  261.      * <p>
  262.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  263.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  264.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  265.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  266.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  267.      * method will be called automatically.
  268.      * </p>
  269.      * @param degree maximal degree
  270.      * @param order maximal order
  271.      * @return a gravity field coefficients provider containing already loaded data
  272.      * @exception OrekitException if some data can't be read (missing or read error)
  273.      * or if some loader specific error occurs
  274.      * @since 6.0
  275.      * @see #getUnnormalizedProvider(int, int)
  276.      */
  277.     public static UnnormalizedSphericalHarmonicsProvider getConstantUnnormalizedProvider(final int degree,
  278.                                                                                          final int order)
  279.         throws OrekitException {
  280.         final PotentialCoefficientsReader reader = readGravityField(degree, order);
  281.         final RawSphericalHarmonicsProvider provider = reader.getConstantProvider(false, degree, order);
  282.         return new WrappingUnnormalizedProvider(provider);
  283.     }

  284.     /** Get the gravity field coefficients provider from the first supported file.
  285.      * <p>
  286.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  287.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  288.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  289.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  290.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  291.      * method will be called automatically.
  292.      * </p>
  293.      * @param degree maximal degree
  294.      * @param order maximal order
  295.      * @return a gravity field coefficients provider containing already loaded data
  296.      * @exception OrekitException if some data can't be read (missing or read error)
  297.      * or if some loader specific error occurs
  298.      * @since 6.0
  299.      * @see #getConstantUnnormalizedProvider(int, int)
  300.      */
  301.     public static UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final int degree,
  302.                                                                                  final int order)
  303.         throws OrekitException {
  304.         final PotentialCoefficientsReader reader = readGravityField(degree, order);
  305.         final RawSphericalHarmonicsProvider provider = reader.getProvider(false, degree, order);
  306.         return new WrappingUnnormalizedProvider(provider);
  307.     }

  308.     /** Create a time-independent {@link UnnormalizedSphericalHarmonicsProvider} from canonical coefficients.
  309.      * <p>
  310.      * Note that contrary to the other factory method, this one does not read any data, it simply uses
  311.      * the provided data
  312.      * </p>
  313.      * @param ae central body reference radius
  314.      * @param mu central body attraction coefficient
  315.      * @param tideSystem tide system
  316.      * @param unnormalizedC un-normalized tesseral-sectorial coefficients (cosine part)
  317.      * @param unnormalizedS un-normalized tesseral-sectorial coefficients (sine part)
  318.      * @return provider for un-normalized coefficients
  319.      * @since 6.0
  320.      */
  321.     public static UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final double ae, final double mu,
  322.                                                                                  final TideSystem tideSystem,
  323.                                                                                  final double[][] unnormalizedC,
  324.                                                                                  final double[][] unnormalizedS) {
  325.         return new WrappingUnnormalizedProvider(new ConstantSphericalHarmonics(ae, mu, tideSystem,
  326.                                                                                unnormalizedC, unnormalizedS));
  327.     }

  328.     /** Create an {@link UnnormalizedSphericalHarmonicsProvider} from a {@link NormalizedSphericalHarmonicsProvider}.
  329.      * <p>
  330.      * Note that contrary to the other factory method, this one does not read any data, it simply uses
  331.      * the provided data.
  332.      * </p>
  333.      * @param normalized provider to un-normalize
  334.      * @return provider for un-normalized coefficients
  335.      * @exception OrekitException if degree and order are too large
  336.      * and the un-normalization coefficients underflow
  337.      * @since 6.0
  338.      */
  339.     public static UnnormalizedSphericalHarmonicsProvider getUnnormalizedProvider(final NormalizedSphericalHarmonicsProvider normalized)
  340.         throws OrekitException {
  341.         return new Unnormalizer(normalized);
  342.     }

  343.     /** Read a gravity field coefficients provider from the first supported file.
  344.      * <p>
  345.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  346.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  347.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  348.      * clearPotentialCoefficientsReaders} has been called afterwards, the {@link
  349.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  350.      * method will be called automatically.
  351.      * </p>
  352.      * @param maxParseDegree maximal degree to parse
  353.      * @param maxParseOrder maximal order to parse
  354.      * @return a reader containing already loaded data
  355.      * @exception OrekitException if some data is missing
  356.      * or if some loader specific error occurs
  357.      * @since 6.0
  358.      */
  359.     public static PotentialCoefficientsReader readGravityField(final int maxParseDegree,
  360.                                                                final int maxParseOrder)
  361.         throws OrekitException {

  362.         synchronized (READERS) {

  363.             if (READERS.isEmpty()) {
  364.                 addDefaultPotentialCoefficientsReaders();
  365.             }

  366.             // test the available readers
  367.             for (final PotentialCoefficientsReader reader : READERS) {
  368.                 reader.setMaxParseDegree(maxParseDegree);
  369.                 reader.setMaxParseOrder(maxParseOrder);
  370.                 DataProvidersManager.getInstance().feed(reader.getSupportedNames(), reader);
  371.                 if (!reader.stillAcceptsData()) {
  372.                     return reader;
  373.                 }
  374.             }
  375.         }

  376.         throw new OrekitException(OrekitMessages.NO_GRAVITY_FIELD_DATA_LOADED);

  377.     }

  378.     /** Get a un-normalization factors array.
  379.      * <p>
  380.      * Un-normalized coefficients are obtained by multiplying normalized
  381.      * coefficients by the factors array elements.
  382.      * </p>
  383.      * @param degree maximal degree
  384.      * @param order maximal order
  385.      * @return triangular un-normalization factors array
  386.      * @exception OrekitException if degree and order are too large
  387.      * and the latest coefficients underflow
  388.      * @since 6.0
  389.      */
  390.     public static double[][] getUnnormalizationFactors(final int degree, final int order)
  391.         throws OrekitException {

  392.         // allocate a triangular array
  393.         final int rows = degree + 1;
  394.         final double[][] factor = new double[rows][];
  395.         factor[0] = new double[] {
  396.             1.0
  397.         };

  398.         // compute the factors
  399.         for (int n = 1; n <= degree; n++) {
  400.             final double[] row = new double[FastMath.min(n, order) + 1];
  401.             row[0] = FastMath.sqrt(2 * n + 1);
  402.             double coeff = 2.0 * (2 * n + 1);
  403.             for (int m = 1; m < row.length; m++) {
  404.                 coeff /= (n - m + 1) * (n + m);
  405.                 row[m] = FastMath.sqrt(coeff);
  406.                 if (row[m] < Precision.SAFE_MIN) {
  407.                     throw new OrekitException(OrekitMessages.GRAVITY_FIELD_NORMALIZATION_UNDERFLOW,
  408.                                               n, m);
  409.                 }
  410.             }
  411.             factor[n] = row;
  412.         }

  413.         return factor;

  414.     }

  415.     /** Get the ocean tides waves from the first supported file.
  416.      * <p>
  417.      * If no {@link OceanTidesReader} has been added by calling {@link
  418.      * #addOceanTidesReader(OceanTidesReader)
  419.      * addOceanTidesReader} or if {@link #clearOceanTidesReaders()
  420.      * clearOceanTidesReaders} has been called afterwards, the {@link
  421.      * #addDefaultOceanTidesReaders() addDefaultOceanTidesReaders}
  422.      * method will be called automatically.
  423.      * </p>
  424.      * <p><span style="color:red">
  425.      * WARNING: as of 2013-11-17, there seem to be an inconsistency when loading
  426.      * one or the other file, for wave Sa (Doodson number 56.554) and P1 (Doodson
  427.      * number 163.555). The sign of the coefficients are different. We think the
  428.      * problem lies in the input files from IERS and not in the conversion (which
  429.      * works for all other waves), but cannot be sure. For this reason, ocean
  430.      * tides are still considered experimental at this date.
  431.      * </span></p>
  432.      * @param degree maximal degree
  433.      * @param order maximal order
  434.      * @return list of tides waves containing already loaded data
  435.      * @exception OrekitException if some data can't be read (missing or read error)
  436.      * or if some loader specific error occurs
  437.      * @since 6.1
  438.      */
  439.     public static List<OceanTidesWave> getOceanTidesWaves(final int degree, final int order)
  440.         throws OrekitException {

  441.         synchronized (OCEAN_TIDES_READERS) {

  442.             if (OCEAN_TIDES_READERS.isEmpty()) {
  443.                 addDefaultOceanTidesReaders();
  444.             }

  445.             // test the available readers
  446.             for (final OceanTidesReader reader : OCEAN_TIDES_READERS) {
  447.                 reader.setMaxParseDegree(degree);
  448.                 reader.setMaxParseOrder(order);
  449.                 DataProvidersManager.getInstance().feed(reader.getSupportedNames(), reader);
  450.                 if (!reader.stillAcceptsData()) {
  451.                     return reader.getWaves();
  452.                 }
  453.             }
  454.         }

  455.         throw new OrekitException(OrekitMessages.NO_OCEAN_TIDE_DATA_LOADED);

  456.     }

  457. }