GravityFieldFactory.java

  1. /* Copyright 2002-2013 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.apache.commons.math3.util.FastMath;
  22. import org.apache.commons.math3.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.     /** Get the 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.      * @return a gravity field coefficients provider containing already loaded data
  353.      * @exception OrekitException if some data is missing
  354.      * or if some loader specific error occurs
  355.      * @deprecated as of 6.0, replaced by {@link #getUnnormalizedProvider(int, int)}
  356.      */
  357.     public static PotentialCoefficientsProvider getPotentialProvider()
  358.         throws OrekitException {
  359.         final PotentialCoefficientsReader reader = readGravityField(Integer.MAX_VALUE, Integer.MAX_VALUE);
  360.         final RawSphericalHarmonicsProvider provider = reader.getConstantProvider(false,
  361.                                                                                   reader.getMaxAvailableDegree(),
  362.                                                                                   reader.getMaxAvailableOrder());
  363.         return new ProviderConverter(new WrappingUnnormalizedProvider(provider));
  364.     }

  365.     /** Read a gravity field coefficients provider from the first supported file.
  366.      * <p>
  367.      * If no {@link PotentialCoefficientsReader} has been added by calling {@link
  368.      * #addPotentialCoefficientsReader(PotentialCoefficientsReader)
  369.      * addPotentialCoefficientsReader} or if {@link #clearPotentialCoefficientsReaders()
  370.      * clearPotentialCoefficientsReaders} has been called afterwards,the {@link
  371.      * #addDefaultPotentialCoefficientsReaders() addDefaultPotentialCoefficientsReaders}
  372.      * method will be called automatically.
  373.      * </p>
  374.      * @param maxParseDegree maximal degree to parse
  375.      * @param maxParseOrder maximal order to parse
  376.      * @return a reader containing already loaded data
  377.      * @exception OrekitException if some data is missing
  378.      * or if some loader specific error occurs
  379.      * @since 6.0
  380.      */
  381.     public static PotentialCoefficientsReader readGravityField(final int maxParseDegree,
  382.                                                                final int maxParseOrder)
  383.         throws OrekitException {

  384.         synchronized (READERS) {

  385.             if (READERS.isEmpty()) {
  386.                 addDefaultPotentialCoefficientsReaders();
  387.             }

  388.             // test the available readers
  389.             for (final PotentialCoefficientsReader reader : READERS) {
  390.                 reader.setMaxParseDegree(maxParseDegree);
  391.                 reader.setMaxParseOrder(maxParseOrder);
  392.                 DataProvidersManager.getInstance().feed(reader.getSupportedNames(), reader);
  393.                 if (!reader.stillAcceptsData()) {
  394.                     return reader;
  395.                 }
  396.             }
  397.         }

  398.         throw new OrekitException(OrekitMessages.NO_GRAVITY_FIELD_DATA_LOADED);

  399.     }

  400.     /** Get a un-normalization factors array.
  401.      * <p>
  402.      * Un-normalized coefficients are obtained by multiplying normalized
  403.      * coefficients by the factors array elements.
  404.      * </p>
  405.      * @param degree maximal degree
  406.      * @param order maximal order
  407.      * @return triangular un-normalization factors array
  408.      * @exception OrekitException if degree and order are too large
  409.      * and the latest coefficients underflow
  410.      * @since 6.0
  411.      */
  412.     public static double[][] getUnnormalizationFactors(final int degree, final int order)
  413.         throws OrekitException {

  414.         // allocate a triangular array
  415.         final double[][] factor = new double[degree + 1][];
  416.         factor[0] = new double[] {
  417.             1.0
  418.         };

  419.         // compute the factors
  420.         for (int n = 1; n <= degree; n++) {
  421.             final double[] row = new double[FastMath.min(n, order) + 1];
  422.             row[0] = FastMath.sqrt(2 * n + 1);
  423.             double coeff = 2.0 * (2 * n + 1);
  424.             for (int m = 1; m < row.length; m++) {
  425.                 coeff /= (n - m + 1) * (n + m);
  426.                 row[m] = FastMath.sqrt(coeff);
  427.                 if (row[m] < Precision.SAFE_MIN) {
  428.                     throw new OrekitException(OrekitMessages.GRAVITY_FIELD_NORMALIZATION_UNDERFLOW,
  429.                                               n, m);
  430.                 }
  431.             }
  432.             factor[n] = row;
  433.         }

  434.         return factor;

  435.     }

  436.     /** Get the ocean tides waves from the first supported file.
  437.      * <p>
  438.      * If no {@link OceanTidesReader} has been added by calling {@link
  439.      * #addOceanTidesReader(OceanTidesReader)
  440.      * addOceanTidesReader} or if {@link #clearOceanTidesReaders()
  441.      * clearOceanTidesReaders} has been called afterwards,the {@link
  442.      * #addDefaultOceanTidesReaders() addDefaultOceanTidesReaders}
  443.      * method will be called automatically.
  444.      * </p>
  445.      * <p><span style="color:red">
  446.      * WARNING: as of 2013-11-17, there seem to be an inconsistency when loading
  447.      * one or the other file, for wave Sa (Doodson number 56.554) and P1 (Doodson
  448.      * number 163.555). The sign of the coefficients are different. We think the
  449.      * problem lies in the input files from IERS and not in the conversion (which
  450.      * works for all other waves), but cannot be sure. For this reason, ocean
  451.      * tides are still considered experimental at this date.
  452.      * </span></p>
  453.      * @param degree maximal degree
  454.      * @param order maximal order
  455.      * @return list of tides waves containing already loaded data
  456.      * @exception OrekitException if some data can't be read (missing or read error)
  457.      * or if some loader specific error occurs
  458.      * @since 6.1
  459.      */
  460.     public static List<OceanTidesWave> getOceanTidesWaves(final int degree, final int order)
  461.         throws OrekitException {

  462.         synchronized (OCEAN_TIDES_READERS) {

  463.             if (OCEAN_TIDES_READERS.isEmpty()) {
  464.                 addDefaultOceanTidesReaders();
  465.             }

  466.             // test the available readers
  467.             for (final OceanTidesReader reader : OCEAN_TIDES_READERS) {
  468.                 reader.setMaxParseDegree(degree);
  469.                 reader.setMaxParseOrder(order);
  470.                 DataProvidersManager.getInstance().feed(reader.getSupportedNames(), reader);
  471.                 if (!reader.stillAcceptsData()) {
  472.                     return reader.getWaves();
  473.                 }
  474.             }
  475.         }

  476.         throw new OrekitException(OrekitMessages.NO_OCEAN_TIDE_DATA_LOADED);

  477.     }

  478. }