FramesFactory.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.frames;

  18. import java.io.Serializable;
  19. import java.util.ArrayList;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Map;
  23. import java.util.SortedSet;
  24. import java.util.TreeSet;

  25. import org.hipparchus.RealFieldElement;
  26. import org.orekit.bodies.CelestialBodyFactory;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitInternalError;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.ChronologicalComparator;
  31. import org.orekit.time.FieldAbsoluteDate;
  32. import org.orekit.utils.AngularDerivativesFilter;
  33. import org.orekit.utils.CartesianDerivativesFilter;
  34. import org.orekit.utils.Constants;
  35. import org.orekit.utils.IERSConventions;
  36. import org.orekit.utils.OrekitConfiguration;


  37. /** Factory for predefined reference frames.
  38.  *
  39.  * <h1> FramesFactory Presentation </h1>
  40.  * <p>
  41.  * Several predefined reference {@link Frame frames} are implemented in OREKIT.
  42.  * They are linked together in a tree with the <i>Geocentric
  43.  * Celestial Reference Frame</i> (GCRF) as the root of the tree.
  44.  * This factory is designed to:
  45.  * </p>
  46.  * <ul>
  47.  *   <li>build the frames tree consistently,</li>
  48.  *   <li>avoid rebuilding some frames that may be costly to recreate all the time,</li>
  49.  *   <li>set up interpolation/caching features for some frames that may induce costly computation</li>
  50.  *   <li>streamline the {@link EOPHistory Earth Orientation Parameters} history loading.</li>
  51.  * </ul>
  52.  * <h2> Reference Frames </h2>
  53.  * <p>
  54.  * The user can retrieve those reference frames using various static methods, the most
  55.  * important ones being: {@link #getFrame(Predefined)}, {@link #getGCRF()},
  56.  * {@link #getCIRF(IERSConventions, boolean)} {@link #getTIRF(IERSConventions, boolean)},
  57.  * {@link #getITRF(IERSConventions, boolean)}, {@link #getITRF(ITRFVersion, IERSConventions, boolean)},
  58.  * {@link #getEME2000()}, {@link #getMOD(IERSConventions)}, {@link #getTOD(IERSConventions, boolean)},
  59.  * {@link #getGTOD(IERSConventions, boolean)}, {@link #getITRFEquinox(IERSConventions, boolean)},
  60.  * {@link #getTEME()} and {@link #getVeis1950()}.
  61.  * </p>
  62.  * <h2> International Terrestrial Reference Frame</h2>
  63.  * <p>
  64.  * This frame is the current (as of 2013) reference realization of
  65.  * the International Terrestrial Reference System produced by IERS.
  66.  * It is described in <a href="ftp://tai.bipm.org/iers/conv2010/tn36.pdf">
  67.  * IERS conventions (2010)</a>. It replaces the Earth Centered Earth Fixed
  68.  * frame which is the reference frame for GPS satellites.
  69.  * </p>
  70.  * <p>
  71.  * This frame is used to define position on solid Earth. It rotates with
  72.  * the Earth and includes the pole motion with respect to Earth crust as
  73.  * provided by IERS {@link EOPHistory Earth Orientation Parameters}.
  74.  * Its pole axis is the IERS Reference Pole (IRP).
  75.  * </p>
  76.  * <p>
  77.  * Depending on the  {@link EOPHistory Earth Orientation Parameters} source,
  78.  * different ITRS realization may be returned by {@link #getITRF(IERSConventions, boolean)},
  79.  * and if EOP are mixed, the ITRF may even jump from one realization to another one.
  80.  * This is not a problem for most users as different ITRS realizations are very close
  81.  * to each other (a few millimeters at Earth surface). If however a specific ITRF version
  82.  * (i.e. an ITRS realization) is needed for very high accuracy, Orekit provides the
  83.  * {@link FramesFactory#getITRF(ITRFVersion, IERSConventions, boolean)} method
  84.  * to get it and take care of jumps in EOP.
  85.  * </p>
  86.  * <p>
  87.  * ITRF can be built using the new non-rotating origin paradigm
  88.  * mandated by IAU 2000 resolution B1.8 and any supported {@link IERSConventions
  89.  * IERS conventions} (even IERS 1996 can be used with non-rotating origin paradigm,
  90.  * despite the resolution was not yet adopted at conventions publication time).
  91.  * </p>
  92.  * <p>
  93.  * ITRF can also be built using the classical equinox paradigm used prior to IAU 2000
  94.  * resolution B1.8 and any supported {@link IERSConventions IERS conventions} (even
  95.  * IERS 2003 and 2010 can be used with equinox paradigm, despite the resolution is
  96.  * in effect now). The choice of paradigm (non-rotating origin or equinox) and the
  97.  * choice of IERS conventions (i.e. the choice of precession/nutation models) can
  98.  * be made independently by user, Orekit provides all alternatives.
  99.  * </p>
  100.  * <h2>Intermediate frames</h2>
  101.  * <p>
  102.  * Orekit also provides all the intermediate frames that are needed to transform
  103.  * between GCRF and ITRF, along the two paths: ITRF/TIRF/CIRF/GCRF for the
  104.  * non-rotating origin paradigm and ITRF/GTOD/TOD/MOD/EME2000/GCRF for the equinox
  105.  * paradigm.
  106.  * </p>
  107.  * <h2> Earth Orientation Parameters </h2>
  108.  * <p>
  109.  * This factory also handles loading of Earth Orientation Parameters (EOP) needed
  110.  * for accurate transformations between inertial and Earth fixed frames, using
  111.  * {@link org.orekit.data.DataProvidersManager} features. EOP are IERS conventions
  112.  * dependent, because they correspond to correction to the precession/nutation
  113.  * models. When EOP should be applied, but EOP data are not available, then a null
  114.  * (0.0) correction is used. This can occur when no EOP data is loaded, or when the
  115.  * requested date is beyond the time span of the loaded EOP data. Using a null
  116.  * correction can result in coarse accuracy. To check the time span covered by EOP data use
  117.  * {@link #getEOPHistory(IERSConventions, boolean)}, {@link EOPHistory#getStartDate()},
  118.  * and {@link EOPHistory#getEndDate()}.
  119.  * <p>
  120.  * For more information on configuring the EOP data Orekit uses see
  121.  * <a href="https://www.orekit.org/forge/projects/orekit/wiki/Configuration">
  122.  * https://www.orekit.org/forge/projects/orekit/wiki/Configuration</a>.
  123.  * <p>
  124.  * Here is a schematic representation of the predefined reference frames tree:
  125.  * </p>
  126.  * <pre>
  127.  *                                                                  GCRF
  128.  *                                                                    |
  129.  *                                                 |-----------------------------------------------
  130.  *                                                 |                         |     Frame bias     |
  131.  *                                                 |                         |                 EME2000
  132.  *                                                 |                         |                    |
  133.  *                                                 |                         | Precession effects |
  134.  *                                                 |                         |                    |
  135.  *           Bias, Precession and Nutation effects |                        MOD                  MOD  (Mean Equator Of Date)
  136.  *                                                 |                         |             w/o EOP corrections
  137.  *                                                 |                         |  Nutation effects  |
  138.  *    (Celestial Intermediate Reference Frame)   CIRF                        |                    |
  139.  *                                                 |                        TOD                  TOD  (True Equator Of Date)
  140.  *                          Earth natural rotation |                         |             w/o EOP corrections
  141.  *                                                 |-------------            |    Sidereal Time   |
  142.  *                                                 |            |            |                    |
  143.  *  (Terrestrial Intermediate Reference Frame)   TIRF         TIRF         GTOD                 GTOD  (Greenwich True Of Date)
  144.  *                                                 |    w/o tidal effects                  w/o EOP corrections
  145.  *                                     Pole motion |            |                                 |
  146.  *                                                 |            |                                 |-------------
  147.  *                                                 |            |                                 |            |
  148.  * (International Terrestrial Reference Frame)   ITRF         ITRF                              ITRF        VEIS1950
  149.  *                                                 |    w/o tidal effects                   equinox-based
  150.  *                                                 |            |
  151.  *                                           other ITRF     other ITRF
  152.  *                                                      w/o tidal effects
  153.  * </pre>
  154.  * <p>
  155.  * This is a utility class, so its constructor is private.
  156.  * </p>
  157.  * @author Guylaine Prat
  158.  * @author Luc Maisonobe
  159.  * @author Pascal Parraud
  160.  */
  161. public class FramesFactory {

  162.     /** Default regular expression for the Rapid Data and Prediction EOP columns files (IAU1980 compatibles). */
  163.     public static final String RAPID_DATA_PREDICTION_COLUMNS_1980_FILENAME = "^finals\\.[^.]*$";

  164.     /** Default regular expression for the Rapid Data and Prediction EOP XML files (IAU1980 compatibles). */
  165.     public static final String RAPID_DATA_PREDICTION_XML_1980_FILENAME = "^finals\\..*\\.xml$";

  166.     /** Default regular expression for the EOPC04 files (IAU1980 compatibles). */
  167.     public static final String EOPC04_1980_FILENAME = "^eopc04_\\d\\d\\.(\\d\\d)$";

  168.     /** Default regular expression for the BulletinB files (IAU1980 compatibles). */
  169.     public static final String BULLETINB_1980_FILENAME = "^bulletinb(_IAU1980)?((-\\d\\d\\d\\.txt)|(\\.\\d\\d\\d))$";

  170.     /** Default regular expression for the Rapid Data and Prediction EOP columns files (IAU2000 compatibles). */
  171.     public static final String RAPID_DATA_PREDICITON_COLUMNS_2000_FILENAME = "^finals2000A\\.[^.]*$";

  172.     /** Default regular expression for the Rapid Data and Prediction EOP XML files (IAU2000 compatibles). */
  173.     public static final String RAPID_DATA_PREDICITON_XML_2000_FILENAME = "^finals2000A\\..*\\.xml$";

  174.     /** Default regular expression for the EOPC04 files (IAU2000 compatibles). */
  175.     public static final String EOPC04_2000_FILENAME = "^eopc04_\\d\\d_IAU2000\\.(\\d\\d)$";

  176.     /** Default regular expression for the BulletinB files (IAU2000 compatibles). */
  177.     public static final String BULLETINB_2000_FILENAME = "^bulletinb(_IAU2000)?((-\\d\\d\\d\\.txt)|(\\.\\d\\d\\d))$";

  178.     /** Default regular expression for the BulletinA files (IAU1980 and IAU2000 compatibles). */
  179.     public static final String BULLETINA_FILENAME = "^bulletina-[ivxlcdm]+-\\d\\d\\d\\.txt$";

  180.     /** Predefined frames. */
  181.     private static transient Map<Predefined, FactoryManagedFrame> FRAMES =
  182.         new HashMap<Predefined, FactoryManagedFrame>();

  183.     /** Predefined versioned ITRF frames. */
  184.     private static transient Map<ITRFKey, VersionedITRF> VERSIONED_ITRF_FRAMES =
  185.         new HashMap<ITRFKey, VersionedITRF>();

  186.     /** Loaders for Earth Orientation parameters. */
  187.     private static final Map<IERSConventions, List<EOPHistoryLoader>> EOP_HISTORY_LOADERS =
  188.         new HashMap<IERSConventions, List<EOPHistoryLoader>>();

  189.     /** Threshold for EOP continuity. */
  190.     private static double EOP_CONTINUITY_THRESHOLD = 5 * Constants.JULIAN_DAY;

  191.     /** Private constructor.
  192.      * <p>This class is a utility class, it should neither have a public
  193.      * nor a default constructor. This private constructor prevents
  194.      * the compiler from generating one automatically.</p>
  195.      */
  196.     private FramesFactory() {
  197.     }

  198.     /** Add the default loaders EOP history (IAU 1980 precession/nutation).
  199.      * <p>
  200.      * The default loaders look for IERS EOP C04 and bulletins B files. They
  201.      * correspond to {@link IERSConventions#IERS_1996 IERS 1996} conventions.
  202.      * </p>
  203.      * @param rapidDataColumnsSupportedNames regular expression for supported
  204.      * rapid data columns EOP files names
  205.      * (may be null if the default IERS file names are used)
  206.      * @param rapidDataXMLSupportedNames regular expression for supported
  207.      * rapid data XML EOP files names
  208.      * (may be null if the default IERS file names are used)
  209.      * @param eopC04SupportedNames regular expression for supported EOP C04 files names
  210.      * (may be null if the default IERS file names are used)
  211.      * @param bulletinBSupportedNames regular expression for supported bulletin B files names
  212.      * (may be null if the default IERS file names are used)
  213.      * @param bulletinASupportedNames regular expression for supported bulletin A files names
  214.      * (may be null if the default IERS file names are used)
  215.      * @see <a href="http://hpiers.obspm.fr/eoppc/eop/eopc04/">IERS EOP C04 files</a>
  216.      * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader)
  217.      * @see #clearEOPHistoryLoaders()
  218.      * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String)
  219.      */
  220.     public static void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupportedNames,
  221.                                                        final String rapidDataXMLSupportedNames,
  222.                                                        final String eopC04SupportedNames,
  223.                                                        final String bulletinBSupportedNames,
  224.                                                        final String bulletinASupportedNames) {
  225.         final String rapidColNames =
  226.                 (rapidDataColumnsSupportedNames == null) ?
  227.                 RAPID_DATA_PREDICTION_COLUMNS_1980_FILENAME : rapidDataColumnsSupportedNames;
  228.         addEOPHistoryLoader(IERSConventions.IERS_1996,
  229.                             new RapidDataAndPredictionColumnsLoader(false, rapidColNames));
  230.         final String rapidXmlNames =
  231.                 (rapidDataXMLSupportedNames == null) ?
  232.                 RAPID_DATA_PREDICTION_XML_1980_FILENAME : rapidDataXMLSupportedNames;
  233.         addEOPHistoryLoader(IERSConventions.IERS_1996,
  234.                             new RapidDataAndPredictionXMLLoader(rapidXmlNames));
  235.         final String eopcNames =
  236.                 (eopC04SupportedNames == null) ? EOPC04_1980_FILENAME : eopC04SupportedNames;
  237.         addEOPHistoryLoader(IERSConventions.IERS_1996,
  238.                             new EOPC04FilesLoader(eopcNames));
  239.         final String bulBNames =
  240.                 (bulletinBSupportedNames == null) ? BULLETINB_1980_FILENAME : bulletinBSupportedNames;
  241.         addEOPHistoryLoader(IERSConventions.IERS_1996,
  242.                             new BulletinBFilesLoader(bulBNames));
  243.         final String bulANames =
  244.                     (bulletinASupportedNames == null) ? BULLETINA_FILENAME : bulletinASupportedNames;
  245.         addEOPHistoryLoader(IERSConventions.IERS_1996,
  246.                             new BulletinAFilesLoader(bulANames));
  247.     }

  248.     /** Add the default loaders for EOP history (IAU 2000/2006 precession/nutation).
  249.      * <p>
  250.      * The default loaders look for IERS EOP C04 and bulletins B files. They
  251.      * correspond to both {@link IERSConventions#IERS_2003 IERS 2003} and {@link
  252.      * IERSConventions#IERS_2010 IERS 2010} conventions.
  253.      * </p>
  254.      * @param rapidDataColumnsSupportedNames regular expression for supported
  255.      * rapid data columns EOP files names
  256.      * (may be null if the default IERS file names are used)
  257.      * @param rapidDataXMLSupportedNames regular expression for supported
  258.      * rapid data XML EOP files names
  259.      * (may be null if the default IERS file names are used)
  260.      * @param eopC04SupportedNames regular expression for supported EOP C04 files names
  261.      * (may be null if the default IERS file names are used)
  262.      * @param bulletinBSupportedNames regular expression for supported bulletin B files names
  263.      * (may be null if the default IERS file names are used)
  264.      * @param bulletinASupportedNames regular expression for supported bulletin A files names
  265.      * (may be null if the default IERS file names are used)
  266.      * @see <a href="http://hpiers.obspm.fr/eoppc/eop/eopc04/">IERS EOP C04 files</a>
  267.      * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader)
  268.      * @see #clearEOPHistoryLoaders()
  269.      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String)
  270.      */
  271.     public static void addDefaultEOP2000HistoryLoaders(final String rapidDataColumnsSupportedNames,
  272.                                                        final String rapidDataXMLSupportedNames,
  273.                                                        final String eopC04SupportedNames,
  274.                                                        final String bulletinBSupportedNames,
  275.                                                        final String bulletinASupportedNames) {
  276.         final String rapidColNames =
  277.                 (rapidDataColumnsSupportedNames == null) ?
  278.                 RAPID_DATA_PREDICITON_COLUMNS_2000_FILENAME : rapidDataColumnsSupportedNames;
  279.         addEOPHistoryLoader(IERSConventions.IERS_2003,
  280.                             new RapidDataAndPredictionColumnsLoader(true, rapidColNames));
  281.         addEOPHistoryLoader(IERSConventions.IERS_2010,
  282.                             new RapidDataAndPredictionColumnsLoader(true, rapidColNames));
  283.         final String rapidXmlNames =
  284.             (rapidDataXMLSupportedNames == null) ?
  285.             RAPID_DATA_PREDICITON_XML_2000_FILENAME : rapidDataXMLSupportedNames;
  286.         addEOPHistoryLoader(IERSConventions.IERS_2003,
  287.                             new RapidDataAndPredictionXMLLoader(rapidXmlNames));
  288.         addEOPHistoryLoader(IERSConventions.IERS_2010,
  289.                             new RapidDataAndPredictionXMLLoader(rapidXmlNames));
  290.         final String eopcNames =
  291.             (eopC04SupportedNames == null) ? EOPC04_2000_FILENAME : eopC04SupportedNames;
  292.         addEOPHistoryLoader(IERSConventions.IERS_2003,
  293.                             new EOPC04FilesLoader(eopcNames));
  294.         addEOPHistoryLoader(IERSConventions.IERS_2010,
  295.                             new EOPC04FilesLoader(eopcNames));
  296.         final String bulBNames =
  297.             (bulletinBSupportedNames == null) ? BULLETINB_2000_FILENAME : bulletinBSupportedNames;
  298.         addEOPHistoryLoader(IERSConventions.IERS_2003,
  299.                             new BulletinBFilesLoader(bulBNames));
  300.         addEOPHistoryLoader(IERSConventions.IERS_2010,
  301.                             new BulletinBFilesLoader(bulBNames));
  302.         final String bulANames =
  303.                 (bulletinASupportedNames == null) ? BULLETINA_FILENAME : bulletinASupportedNames;
  304.         addEOPHistoryLoader(IERSConventions.IERS_2003,
  305.                             new BulletinAFilesLoader(bulANames));
  306.         addEOPHistoryLoader(IERSConventions.IERS_2010,
  307.                             new BulletinAFilesLoader(bulANames));
  308.     }

  309.     /** Add a loader for Earth Orientation Parameters history.
  310.      * @param conventions IERS conventions to which EOP history applies
  311.      * @param loader custom loader to add for the EOP history
  312.      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String)
  313.      * @see #clearEOPHistoryLoaders()
  314.      */
  315.     public static void addEOPHistoryLoader(final IERSConventions conventions, final EOPHistoryLoader loader) {
  316.         synchronized (EOP_HISTORY_LOADERS) {
  317.             if (!EOP_HISTORY_LOADERS.containsKey(conventions)) {
  318.                 EOP_HISTORY_LOADERS.put(conventions, new ArrayList<EOPHistoryLoader>());
  319.             }
  320.             EOP_HISTORY_LOADERS.get(conventions).add(loader);
  321.         }
  322.     }

  323.     /** Clear loaders for Earth Orientation Parameters history.
  324.      * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader)
  325.      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String)
  326.      */
  327.     public static void clearEOPHistoryLoaders() {
  328.         synchronized (EOP_HISTORY_LOADERS) {
  329.             EOP_HISTORY_LOADERS.clear();
  330.         }
  331.     }

  332.     /** Set the threshold to check EOP continuity.
  333.      * <p>
  334.      * The default threshold (used if this method is never called)
  335.      * is 5 Julian days. If after loading EOP entries some holes
  336.      * between entries exceed this threshold, an exception will
  337.      * be triggered.
  338.      * </p>
  339.      * <p>
  340.      * One case when calling this method is really useful is for
  341.      * applications that use a single Bulletin A, as these bulletins
  342.      * have a roughly one month wide hole for the first bulletin of
  343.      * each month, which contains older final data in addition to the
  344.      * rapid data and the predicted data.
  345.      * </p>
  346.      * @param threshold threshold to use for checking EOP continuity (in seconds)
  347.      */
  348.     public static void setEOPContinuityThreshold(final double threshold) {
  349.         EOP_CONTINUITY_THRESHOLD = threshold;
  350.     }

  351.     /** Get Earth Orientation Parameters history.
  352.      * <p>
  353.      * If no {@link EOPHistoryLoader} has been added by calling {@link
  354.      * #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) addEOPHistoryLoader}
  355.      * or if {@link #clearEOPHistoryLoaders() clearEOPHistoryLoaders} has been
  356.      * called afterwards, the {@link #addDefaultEOP1980HistoryLoaders(String, String,
  357.      * String, String, String)} and {@link #addDefaultEOP2000HistoryLoaders(String,
  358.      * String, String, String, String)} methods will be called automatically with
  359.      * supported file names parameters all set to null, in order to get the default
  360.      * loaders configuration.
  361.      * </p>
  362.      * @param conventions conventions for which EOP history is requested
  363.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  364.      * @return Earth Orientation Parameters history
  365.      * @exception OrekitException if the data cannot be loaded
  366.      */
  367.     public static EOPHistory getEOPHistory(final IERSConventions conventions, final boolean simpleEOP)
  368.         throws OrekitException {

  369.         synchronized (EOP_HISTORY_LOADERS) {

  370.             if (EOP_HISTORY_LOADERS.isEmpty()) {
  371.                 // set up using default loaders
  372.                 addDefaultEOP2000HistoryLoaders(null, null, null, null, null);
  373.                 addDefaultEOP1980HistoryLoaders(null, null, null, null, null);
  374.             }

  375.             // TimeStamped based set needed to remove duplicates
  376.             OrekitException pendingException = null;
  377.             final SortedSet<EOPEntry> data = new TreeSet<EOPEntry>(new ChronologicalComparator());

  378.             // try to load canonical data if available
  379.             if (EOP_HISTORY_LOADERS.containsKey(conventions)) {
  380.                 for (final EOPHistoryLoader loader : EOP_HISTORY_LOADERS.get(conventions)) {
  381.                     try {
  382.                         loader.fillHistory(conventions.getNutationCorrectionConverter(), data);
  383.                     } catch (OrekitException oe) {
  384.                         pendingException = oe;
  385.                     }
  386.                 }
  387.             }

  388.             if (data.isEmpty() && pendingException != null) {
  389.                 throw pendingException;
  390.             }

  391.             final EOPHistory history = new EOPHistory(conventions, data, simpleEOP);
  392.             history.checkEOPContinuity(EOP_CONTINUITY_THRESHOLD);
  393.             return history;

  394.         }

  395.     }

  396.     /** Get one of the predefined frames.
  397.      * @param factoryKey key of the frame within the factory
  398.      * @return the predefined frame
  399.      * @exception OrekitException if frame cannot be built due to missing data
  400.      */
  401.     public static Frame getFrame(final Predefined factoryKey)
  402.         throws OrekitException {
  403.         switch (factoryKey) {
  404.             case GCRF :
  405.                 return getGCRF();
  406.             case ICRF :
  407.                 return getICRF();
  408.             case ECLIPTIC_CONVENTIONS_1996 :
  409.                 return getEcliptic(IERSConventions.IERS_1996);
  410.             case ECLIPTIC_CONVENTIONS_2003 :
  411.                 return getEcliptic(IERSConventions.IERS_2003);
  412.             case ECLIPTIC_CONVENTIONS_2010 :
  413.                 return getEcliptic(IERSConventions.IERS_2010);
  414.             case EME2000 :
  415.                 return getEME2000();
  416.             case ITRF_CIO_CONV_2010_SIMPLE_EOP :
  417.                 return getITRF(IERSConventions.IERS_2010, true);
  418.             case ITRF_CIO_CONV_2010_ACCURATE_EOP :
  419.                 return getITRF(IERSConventions.IERS_2010, false);
  420.             case ITRF_CIO_CONV_2003_SIMPLE_EOP :
  421.                 return getITRF(IERSConventions.IERS_2003, true);
  422.             case ITRF_CIO_CONV_2003_ACCURATE_EOP :
  423.                 return getITRF(IERSConventions.IERS_2003, false);
  424.             case ITRF_CIO_CONV_1996_SIMPLE_EOP :
  425.                 return getITRF(IERSConventions.IERS_1996, true);
  426.             case ITRF_CIO_CONV_1996_ACCURATE_EOP :
  427.                 return getITRF(IERSConventions.IERS_1996, false);
  428.             case ITRF_EQUINOX_CONV_2010_SIMPLE_EOP :
  429.                 return getITRFEquinox(IERSConventions.IERS_2010, true);
  430.             case ITRF_EQUINOX_CONV_2010_ACCURATE_EOP :
  431.                 return getITRFEquinox(IERSConventions.IERS_2010, false);
  432.             case ITRF_EQUINOX_CONV_2003_SIMPLE_EOP :
  433.                 return getITRFEquinox(IERSConventions.IERS_2003, true);
  434.             case ITRF_EQUINOX_CONV_2003_ACCURATE_EOP :
  435.                 return getITRFEquinox(IERSConventions.IERS_2003, false);
  436.             case ITRF_EQUINOX_CONV_1996_SIMPLE_EOP :
  437.                 return getITRFEquinox(IERSConventions.IERS_1996, true);
  438.             case ITRF_EQUINOX_CONV_1996_ACCURATE_EOP :
  439.                 return getITRFEquinox(IERSConventions.IERS_1996, false);
  440.             case TIRF_CONVENTIONS_2010_SIMPLE_EOP :
  441.                 return getTIRF(IERSConventions.IERS_2010, true);
  442.             case TIRF_CONVENTIONS_2010_ACCURATE_EOP :
  443.                 return getTIRF(IERSConventions.IERS_2010, false);
  444.             case TIRF_CONVENTIONS_2003_SIMPLE_EOP :
  445.                 return getTIRF(IERSConventions.IERS_2003, true);
  446.             case TIRF_CONVENTIONS_2003_ACCURATE_EOP :
  447.                 return getTIRF(IERSConventions.IERS_2003, false);
  448.             case TIRF_CONVENTIONS_1996_SIMPLE_EOP :
  449.                 return getTIRF(IERSConventions.IERS_1996, true);
  450.             case TIRF_CONVENTIONS_1996_ACCURATE_EOP :
  451.                 return getTIRF(IERSConventions.IERS_1996, false);
  452.             case CIRF_CONVENTIONS_2010_ACCURATE_EOP :
  453.                 return getCIRF(IERSConventions.IERS_2010, false);
  454.             case CIRF_CONVENTIONS_2010_SIMPLE_EOP :
  455.                 return getCIRF(IERSConventions.IERS_2010, true);
  456.             case CIRF_CONVENTIONS_2003_ACCURATE_EOP :
  457.                 return getCIRF(IERSConventions.IERS_2003, false);
  458.             case CIRF_CONVENTIONS_2003_SIMPLE_EOP :
  459.                 return getCIRF(IERSConventions.IERS_2003, true);
  460.             case CIRF_CONVENTIONS_1996_ACCURATE_EOP :
  461.                 return getCIRF(IERSConventions.IERS_1996, false);
  462.             case CIRF_CONVENTIONS_1996_SIMPLE_EOP :
  463.                 return getCIRF(IERSConventions.IERS_1996, true);
  464.             case VEIS_1950 :
  465.                 return getVeis1950();
  466.             case GTOD_WITHOUT_EOP_CORRECTIONS :
  467.                 return getGTOD(IERSConventions.IERS_1996, false, true);
  468.             case GTOD_CONVENTIONS_2010_ACCURATE_EOP :
  469.                 return getGTOD(IERSConventions.IERS_2010, true, false);
  470.             case GTOD_CONVENTIONS_2010_SIMPLE_EOP :
  471.                 return getGTOD(IERSConventions.IERS_2010, true, true);
  472.             case GTOD_CONVENTIONS_2003_ACCURATE_EOP :
  473.                 return getGTOD(IERSConventions.IERS_2003, true, false);
  474.             case GTOD_CONVENTIONS_2003_SIMPLE_EOP :
  475.                 return getGTOD(IERSConventions.IERS_2003, true, true);
  476.             case GTOD_CONVENTIONS_1996_ACCURATE_EOP :
  477.                 return getGTOD(IERSConventions.IERS_1996, true, false);
  478.             case GTOD_CONVENTIONS_1996_SIMPLE_EOP :
  479.                 return getGTOD(IERSConventions.IERS_1996, true, true);
  480.             case TOD_WITHOUT_EOP_CORRECTIONS :
  481.                 return getTOD(IERSConventions.IERS_1996, false, true);
  482.             case TOD_CONVENTIONS_2010_ACCURATE_EOP :
  483.                 return getTOD(IERSConventions.IERS_2010, true, false);
  484.             case TOD_CONVENTIONS_2010_SIMPLE_EOP :
  485.                 return getTOD(IERSConventions.IERS_2010, true, true);
  486.             case TOD_CONVENTIONS_2003_ACCURATE_EOP :
  487.                 return getTOD(IERSConventions.IERS_2003, true, false);
  488.             case TOD_CONVENTIONS_2003_SIMPLE_EOP :
  489.                 return getTOD(IERSConventions.IERS_2003, true, true);
  490.             case TOD_CONVENTIONS_1996_ACCURATE_EOP :
  491.                 return getTOD(IERSConventions.IERS_1996, true, false);
  492.             case TOD_CONVENTIONS_1996_SIMPLE_EOP :
  493.                 return getTOD(IERSConventions.IERS_1996, true, true);
  494.             case MOD_WITHOUT_EOP_CORRECTIONS :
  495.                 return getMOD(IERSConventions.IERS_1996, false);
  496.             case MOD_CONVENTIONS_2010 :
  497.                 return getMOD(IERSConventions.IERS_2010, true);
  498.             case MOD_CONVENTIONS_2003 :
  499.                 return getMOD(IERSConventions.IERS_2003, true);
  500.             case MOD_CONVENTIONS_1996 :
  501.                 return getMOD(IERSConventions.IERS_1996, true);
  502.             case TEME :
  503.                 return getTEME();
  504.             default :
  505.                 // this should never happen
  506.                 throw new OrekitInternalError(null);
  507.         }
  508.     }

  509.     /** Get the unique GCRF frame.
  510.      * <p>The GCRF frame is the root frame in the frame tree.</p>
  511.      * @return the unique instance of the GCRF frame
  512.      */
  513.     public static Frame getGCRF() {
  514.         return Frame.getRoot();
  515.     }

  516.     /** Get the unique ICRF frame.
  517.      * <p>The ICRF frame is centered at solar system barycenter and aligned
  518.      * with GCRF.</p>
  519.      * @return the unique instance of the ICRF frame
  520.      * @exception OrekitException if solar system ephemerides cannot be loaded
  521.      */
  522.     public static Frame getICRF() throws OrekitException {
  523.         return CelestialBodyFactory.getSolarSystemBarycenter().getInertiallyOrientedFrame();
  524.     }

  525.     /** Get the ecliptic frame.
  526.      * The IAU defines the ecliptic as "the plane perpendicular to the mean heliocentric
  527.      * orbital angular momentum vector of the Earth-Moon barycentre in the BCRS (IAU 2006
  528.      * Resolution B1)." The +z axis is aligned with the angular momentum vector, and the +x
  529.      * axis is aligned with +x axis of {@link FramesFactory#getMOD(IERSConventions) MOD}.
  530.      *
  531.      * <p> This implementation agrees with the JPL 406 ephemerides to within 0.5 arc seconds.
  532.      * @param conventions IERS conventions to apply
  533.      * @return the selected reference frame singleton.
  534.      * @exception OrekitException if data embedded in the library cannot be read
  535.      */
  536.     public static Frame getEcliptic(final IERSConventions conventions) throws OrekitException {
  537.         synchronized (FramesFactory.class) {

  538.             final Predefined factoryKey;
  539.             switch (conventions) {
  540.                 case IERS_1996 :
  541.                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_1996;
  542.                     break;
  543.                 case IERS_2003 :
  544.                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_2003;
  545.                     break;
  546.                 case IERS_2010 :
  547.                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_2010;
  548.                     break;
  549.                 default :
  550.                     // this should never happen
  551.                     throw new OrekitInternalError(null);
  552.             }
  553.             final Frame parent = getMOD(conventions);

  554.             // try to find an already built frame
  555.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  556.             if (frame == null) {
  557.                 // it's the first time we need this frame, build it and store it
  558.                 frame = new FactoryManagedFrame(parent, new EclipticProvider(conventions),
  559.                                                 true, factoryKey);
  560.                 FRAMES.put(factoryKey, frame);
  561.             }

  562.             return frame;

  563.         }
  564.     }

  565.     /** Get the unique EME2000 frame.
  566.      * <p>The EME2000 frame is also called the J2000 frame.
  567.      * The former denomination is preferred in Orekit.</p>
  568.      * @return the unique instance of the EME2000 frame
  569.      */
  570.     public static FactoryManagedFrame getEME2000() {
  571.         synchronized (FramesFactory.class) {

  572.             // try to find an already built frame
  573.             FactoryManagedFrame frame = FRAMES.get(Predefined.EME2000);

  574.             if (frame == null) {
  575.                 // it's the first time we need this frame, build it and store it
  576.                 frame = new FactoryManagedFrame(getGCRF(), new EME2000Provider(), true, Predefined.EME2000);
  577.                 FRAMES.put(Predefined.EME2000, frame);
  578.             }

  579.             return frame;

  580.         }
  581.     }

  582.     /** Get an unspecified International Terrestrial Reference Frame.
  583.      * <p>
  584.      * The frame returned uses the {@link EOPEntry Earth Orientation Parameters}
  585.      * blindly. So if for example one loads only EOP 14 C04 files to retrieve
  586.      * the parameters, the frame will be an {@link ITRFVersion#ITRF_2014}. However,
  587.      * if parameters are loaded from different files types, or even for file
  588.      * types that changed their reference (like Bulletin A switching from
  589.      * {@link ITRFVersion#ITRF_2008} to {@link ITRFVersion#ITRF_2014} starting
  590.      * with Vol. XXXI No. 013 published on 2018-03-29), then the ITRF returned
  591.      * by this method will jump from one version to another version.
  592.      * </p>
  593.      * <p>
  594.      * IF a specific version of ITRF is needed, then {@link #getITRF(ITRFVersion,
  595.      * IERSConventions, boolean)} should be used instead.
  596.      * </p>
  597.      * @param conventions IERS conventions to apply
  598.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  599.      * @return the selected reference frame singleton.
  600.      * @exception OrekitException if data embedded in the library cannot be read
  601.      * @see #getITRF(ITRFVersion, IERSConventions, boolean)
  602.      * @since 6.1
  603.      */
  604.     public static FactoryManagedFrame getITRF(final IERSConventions conventions,
  605.                                               final boolean simpleEOP)
  606.         throws OrekitException {
  607.         synchronized (FramesFactory.class) {

  608.             // try to find an already built frame
  609.             final Predefined factoryKey;
  610.             switch (conventions) {
  611.                 case IERS_1996 :
  612.                     factoryKey = simpleEOP ?
  613.                                  Predefined.ITRF_CIO_CONV_1996_SIMPLE_EOP :
  614.                                  Predefined.ITRF_CIO_CONV_1996_ACCURATE_EOP;
  615.                     break;
  616.                 case IERS_2003 :
  617.                     factoryKey = simpleEOP ?
  618.                                  Predefined.ITRF_CIO_CONV_2003_SIMPLE_EOP :
  619.                                  Predefined.ITRF_CIO_CONV_2003_ACCURATE_EOP;
  620.                     break;
  621.                 case IERS_2010 :
  622.                     factoryKey = simpleEOP ?
  623.                                  Predefined.ITRF_CIO_CONV_2010_SIMPLE_EOP :
  624.                                  Predefined.ITRF_CIO_CONV_2010_ACCURATE_EOP;
  625.                     break;
  626.                 default :
  627.                     // this should never happen
  628.                     throw new OrekitInternalError(null);
  629.             }
  630.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  631.             if (frame == null) {
  632.                 // it's the first time we need this frame, build it and store it
  633.                 final Frame tirfFrame = getTIRF(conventions, simpleEOP);
  634.                 final TIRFProvider tirfProvider = (TIRFProvider) tirfFrame.getTransformProvider();
  635.                 frame = new FactoryManagedFrame(tirfFrame,
  636.                                                 new ITRFProvider(tirfProvider.getEOPHistory()),
  637.                                                 false, factoryKey);
  638.                 FRAMES.put(factoryKey, frame);
  639.             }

  640.             return frame;

  641.         }
  642.     }

  643.     /** Get the TIRF reference frame, ignoring tidal effects.
  644.      * @param conventions IERS conventions to apply
  645.      * @return the selected reference frame singleton.
  646.      * @exception OrekitException if the precession-nutation model data embedded in the
  647.      * library cannot be read.
  648.      */
  649.     public static FactoryManagedFrame getTIRF(final IERSConventions conventions) throws OrekitException {
  650.         return getTIRF(conventions, true);
  651.     }

  652.     /** Get an specific International Terrestrial Reference Frame.
  653.      * <p>
  654.      * Note that if a specific version of ITRF is required, then {@code simpleEOP}
  655.      * should most probably be set to {@code false}, as ignoring tidal effects
  656.      * has an effect of the same order of magnitude as the differences between
  657.      * the various {@link ITRFVersion ITRF versions}.
  658.      * </p>
  659.      * @param version ITRF version
  660.      * @param conventions IERS conventions to apply
  661.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  662.      * @return the selected reference frame singleton.
  663.      * @exception OrekitException if data embedded in the library cannot be read
  664.      * @since 9.2
  665.      */
  666.     public static VersionedITRF getITRF(final ITRFVersion version,
  667.                                         final IERSConventions conventions,
  668.                                         final boolean simpleEOP)
  669.         throws OrekitException {
  670.         synchronized (FramesFactory.class) {
  671.             // try to find an already built frame
  672.             final ITRFKey key = new ITRFKey(version, conventions, simpleEOP);
  673.             VersionedITRF frame = VERSIONED_ITRF_FRAMES.get(key);

  674.             if (frame == null) {
  675.                 // it's the first time we need this frame, build it and store it
  676.                 final FactoryManagedFrame rawITRF = getITRF(conventions, simpleEOP);
  677.                 frame = new VersionedITRF(rawITRF.getParent(), version,
  678.                                           (ITRFProvider) rawITRF.getTransformProvider(),
  679.                                           version.toString().replace('_', '-') +
  680.                                           "/" +
  681.                                           rawITRF.getName());
  682.                 VERSIONED_ITRF_FRAMES.put(key, frame);
  683.             }

  684.             return frame;

  685.         }
  686.     }

  687.     /** Get the TIRF reference frame.
  688.      * @param conventions IERS conventions to apply
  689.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  690.      * @return the selected reference frame singleton.
  691.      * @exception OrekitException if the precession-nutation model data embedded in the
  692.      * library cannot be read.
  693.      * @since 6.1
  694.      */
  695.     public static FactoryManagedFrame getTIRF(final IERSConventions conventions,
  696.                                               final boolean simpleEOP)
  697.         throws OrekitException {
  698.         synchronized (FramesFactory.class) {

  699.             // try to find an already built frame
  700.             final Predefined factoryKey;
  701.             switch (conventions) {
  702.                 case IERS_1996 :
  703.                     factoryKey = simpleEOP ?
  704.                                  Predefined.TIRF_CONVENTIONS_1996_SIMPLE_EOP :
  705.                                  Predefined.TIRF_CONVENTIONS_1996_ACCURATE_EOP;
  706.                     break;
  707.                 case IERS_2003 :
  708.                     factoryKey = simpleEOP ?
  709.                                  Predefined.TIRF_CONVENTIONS_2003_SIMPLE_EOP :
  710.                                  Predefined.TIRF_CONVENTIONS_2003_ACCURATE_EOP;
  711.                     break;
  712.                 case IERS_2010 :
  713.                     factoryKey = simpleEOP ?
  714.                                  Predefined.TIRF_CONVENTIONS_2010_SIMPLE_EOP :
  715.                                  Predefined.TIRF_CONVENTIONS_2010_ACCURATE_EOP;
  716.                     break;
  717.                 default :
  718.                     // this should never happen
  719.                     throw new OrekitInternalError(null);
  720.             }
  721.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  722.             if (frame == null) {
  723.                 // it's the first time we need this frame, build it and store it
  724.                 final Frame cirf = getCIRF(conventions, simpleEOP);
  725.                 final ShiftingTransformProvider cirfInterpolating =
  726.                         (ShiftingTransformProvider) cirf.getTransformProvider();
  727.                 final CIRFProvider cirfRaw = (CIRFProvider) cirfInterpolating.getRawProvider();
  728.                 final EOPHistory eopHistory = cirfRaw.getEOPHistory();
  729.                 frame = new FactoryManagedFrame(cirf, new TIRFProvider(eopHistory), false, factoryKey);
  730.                 FRAMES.put(factoryKey, frame);
  731.             }

  732.             return frame;

  733.         }
  734.     }

  735.     /** Get the CIRF2000 reference frame.
  736.      * @param conventions IERS conventions to apply
  737.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  738.      * @return the selected reference frame singleton.
  739.      * @exception OrekitException if the precession-nutation model data embedded in the
  740.      * library cannot be read.
  741.      */
  742.     public static FactoryManagedFrame getCIRF(final IERSConventions conventions,
  743.                                               final boolean simpleEOP)
  744.         throws OrekitException {
  745.         synchronized (FramesFactory.class) {

  746.             // try to find an already built frame
  747.             final Predefined factoryKey;
  748.             switch (conventions) {
  749.                 case IERS_1996 :
  750.                     factoryKey = simpleEOP ?
  751.                                  Predefined.CIRF_CONVENTIONS_1996_SIMPLE_EOP :
  752.                                  Predefined.CIRF_CONVENTIONS_1996_ACCURATE_EOP;
  753.                     break;
  754.                 case IERS_2003 :
  755.                     factoryKey = simpleEOP ?
  756.                                  Predefined.CIRF_CONVENTIONS_2003_SIMPLE_EOP :
  757.                                  Predefined.CIRF_CONVENTIONS_2003_ACCURATE_EOP;
  758.                     break;
  759.                 case IERS_2010 :
  760.                     factoryKey = simpleEOP ?
  761.                                  Predefined.CIRF_CONVENTIONS_2010_SIMPLE_EOP :
  762.                                  Predefined.CIRF_CONVENTIONS_2010_ACCURATE_EOP;
  763.                     break;
  764.                 default :
  765.                     // this should never happen
  766.                     throw new OrekitInternalError(null);
  767.             }
  768.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  769.             if (frame == null) {
  770.                 // it's the first time we need this frame, build it and store it
  771.                 final EOPHistory eopHistory = FramesFactory.getEOPHistory(conventions, simpleEOP);
  772.                 final TransformProvider shifting =
  773.                         new ShiftingTransformProvider(new CIRFProvider(eopHistory),
  774.                                                       CartesianDerivativesFilter.USE_PVA,
  775.                                                       AngularDerivativesFilter.USE_R,
  776.                                                       6, Constants.JULIAN_DAY / 24,
  777.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  778.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
  779.                 frame = new FactoryManagedFrame(getGCRF(), shifting, true, factoryKey);
  780.                 FRAMES.put(factoryKey, frame);
  781.             }

  782.             return frame;

  783.         }
  784.     }

  785.     /** Get the VEIS 1950 reference frame.
  786.      * <p>Its parent frame is the GTOD frame with IERS 1996 conventions without EOP corrections.<p>
  787.      * @return the selected reference frame singleton.
  788.      * @exception OrekitException if data embedded in the library cannot be read
  789.      */
  790.     public static FactoryManagedFrame getVeis1950() throws OrekitException {
  791.         synchronized (FramesFactory.class) {

  792.             // try to find an already built frame
  793.             final Predefined factoryKey = Predefined.VEIS_1950;
  794.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  795.             if (frame == null) {
  796.                 // it's the first time we need this frame, build it and store it
  797.                 frame = new FactoryManagedFrame(FramesFactory.getGTOD(IERSConventions.IERS_1996, false, true),
  798.                                                 new VEISProvider(), true, factoryKey);
  799.                 FRAMES.put(factoryKey, frame);
  800.             }

  801.             return frame;

  802.         }
  803.     }

  804.     /** Get the equinox-based ITRF reference frame.
  805.      * @param conventions IERS conventions to apply
  806.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  807.      * @return the selected reference frame singleton.
  808.      * @exception OrekitException if data embedded in the library cannot be read
  809.      * @since 6.1
  810.      */
  811.     public static FactoryManagedFrame getITRFEquinox(final IERSConventions conventions,
  812.                                                      final boolean simpleEOP)
  813.         throws OrekitException {
  814.         synchronized (FramesFactory.class) {

  815.             // try to find an already built frame
  816.             final Predefined factoryKey;
  817.             switch (conventions) {
  818.                 case IERS_1996 :
  819.                     factoryKey = simpleEOP ?
  820.                                  Predefined.ITRF_EQUINOX_CONV_1996_SIMPLE_EOP :
  821.                                  Predefined.ITRF_EQUINOX_CONV_1996_ACCURATE_EOP;
  822.                     break;
  823.                 case IERS_2003 :
  824.                     factoryKey = simpleEOP ?
  825.                                  Predefined.ITRF_EQUINOX_CONV_2003_SIMPLE_EOP :
  826.                                  Predefined.ITRF_EQUINOX_CONV_2003_ACCURATE_EOP;
  827.                     break;
  828.                 case IERS_2010 :
  829.                     factoryKey = simpleEOP ?
  830.                                  Predefined.ITRF_EQUINOX_CONV_2010_SIMPLE_EOP :
  831.                                  Predefined.ITRF_EQUINOX_CONV_2010_ACCURATE_EOP;
  832.                     break;
  833.                 default :
  834.                     // this should never happen
  835.                     throw new OrekitInternalError(null);
  836.             }
  837.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  838.             if (frame == null) {
  839.                 // it's the first time we need this frame, build it and store it
  840.                 final Frame gtod = getGTOD(conventions, true, simpleEOP);
  841.                 final ShiftingTransformProvider gtodShifting =
  842.                         (ShiftingTransformProvider) gtod.getTransformProvider();
  843.                 final GTODProvider gtodRaw    = (GTODProvider) gtodShifting.getRawProvider();
  844.                 final EOPHistory   eopHistory = gtodRaw.getEOPHistory();
  845.                 frame = new FactoryManagedFrame(gtod, new ITRFProvider(eopHistory), false, factoryKey);
  846.                 FRAMES.put(factoryKey, frame);
  847.             }

  848.             return frame;

  849.         }
  850.     }

  851.     /** Get the GTOD reference frame.
  852.      * <p>
  853.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  854.      * consistency with legacy software that don't handle EOP correction parameters.
  855.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  856.      * (order of magnitudes for errors might be above 250m in LEO and 1400m in GEO).
  857.      * For this reason, setting this parameter to false is restricted to {@link
  858.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  859.      * IERSConventions IERS conventions} cannot be freely chosen here.
  860.      * </p>
  861.      * @param applyEOPCorr if true, EOP corrections are applied (here, dut1 and lod)
  862.      * @return the selected reference frame singleton.
  863.      * @exception OrekitException if data embedded in the library cannot be read
  864.      */
  865.     public static FactoryManagedFrame getGTOD(final boolean applyEOPCorr) throws OrekitException {
  866.         return getGTOD(IERSConventions.IERS_1996, applyEOPCorr, true);
  867.     }

  868.     /** Get the GTOD reference frame.
  869.      * @param conventions IERS conventions to apply
  870.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  871.      * @return the selected reference frame singleton.
  872.      * @exception OrekitException if data embedded in the library cannot be read
  873.      */
  874.     public static FactoryManagedFrame getGTOD(final IERSConventions conventions,
  875.                                               final boolean simpleEOP)
  876.         throws OrekitException {
  877.         return getGTOD(conventions, true, simpleEOP);
  878.     }

  879.     /** Get the GTOD reference frame.
  880.      * <p>
  881.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  882.      * consistency with legacy software that don't handle EOP correction parameters.
  883.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  884.      * (order of magnitudes for errors might be above 250m in LEO and 1400m in GEO).
  885.      * For this reason, setting this parameter to false is restricted to {@link
  886.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
  887.      * </p>
  888.      * @param conventions IERS conventions to apply
  889.      * @param applyEOPCorr if true, EOP corrections are applied (here, dut1 and lod)
  890.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  891.      * @return the selected reference frame singleton.
  892.      * @exception OrekitException if data embedded in the library cannot be read
  893.      */
  894.     private static FactoryManagedFrame getGTOD(final IERSConventions conventions,
  895.                                                final boolean applyEOPCorr,
  896.                                                final boolean simpleEOP)
  897.         throws OrekitException {

  898.         synchronized (FramesFactory.class) {

  899.             // try to find an already built frame
  900.             final Predefined factoryKey;
  901.             switch (conventions) {
  902.                 case IERS_1996 :
  903.                     factoryKey = applyEOPCorr ?
  904.                                  (simpleEOP ? Predefined.GTOD_CONVENTIONS_1996_SIMPLE_EOP : Predefined.GTOD_CONVENTIONS_1996_ACCURATE_EOP) :
  905.                                  Predefined.GTOD_WITHOUT_EOP_CORRECTIONS;
  906.                     break;
  907.                 case IERS_2003 :
  908.                     factoryKey = simpleEOP ?
  909.                                  Predefined.GTOD_CONVENTIONS_2003_SIMPLE_EOP :
  910.                                  Predefined.GTOD_CONVENTIONS_2003_ACCURATE_EOP;
  911.                     break;
  912.                 case IERS_2010 :
  913.                     factoryKey = simpleEOP ? Predefined.GTOD_CONVENTIONS_2010_SIMPLE_EOP :
  914.                                              Predefined.GTOD_CONVENTIONS_2010_ACCURATE_EOP;
  915.                     break;
  916.                 default :
  917.                     // this should never happen
  918.                     throw new OrekitInternalError(null);
  919.             }
  920.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  921.             if (frame == null) {
  922.                 // it's the first time we need this frame, build it and store it
  923.                 final Frame tod = getTOD(conventions, applyEOPCorr, simpleEOP);
  924.                 final ShiftingTransformProvider todInterpolating =
  925.                         (ShiftingTransformProvider) tod.getTransformProvider();
  926.                 final TODProvider       todRaw     = (TODProvider) todInterpolating.getRawProvider();
  927.                 final EOPHistory        eopHistory = todRaw.getEOPHistory();
  928.                 final GTODProvider      gtodRaw    = new GTODProvider(conventions, eopHistory);
  929.                 final TransformProvider gtodShifting =
  930.                         new ShiftingTransformProvider(gtodRaw,
  931.                                                       CartesianDerivativesFilter.USE_PVA,
  932.                                                       AngularDerivativesFilter.USE_R,
  933.                                                       todInterpolating.getGridPoints(), todInterpolating.getStep(),
  934.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  935.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
  936.                 frame = new FactoryManagedFrame(tod, gtodShifting, false, factoryKey);
  937.                 FRAMES.put(factoryKey, frame);
  938.             }

  939.             return frame;

  940.         }
  941.     }

  942.     /** Get the TOD reference frame.
  943.      * <p>
  944.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  945.      * consistency with legacy software that don't handle EOP correction parameters.
  946.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  947.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  948.      * For this reason, setting this parameter to false is restricted to {@link
  949.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  950.      * IERSConventions IERS conventions} cannot be freely chosen here.
  951.      * </p>
  952.      * @param applyEOPCorr if true, EOP corrections are applied (here, nutation)
  953.      * @return the selected reference frame singleton.
  954.      * @exception OrekitException if data embedded in the library cannot be read
  955.      */
  956.     public static FactoryManagedFrame getTOD(final boolean applyEOPCorr)
  957.         throws OrekitException {
  958.         return getTOD(IERSConventions.IERS_1996, applyEOPCorr, false);
  959.     }

  960.     /** Get the TOD reference frame.
  961.      * @param conventions IERS conventions to apply
  962.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  963.      * @return the selected reference frame singleton.
  964.      * @exception OrekitException if data embedded in the library cannot be read
  965.      */
  966.     public static FactoryManagedFrame getTOD(final IERSConventions conventions,
  967.                                              final boolean simpleEOP)
  968.         throws OrekitException {
  969.         return getTOD(conventions, true, simpleEOP);
  970.     }

  971.     /** Get the TOD reference frame.
  972.      * <p>
  973.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  974.      * consistency with legacy software that don't handle EOP correction parameters.
  975.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  976.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  977.      * For this reason, setting this parameter to false is restricted to {@link
  978.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
  979.      * </p>
  980.      * @param conventions IERS conventions to apply
  981.      * @param applyEOPCorr if true, EOP corrections are applied (here, nutation)
  982.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  983.      * @return the selected reference frame singleton.
  984.      * @exception OrekitException if data embedded in the library cannot be read
  985.      */
  986.     private static FactoryManagedFrame getTOD(final IERSConventions conventions,
  987.                                               final boolean applyEOPCorr,
  988.                                               final boolean simpleEOP)
  989.         throws OrekitException {

  990.         synchronized (FramesFactory.class) {

  991.             // try to find an already built frame
  992.             final Predefined factoryKey;
  993.             switch (conventions) {
  994.                 case IERS_1996 :
  995.                     factoryKey = applyEOPCorr ?
  996.                                  (simpleEOP ? Predefined.TOD_CONVENTIONS_1996_SIMPLE_EOP : Predefined.TOD_CONVENTIONS_1996_ACCURATE_EOP) :
  997.                                  Predefined.TOD_WITHOUT_EOP_CORRECTIONS;
  998.                     break;
  999.                 case IERS_2003 :
  1000.                     factoryKey = simpleEOP ?
  1001.                                  Predefined.TOD_CONVENTIONS_2003_SIMPLE_EOP :
  1002.                                      Predefined.TOD_CONVENTIONS_2003_ACCURATE_EOP;
  1003.                     break;
  1004.                 case IERS_2010 :
  1005.                     factoryKey = simpleEOP ?
  1006.                                  Predefined.TOD_CONVENTIONS_2010_SIMPLE_EOP :
  1007.                                  Predefined.TOD_CONVENTIONS_2010_ACCURATE_EOP;
  1008.                     break;
  1009.                 default :
  1010.                     // this should never happen
  1011.                     throw new OrekitInternalError(null);
  1012.             }
  1013.             final int interpolationPoints;
  1014.             final int pointsPerDay;
  1015.             if (applyEOPCorr) {
  1016.                 interpolationPoints = 6;
  1017.                 pointsPerDay        = 24;
  1018.             } else {
  1019.                 interpolationPoints = 6;
  1020.                 pointsPerDay        = 8;
  1021.             }
  1022.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  1023.             if (frame == null) {
  1024.                 // it's the first time we need this frame, build it and store it
  1025.                 final EOPHistory eopHistory = applyEOPCorr ? getEOPHistory(conventions, simpleEOP) : null;
  1026.                 final TransformProvider shifting =
  1027.                         new ShiftingTransformProvider(new TODProvider(conventions, eopHistory),
  1028.                                                       CartesianDerivativesFilter.USE_PVA,
  1029.                                                       AngularDerivativesFilter.USE_R,
  1030.                                                       interpolationPoints, Constants.JULIAN_DAY / pointsPerDay,
  1031.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  1032.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
  1033.                 frame = new FactoryManagedFrame(getMOD(conventions, applyEOPCorr), shifting, true, factoryKey);
  1034.                 FRAMES.put(factoryKey, frame);
  1035.             }

  1036.             return frame;

  1037.         }
  1038.     }

  1039.     /** Get the MOD reference frame.
  1040.      * <p>
  1041.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  1042.      * consistency with legacy software that don't handle EOP correction parameters.
  1043.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  1044.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  1045.      * For this reason, setting this parameter to false is restricted to {@link
  1046.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  1047.      * IERSConventions IERS conventions} cannot be freely chosen here.
  1048.      * </p>
  1049.      * @param applyEOPCorr if true, EOP corrections are applied (EME2000/GCRF bias compensation)
  1050.      * @return the selected reference frame singleton.
  1051.      * @exception OrekitException if data embedded in the library cannot be read
  1052.      */
  1053.     public static FactoryManagedFrame getMOD(final boolean applyEOPCorr)
  1054.         throws OrekitException {
  1055.         return getMOD(IERSConventions.IERS_1996, applyEOPCorr);
  1056.     }

  1057.     /** Get the MOD reference frame.
  1058.      * @param conventions IERS conventions to apply
  1059.      * @return the selected reference frame singleton.
  1060.      * @exception OrekitException if data embedded in the library cannot be read
  1061.      */
  1062.     public static FactoryManagedFrame getMOD(final IERSConventions conventions)
  1063.         throws OrekitException {
  1064.         return getMOD(conventions, true);
  1065.     }

  1066.     /** Get the MOD reference frame.
  1067.      * <p>
  1068.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  1069.      * consistency with legacy software that don't handle EOP correction parameters.
  1070.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  1071.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  1072.      * For this reason, setting this parameter to false is restricted to {@link
  1073.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
  1074.      * </p>
  1075.      * @param conventions IERS conventions to apply
  1076.      * @param applyEOPCorr if true, EOP corrections are applied (EME2000/GCRF bias compensation)
  1077.      * @return the selected reference frame singleton.
  1078.      * @exception OrekitException if data embedded in the library cannot be read
  1079.      */
  1080.     private static FactoryManagedFrame getMOD(final IERSConventions conventions, final boolean applyEOPCorr)
  1081.         throws OrekitException {

  1082.         synchronized (FramesFactory.class) {

  1083.             final Predefined factoryKey;
  1084.             final Frame parent;
  1085.             switch (conventions) {
  1086.                 case IERS_1996 :
  1087.                     factoryKey = applyEOPCorr ? Predefined.MOD_CONVENTIONS_1996 : Predefined.MOD_WITHOUT_EOP_CORRECTIONS;
  1088.                     parent     = applyEOPCorr ? FramesFactory.getGCRF() : FramesFactory.getEME2000();
  1089.                     break;
  1090.                 case IERS_2003 :
  1091.                     factoryKey = Predefined.MOD_CONVENTIONS_2003;
  1092.                     // in IERS conventions 2003, the precession angles zetaA, thetaA and zA
  1093.                     // from equation 33 are computed from EME2000, not from GCRF
  1094.                     parent     = FramesFactory.getEME2000();
  1095.                     break;
  1096.                 case IERS_2010 :
  1097.                     factoryKey = Predefined.MOD_CONVENTIONS_2010;
  1098.                     // precession angles epsilon0, psiA, omegaA and chiA
  1099.                     // from equations 5.39 and 5.40 are computed from EME2000
  1100.                     parent     = FramesFactory.getEME2000();
  1101.                     break;
  1102.                 default :
  1103.                     // this should never happen
  1104.                     throw new OrekitInternalError(null);
  1105.             }

  1106.             // try to find an already built frame
  1107.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  1108.             if (frame == null) {
  1109.                 // it's the first time we need this frame, build it and store it
  1110.                 frame = new FactoryManagedFrame(parent, new MODProvider(conventions), true, factoryKey);
  1111.                 FRAMES.put(factoryKey, frame);
  1112.             }

  1113.             return frame;

  1114.         }
  1115.     }

  1116.     /** Get the TEME reference frame.
  1117.      * <p>
  1118.      * The TEME frame is used for the SGP4 model in TLE propagation. This frame has <em>no</em>
  1119.      * official definition and there are some ambiguities about whether it should be used
  1120.      * as "of date" or "of epoch". This frame should therefore be used <em>only</em> for
  1121.      * TLE propagation and not for anything else, as recommended by the CCSDS Orbit Data Message
  1122.      * blue book.
  1123.      * </p>
  1124.      * @return the selected reference frame singleton.
  1125.      * @exception OrekitException if data embedded in the library cannot be read
  1126.      */
  1127.     public static FactoryManagedFrame getTEME() throws OrekitException {
  1128.         synchronized (FramesFactory.class) {

  1129.             // try to find an already built frame
  1130.             final Predefined factoryKey = Predefined.TEME;
  1131.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  1132.             if (frame == null) {
  1133.                 // it's the first time we need this frame, build it and store it
  1134.                 final Frame tod = getTOD(IERSConventions.IERS_1996, false, true);
  1135.                 final ShiftingTransformProvider todShifting =
  1136.                         (ShiftingTransformProvider) tod.getTransformProvider();
  1137.                 final TEMEProvider temeRaw = new TEMEProvider(IERSConventions.IERS_1996, null);
  1138.                 final TransformProvider temeShifting =
  1139.                         new ShiftingTransformProvider(temeRaw,
  1140.                                                       CartesianDerivativesFilter.USE_PVA,
  1141.                                                       AngularDerivativesFilter.USE_R,
  1142.                                                       todShifting.getGridPoints(), todShifting.getStep(),
  1143.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  1144.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);

  1145.                 frame = new FactoryManagedFrame(tod, temeShifting, true, factoryKey);
  1146.                 FRAMES.put(factoryKey, frame);
  1147.             }

  1148.             return frame;

  1149.         }
  1150.     }

  1151.     /** Get the transform between two frames, suppressing all interpolation.
  1152.      * <p>
  1153.      * This method is similar to {@link Frame#getTransformTo(Frame, AbsoluteDate)}
  1154.      * except it removes the performance enhancing interpolation features that are
  1155.      * added by the {@link FramesFactory factory} to some frames, in order to focus
  1156.      * on accuracy. The interpolation features are intended to save processing time
  1157.      * by avoiding doing some lengthy computation like nutation evaluation at each
  1158.      * time step and caching some results. This method can be used to avoid this,
  1159.      * when very high accuracy is desired, or for testing purposes. It should be
  1160.      * used with care, as doing the full computation is <em>really</em> costly for
  1161.      * some frames.
  1162.      * </p>
  1163.      * @param from frame from which transformation starts
  1164.      * @param to frame to which transformation ends
  1165.      * @param date date of the transform
  1166.      * @return transform between the two frames, avoiding interpolation
  1167.      * @throws OrekitException if transform cannot be computed at this date
  1168.      */
  1169.     public static Transform getNonInterpolatingTransform(final Frame from, final Frame to,
  1170.                                                          final AbsoluteDate date)
  1171.         throws OrekitException {

  1172.         // common ancestor to both frames in the frames tree
  1173.         Frame currentF = from.getDepth() > to.getDepth() ? from.getAncestor(from.getDepth() - to.getDepth()) : from;
  1174.         Frame currentT = from.getDepth() > to.getDepth() ? to : to.getAncestor(to.getDepth() - from.getDepth());
  1175.         while (currentF != currentT) {
  1176.             currentF = currentF.getParent();
  1177.             currentT = currentT.getParent();
  1178.         }
  1179.         final Frame common = currentF;

  1180.         // transform from common to origin
  1181.         Transform commonToOrigin = Transform.IDENTITY;
  1182.         for (Frame frame = from; frame != common; frame = frame.getParent()) {
  1183.             commonToOrigin = new Transform(date,
  1184.                                              peel(frame.getTransformProvider()).getTransform(date),
  1185.                                              commonToOrigin);
  1186.         }

  1187.         // transform from destination up to common
  1188.         Transform commonToDestination = Transform.IDENTITY;
  1189.         for (Frame frame = to; frame != common; frame = frame.getParent()) {
  1190.             commonToDestination = new Transform(date,
  1191.                                                 peel(frame.getTransformProvider()).getTransform(date),
  1192.                                                 commonToDestination);
  1193.         }

  1194.         // transform from origin to destination via common
  1195.         return new Transform(date, commonToOrigin.getInverse(), commonToDestination);

  1196.     }

  1197.     /** Get the transform between two frames, suppressing all interpolation.
  1198.      * <p>
  1199.      * This method is similar to {@link Frame#getTransformTo(Frame, AbsoluteDate)}
  1200.      * except it removes the performance enhancing interpolation features that are
  1201.      * added by the {@link FramesFactory factory} to some frames, in order to focus
  1202.      * on accuracy. The interpolation features are intended to save processing time
  1203.      * by avoiding doing some lengthy computation like nutation evaluation at each
  1204.      * time step and caching some results. This method can be used to avoid this,
  1205.      * when very high accuracy is desired, or for testing purposes. It should be
  1206.      * used with care, as doing the full computation is <em>really</em> costly for
  1207.      * some frames.
  1208.      * </p>
  1209.      * @param from frame from which transformation starts
  1210.      * @param to frame to which transformation ends
  1211.      * @param date date of the transform
  1212.      * @param <T> type of the field elements
  1213.      * @return transform between the two frames, avoiding interpolation
  1214.      * @throws OrekitException if transform cannot be computed at this date
  1215.      * @since 9.0
  1216.      */
  1217.     public static <T extends RealFieldElement<T>> FieldTransform<T> getNonInterpolatingTransform(final Frame from, final Frame to,
  1218.                                                                                                  final FieldAbsoluteDate<T> date)
  1219.         throws OrekitException {

  1220.         // common ancestor to both frames in the frames tree
  1221.         Frame currentF = from.getDepth() > to.getDepth() ? from.getAncestor(from.getDepth() - to.getDepth()) : from;
  1222.         Frame currentT = from.getDepth() > to.getDepth() ? to : to.getAncestor(to.getDepth() - from.getDepth());
  1223.         while (currentF != currentT) {
  1224.             currentF = currentF.getParent();
  1225.             currentT = currentT.getParent();
  1226.         }
  1227.         final Frame common = currentF;

  1228.         // transform from common to origin
  1229.         FieldTransform<T> commonToOrigin = FieldTransform.getIdentity(date.getField());
  1230.         for (Frame frame = from; frame != common; frame = frame.getParent()) {
  1231.             commonToOrigin = new FieldTransform<>(date,
  1232.                                                    peel(frame.getTransformProvider()).getTransform(date),
  1233.                                                    commonToOrigin);
  1234.         }

  1235.         // transform from destination up to common
  1236.         FieldTransform<T> commonToDestination = FieldTransform.getIdentity(date.getField());
  1237.         for (Frame frame = to; frame != common; frame = frame.getParent()) {
  1238.             commonToDestination = new FieldTransform<>(date,
  1239.                                                        peel(frame.getTransformProvider()).getTransform(date),
  1240.                                                        commonToDestination);
  1241.         }

  1242.         // transform from origin to destination via common
  1243.         return new FieldTransform<>(date, commonToOrigin.getInverse(), commonToDestination);

  1244.     }

  1245.     /** Retrieve EOP from a frame hierarchy.
  1246.      * <p>
  1247.      * The frame hierarchy tree is walked from specified frame up to root
  1248.      * goind though parent frames, and the providers are checked to see if they
  1249.      * reference EOP history.the first EOP history found is returned.
  1250.      * </p>
  1251.      * @param start frame from which to start search, will typically be some
  1252.      * Earth related frame, like a topocentric frame or an ITRF frame
  1253.      * @return EOP history found while walking the frames tree, or null if
  1254.      * no EOP history is found
  1255.      * @since 9.1
  1256.      */
  1257.     public static EOPHistory findEOP(final Frame start) {

  1258.         for (Frame frame = start; frame != null; frame = frame.getParent()) {

  1259.             TransformProvider peeled = frame.getTransformProvider();

  1260.             boolean peeling = true;
  1261.             while (peeling) {
  1262.                 if (peeled instanceof InterpolatingTransformProvider) {
  1263.                     peeled = ((InterpolatingTransformProvider) peeled).getRawProvider();
  1264.                 } else if (peeled instanceof ShiftingTransformProvider) {
  1265.                     peeled = ((ShiftingTransformProvider) peeled).getRawProvider();
  1266.                 } else if (peeled instanceof EOPBasedTransformProvider &&
  1267.                            ((EOPBasedTransformProvider) peeled).getEOPHistory() != null) {
  1268.                     return ((EOPBasedTransformProvider) peeled).getEOPHistory();
  1269.                 } else {
  1270.                     peeling = false;
  1271.                 }
  1272.             }

  1273.         }

  1274.         // no history found
  1275.         return null;

  1276.     }

  1277.     /** Peel interpolation and shifting from a transform provider.
  1278.      * @param provider transform provider to peel
  1279.      * @return peeled transform provider
  1280.      * @exception OrekitException if EOP cannot be retrieved
  1281.      */
  1282.     private static TransformProvider peel(final TransformProvider provider)
  1283.         throws OrekitException {

  1284.         TransformProvider peeled = provider;

  1285.         boolean peeling = true;
  1286.         while (peeling) {
  1287.             if (peeled instanceof InterpolatingTransformProvider) {
  1288.                 peeled = ((InterpolatingTransformProvider) peeled).getRawProvider();
  1289.             } else if (peeled instanceof ShiftingTransformProvider) {
  1290.                 peeled = ((ShiftingTransformProvider) peeled).getRawProvider();
  1291.             } else if (peeled instanceof EOPBasedTransformProvider &&
  1292.                        ((EOPBasedTransformProvider) peeled).getEOPHistory() != null &&
  1293.                        ((EOPBasedTransformProvider) peeled).getEOPHistory().usesInterpolation()) {
  1294.                 peeled = ((EOPBasedTransformProvider) peeled).getNonInterpolatingProvider();
  1295.             } else {
  1296.                 peeling = false;
  1297.             }
  1298.         }

  1299.         return peeled;

  1300.     }

  1301.     /** Local class for different ITRF versions keys.
  1302.      * @since 9.2
  1303.      */
  1304.     private static class ITRFKey implements Serializable {

  1305.         /** Serialized UID. */
  1306.         private static final long serialVersionUID = 20180412L;

  1307.         /** ITRF version. */
  1308.         private final ITRFVersion version;

  1309.         /** IERS conventions to apply. */
  1310.         private final IERSConventions conventions;

  1311.         /** Tidal effects flag. */
  1312.         private final boolean simpleEOP;

  1313.         /** Simple constructor.
  1314.          * @param version ITRF version
  1315.          * @param conventions IERS conventions to apply
  1316.          * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  1317.          */
  1318.         ITRFKey(final ITRFVersion version, final IERSConventions conventions, final boolean simpleEOP) {
  1319.             this.version     = version;
  1320.             this.conventions = conventions;
  1321.             this.simpleEOP   = simpleEOP;
  1322.         }

  1323.         /** {@inheritDoc} */
  1324.         @Override
  1325.         public int hashCode() {
  1326.             return (version.ordinal()     << 5) +
  1327.                    (conventions.ordinal() << 1) +
  1328.                    (simpleEOP ? 0 : 1);
  1329.         }

  1330.         /** {@inheritDoc} */
  1331.         @Override
  1332.         public boolean equals(final Object other) {

  1333.             if (this == other) {
  1334.                 return true;
  1335.             }

  1336.             if (other instanceof ITRFKey) {
  1337.                 final ITRFKey key = (ITRFKey) other;
  1338.                 return version     == key.version     &&
  1339.                        conventions == key.conventions &&
  1340.                        simpleEOP   == key.simpleEOP;
  1341.             }

  1342.             return false;
  1343.         }

  1344.     }

  1345. }