FramesFactory.java

  1. /* Copyright 2002-2017 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.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.SortedSet;
  23. import java.util.TreeSet;

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


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

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

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

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

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

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

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

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

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

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

  177.     /** Predefined frames. */
  178.     private static transient Map<Predefined, FactoryManagedFrame> FRAMES =
  179.         new HashMap<Predefined, FactoryManagedFrame>();

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

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

  185.     /** Private constructor.
  186.      * <p>This class is a utility class, it should neither have a public
  187.      * nor a default constructor. This private constructor prevents
  188.      * the compiler from generating one automatically.</p>
  189.      */
  190.     private FramesFactory() {
  191.     }

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

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

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

  317.     /** Clear loaders for Earth Orientation Parameters history.
  318.      * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader)
  319.      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String)
  320.      */
  321.     public static void clearEOPHistoryLoaders() {
  322.         synchronized (EOP_HISTORY_LOADERS) {
  323.             EOP_HISTORY_LOADERS.clear();
  324.         }
  325.     }

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

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

  363.         synchronized (EOP_HISTORY_LOADERS) {

  364.             if (EOP_HISTORY_LOADERS.isEmpty()) {
  365.                 // set up using default loaders
  366.                 addDefaultEOP2000HistoryLoaders(null, null, null, null, null);
  367.                 addDefaultEOP1980HistoryLoaders(null, null, null, null, null);
  368.             }

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

  372.             // try to load canonical data if available
  373.             if (EOP_HISTORY_LOADERS.containsKey(conventions)) {
  374.                 for (final EOPHistoryLoader loader : EOP_HISTORY_LOADERS.get(conventions)) {
  375.                     try {
  376.                         loader.fillHistory(conventions.getNutationCorrectionConverter(), data);
  377.                     } catch (OrekitException oe) {
  378.                         pendingException = oe;
  379.                     }
  380.                 }
  381.             }

  382.             if (data.isEmpty() && pendingException != null) {
  383.                 throw pendingException;
  384.             }

  385.             final EOPHistory history = new EOPHistory(conventions, data, simpleEOP);
  386.             history.checkEOPContinuity(EOP_CONTINUITY_THRESHOLD);
  387.             return history;

  388.         }

  389.     }

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

  503.     /** Get the unique GCRF frame.
  504.      * <p>The GCRF frame is the root frame in the frame tree.</p>
  505.      * @return the unique instance of the GCRF frame
  506.      */
  507.     public static Frame getGCRF() {
  508.         return Frame.getRoot();
  509.     }

  510.     /** Get the unique ICRF frame.
  511.      * <p>The ICRF frame is centered at solar system barycenter and aligned
  512.      * with EME2000.</p>
  513.      * @return the unique instance of the ICRF frame
  514.      * @exception OrekitException if solar system ephemerides cannot be loaded
  515.      */
  516.     public static Frame getICRF() throws OrekitException {
  517.         return CelestialBodyFactory.getSolarSystemBarycenter().getInertiallyOrientedFrame();
  518.     }

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

  532.             final Predefined factoryKey;
  533.             switch (conventions) {
  534.                 case IERS_1996 :
  535.                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_1996;
  536.                     break;
  537.                 case IERS_2003 :
  538.                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_2003;
  539.                     break;
  540.                 case IERS_2010 :
  541.                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_2010;
  542.                     break;
  543.                 default :
  544.                     // this should never happen
  545.                     throw new OrekitInternalError(null);
  546.             }
  547.             final Frame parent = getMOD(conventions);

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

  550.             if (frame == null) {
  551.                 // it's the first time we need this frame, build it and store it
  552.                 frame = new FactoryManagedFrame(parent, new EclipticProvider(conventions),
  553.                                                 true, factoryKey);
  554.                 FRAMES.put(factoryKey, frame);
  555.             }

  556.             return frame;

  557.         }
  558.     }

  559.     /** Get the unique EME2000 frame.
  560.      * <p>The EME2000 frame is also called the J2000 frame.
  561.      * The former denomination is preferred in Orekit.</p>
  562.      * @return the unique instance of the EME2000 frame
  563.      */
  564.     public static FactoryManagedFrame getEME2000() {
  565.         synchronized (FramesFactory.class) {

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

  568.             if (frame == null) {
  569.                 // it's the first time we need this frame, build it and store it
  570.                 frame = new FactoryManagedFrame(getGCRF(), new EME2000Provider(), true, Predefined.EME2000);
  571.                 FRAMES.put(Predefined.EME2000, frame);
  572.             }

  573.             return frame;

  574.         }
  575.     }

  576.     /** Get the ITRF2008 reference frame, using IERS 2010 conventions.
  577.      * @param conventions IERS conventions to apply
  578.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  579.      * @return the selected reference frame singleton.
  580.      * @exception OrekitException if data embedded in the library cannot be read
  581.      * @since 6.1
  582.      */
  583.     public static FactoryManagedFrame getITRF(final IERSConventions conventions,
  584.                                               final boolean simpleEOP)
  585.         throws OrekitException {
  586.         synchronized (FramesFactory.class) {

  587.             // try to find an already built frame
  588.             final Predefined factoryKey;
  589.             switch (conventions) {
  590.                 case IERS_1996 :
  591.                     factoryKey = simpleEOP ?
  592.                                  Predefined.ITRF_CIO_CONV_1996_SIMPLE_EOP :
  593.                                  Predefined.ITRF_CIO_CONV_1996_ACCURATE_EOP;
  594.                     break;
  595.                 case IERS_2003 :
  596.                     factoryKey = simpleEOP ?
  597.                                  Predefined.ITRF_CIO_CONV_2003_SIMPLE_EOP :
  598.                                  Predefined.ITRF_CIO_CONV_2003_ACCURATE_EOP;
  599.                     break;
  600.                 case IERS_2010 :
  601.                     factoryKey = simpleEOP ?
  602.                                  Predefined.ITRF_CIO_CONV_2010_SIMPLE_EOP :
  603.                                  Predefined.ITRF_CIO_CONV_2010_ACCURATE_EOP;
  604.                     break;
  605.                 default :
  606.                     // this should never happen
  607.                     throw new OrekitInternalError(null);
  608.             }
  609.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  610.             if (frame == null) {
  611.                 // it's the first time we need this frame, build it and store it
  612.                 final Frame tirfFrame = getTIRF(conventions, simpleEOP);
  613.                 final TIRFProvider tirfProvider = (TIRFProvider) tirfFrame.getTransformProvider();
  614.                 frame = new FactoryManagedFrame(tirfFrame,
  615.                                                 new ITRFProvider(tirfProvider.getEOPHistory()),
  616.                                                 false, factoryKey);
  617.                 FRAMES.put(factoryKey, frame);
  618.             }

  619.             return frame;

  620.         }
  621.     }

  622.     /** Get the TIRF reference frame, ignoring tidal effects.
  623.      * @param conventions IERS conventions to apply
  624.      * @return the selected reference frame singleton.
  625.      * @exception OrekitException if the precession-nutation model data embedded in the
  626.      * library cannot be read.
  627.      */
  628.     public static FactoryManagedFrame getTIRF(final IERSConventions conventions) throws OrekitException {
  629.         return getTIRF(conventions, true);
  630.     }

  631.     /** Get the TIRF reference frame.
  632.      * @param conventions IERS conventions to apply
  633.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  634.      * @return the selected reference frame singleton.
  635.      * @exception OrekitException if the precession-nutation model data embedded in the
  636.      * library cannot be read.
  637.      * @since 6.1
  638.      */
  639.     public static FactoryManagedFrame getTIRF(final IERSConventions conventions,
  640.                                               final boolean simpleEOP)
  641.         throws OrekitException {
  642.         synchronized (FramesFactory.class) {

  643.             // try to find an already built frame
  644.             final Predefined factoryKey;
  645.             switch (conventions) {
  646.                 case IERS_1996 :
  647.                     factoryKey = simpleEOP ?
  648.                                  Predefined.TIRF_CONVENTIONS_1996_SIMPLE_EOP :
  649.                                  Predefined.TIRF_CONVENTIONS_1996_ACCURATE_EOP;
  650.                     break;
  651.                 case IERS_2003 :
  652.                     factoryKey = simpleEOP ?
  653.                                  Predefined.TIRF_CONVENTIONS_2003_SIMPLE_EOP :
  654.                                  Predefined.TIRF_CONVENTIONS_2003_ACCURATE_EOP;
  655.                     break;
  656.                 case IERS_2010 :
  657.                     factoryKey = simpleEOP ?
  658.                                  Predefined.TIRF_CONVENTIONS_2010_SIMPLE_EOP :
  659.                                  Predefined.TIRF_CONVENTIONS_2010_ACCURATE_EOP;
  660.                     break;
  661.                 default :
  662.                     // this should never happen
  663.                     throw new OrekitInternalError(null);
  664.             }
  665.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  666.             if (frame == null) {
  667.                 // it's the first time we need this frame, build it and store it
  668.                 final Frame cirf = getCIRF(conventions, simpleEOP);
  669.                 final ShiftingTransformProvider cirfInterpolating =
  670.                         (ShiftingTransformProvider) cirf.getTransformProvider();
  671.                 final CIRFProvider cirfRaw = (CIRFProvider) cirfInterpolating.getRawProvider();
  672.                 final EOPHistory eopHistory = cirfRaw.getEOPHistory();
  673.                 frame = new FactoryManagedFrame(cirf, new TIRFProvider(eopHistory), false, factoryKey);
  674.                 FRAMES.put(factoryKey, frame);
  675.             }

  676.             return frame;

  677.         }
  678.     }

  679.     /** Get the CIRF2000 reference frame.
  680.      * @param conventions IERS conventions to apply
  681.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  682.      * @return the selected reference frame singleton.
  683.      * @exception OrekitException if the precession-nutation model data embedded in the
  684.      * library cannot be read.
  685.      */
  686.     public static FactoryManagedFrame getCIRF(final IERSConventions conventions,
  687.                                               final boolean simpleEOP)
  688.         throws OrekitException {
  689.         synchronized (FramesFactory.class) {

  690.             // try to find an already built frame
  691.             final Predefined factoryKey;
  692.             switch (conventions) {
  693.                 case IERS_1996 :
  694.                     factoryKey = simpleEOP ?
  695.                                  Predefined.CIRF_CONVENTIONS_1996_SIMPLE_EOP :
  696.                                  Predefined.CIRF_CONVENTIONS_1996_ACCURATE_EOP;
  697.                     break;
  698.                 case IERS_2003 :
  699.                     factoryKey = simpleEOP ?
  700.                                  Predefined.CIRF_CONVENTIONS_2003_SIMPLE_EOP :
  701.                                  Predefined.CIRF_CONVENTIONS_2003_ACCURATE_EOP;
  702.                     break;
  703.                 case IERS_2010 :
  704.                     factoryKey = simpleEOP ?
  705.                                  Predefined.CIRF_CONVENTIONS_2010_SIMPLE_EOP :
  706.                                  Predefined.CIRF_CONVENTIONS_2010_ACCURATE_EOP;
  707.                     break;
  708.                 default :
  709.                     // this should never happen
  710.                     throw new OrekitInternalError(null);
  711.             }
  712.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  713.             if (frame == null) {
  714.                 // it's the first time we need this frame, build it and store it
  715.                 final EOPHistory eopHistory = FramesFactory.getEOPHistory(conventions, simpleEOP);
  716.                 final TransformProvider shifting =
  717.                         new ShiftingTransformProvider(new CIRFProvider(eopHistory),
  718.                                                       CartesianDerivativesFilter.USE_PVA,
  719.                                                       AngularDerivativesFilter.USE_R,
  720.                                                       AbsoluteDate.PAST_INFINITY, AbsoluteDate.FUTURE_INFINITY,
  721.                                                       6, Constants.JULIAN_DAY / 24,
  722.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  723.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
  724.                 frame = new FactoryManagedFrame(getGCRF(), shifting, true, factoryKey);
  725.                 FRAMES.put(factoryKey, frame);
  726.             }

  727.             return frame;

  728.         }
  729.     }

  730.     /** Get the VEIS 1950 reference frame.
  731.      * <p>Its parent frame is the GTOD frame with IERS 1996 conventions without EOP corrections.<p>
  732.      * @return the selected reference frame singleton.
  733.      * @exception OrekitException if data embedded in the library cannot be read
  734.      */
  735.     public static FactoryManagedFrame getVeis1950() throws OrekitException {
  736.         synchronized (FramesFactory.class) {

  737.             // try to find an already built frame
  738.             final Predefined factoryKey = Predefined.VEIS_1950;
  739.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  740.             if (frame == null) {
  741.                 // it's the first time we need this frame, build it and store it
  742.                 frame = new FactoryManagedFrame(FramesFactory.getGTOD(IERSConventions.IERS_1996, false, true),
  743.                                                 new VEISProvider(), true, factoryKey);
  744.                 FRAMES.put(factoryKey, frame);
  745.             }

  746.             return frame;

  747.         }
  748.     }

  749.     /** Get the equinox-based ITRF reference frame.
  750.      * @param conventions IERS conventions to apply
  751.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  752.      * @return the selected reference frame singleton.
  753.      * @exception OrekitException if data embedded in the library cannot be read
  754.      * @since 6.1
  755.      */
  756.     public static FactoryManagedFrame getITRFEquinox(final IERSConventions conventions,
  757.                                                      final boolean simpleEOP)
  758.         throws OrekitException {
  759.         synchronized (FramesFactory.class) {

  760.             // try to find an already built frame
  761.             final Predefined factoryKey;
  762.             switch (conventions) {
  763.                 case IERS_1996 :
  764.                     factoryKey = simpleEOP ?
  765.                                  Predefined.ITRF_EQUINOX_CONV_1996_SIMPLE_EOP :
  766.                                  Predefined.ITRF_EQUINOX_CONV_1996_ACCURATE_EOP;
  767.                     break;
  768.                 case IERS_2003 :
  769.                     factoryKey = simpleEOP ?
  770.                                  Predefined.ITRF_EQUINOX_CONV_2003_SIMPLE_EOP :
  771.                                  Predefined.ITRF_EQUINOX_CONV_2003_ACCURATE_EOP;
  772.                     break;
  773.                 case IERS_2010 :
  774.                     factoryKey = simpleEOP ?
  775.                                  Predefined.ITRF_EQUINOX_CONV_2010_SIMPLE_EOP :
  776.                                  Predefined.ITRF_EQUINOX_CONV_2010_ACCURATE_EOP;
  777.                     break;
  778.                 default :
  779.                     // this should never happen
  780.                     throw new OrekitInternalError(null);
  781.             }
  782.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  783.             if (frame == null) {
  784.                 // it's the first time we need this frame, build it and store it
  785.                 final Frame gtod = getGTOD(conventions, true, simpleEOP);
  786.                 final ShiftingTransformProvider gtodShifting =
  787.                         (ShiftingTransformProvider) gtod.getTransformProvider();
  788.                 final GTODProvider gtodRaw    = (GTODProvider) gtodShifting.getRawProvider();
  789.                 final EOPHistory   eopHistory = gtodRaw.getEOPHistory();
  790.                 frame = new FactoryManagedFrame(gtod, new ITRFProvider(eopHistory), false, factoryKey);
  791.                 FRAMES.put(factoryKey, frame);
  792.             }

  793.             return frame;

  794.         }
  795.     }

  796.     /** Get the GTOD reference frame.
  797.      * <p>
  798.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  799.      * consistency with legacy software that don't handle EOP correction parameters.
  800.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  801.      * (order of magnitudes for errors might be above 250m in LEO and 1400m in GEO).
  802.      * For this reason, setting this parameter to false is restricted to {@link
  803.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  804.      * IERSConventions IERS conventions} cannot be freely chosen here.
  805.      * </p>
  806.      * @param applyEOPCorr if true, EOP corrections are applied (here, dut1 and lod)
  807.      * @return the selected reference frame singleton.
  808.      * @exception OrekitException if data embedded in the library cannot be read
  809.      */
  810.     public static FactoryManagedFrame getGTOD(final boolean applyEOPCorr) throws OrekitException {
  811.         return getGTOD(IERSConventions.IERS_1996, applyEOPCorr, true);
  812.     }

  813.     /** Get the GTOD reference frame.
  814.      * @param conventions IERS conventions to apply
  815.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  816.      * @return the selected reference frame singleton.
  817.      * @exception OrekitException if data embedded in the library cannot be read
  818.      */
  819.     public static FactoryManagedFrame getGTOD(final IERSConventions conventions,
  820.                                               final boolean simpleEOP)
  821.         throws OrekitException {
  822.         return getGTOD(conventions, true, simpleEOP);
  823.     }

  824.     /** Get the GTOD reference frame.
  825.      * <p>
  826.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  827.      * consistency with legacy software that don't handle EOP correction parameters.
  828.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  829.      * (order of magnitudes for errors might be above 250m in LEO and 1400m in GEO).
  830.      * For this reason, setting this parameter to false is restricted to {@link
  831.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
  832.      * </p>
  833.      * @param conventions IERS conventions to apply
  834.      * @param applyEOPCorr if true, EOP corrections are applied (here, dut1 and lod)
  835.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  836.      * @return the selected reference frame singleton.
  837.      * @exception OrekitException if data embedded in the library cannot be read
  838.      */
  839.     private static FactoryManagedFrame getGTOD(final IERSConventions conventions,
  840.                                                final boolean applyEOPCorr,
  841.                                                final boolean simpleEOP)
  842.         throws OrekitException {

  843.         synchronized (FramesFactory.class) {

  844.             // try to find an already built frame
  845.             final Predefined factoryKey;
  846.             switch (conventions) {
  847.                 case IERS_1996 :
  848.                     factoryKey = applyEOPCorr ?
  849.                                  (simpleEOP ? Predefined.GTOD_CONVENTIONS_1996_SIMPLE_EOP : Predefined.GTOD_CONVENTIONS_1996_ACCURATE_EOP) :
  850.                                  Predefined.GTOD_WITHOUT_EOP_CORRECTIONS;
  851.                     break;
  852.                 case IERS_2003 :
  853.                     factoryKey = simpleEOP ?
  854.                                  Predefined.GTOD_CONVENTIONS_2003_SIMPLE_EOP :
  855.                                  Predefined.GTOD_CONVENTIONS_2003_ACCURATE_EOP;
  856.                     break;
  857.                 case IERS_2010 :
  858.                     factoryKey = simpleEOP ? Predefined.GTOD_CONVENTIONS_2010_SIMPLE_EOP :
  859.                                              Predefined.GTOD_CONVENTIONS_2010_ACCURATE_EOP;
  860.                     break;
  861.                 default :
  862.                     // this should never happen
  863.                     throw new OrekitInternalError(null);
  864.             }
  865.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  866.             if (frame == null) {
  867.                 // it's the first time we need this frame, build it and store it
  868.                 final Frame tod = getTOD(conventions, applyEOPCorr, simpleEOP);
  869.                 final ShiftingTransformProvider todInterpolating =
  870.                         (ShiftingTransformProvider) tod.getTransformProvider();
  871.                 final TODProvider       todRaw     = (TODProvider) todInterpolating.getRawProvider();
  872.                 final EOPHistory        eopHistory = todRaw.getEOPHistory();
  873.                 final GTODProvider      gtodRaw    = new GTODProvider(conventions, eopHistory);
  874.                 final TransformProvider gtodShifting =
  875.                         new ShiftingTransformProvider(gtodRaw,
  876.                                                       CartesianDerivativesFilter.USE_PVA,
  877.                                                       AngularDerivativesFilter.USE_R,
  878.                                                       AbsoluteDate.PAST_INFINITY, AbsoluteDate.FUTURE_INFINITY,
  879.                                                       todInterpolating.getGridPoints(), todInterpolating.getStep(),
  880.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  881.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
  882.                 frame = new FactoryManagedFrame(tod, gtodShifting, false, factoryKey);
  883.                 FRAMES.put(factoryKey, frame);
  884.             }

  885.             return frame;

  886.         }
  887.     }

  888.     /** Get the TOD reference frame.
  889.      * <p>
  890.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  891.      * consistency with legacy software that don't handle EOP correction parameters.
  892.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  893.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  894.      * For this reason, setting this parameter to false is restricted to {@link
  895.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  896.      * IERSConventions IERS conventions} cannot be freely chosen here.
  897.      * </p>
  898.      * @param applyEOPCorr if true, EOP corrections are applied (here, nutation)
  899.      * @return the selected reference frame singleton.
  900.      * @exception OrekitException if data embedded in the library cannot be read
  901.      */
  902.     public static FactoryManagedFrame getTOD(final boolean applyEOPCorr)
  903.         throws OrekitException {
  904.         return getTOD(IERSConventions.IERS_1996, applyEOPCorr, false);
  905.     }

  906.     /** Get the TOD reference frame.
  907.      * @param conventions IERS conventions to apply
  908.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  909.      * @return the selected reference frame singleton.
  910.      * @exception OrekitException if data embedded in the library cannot be read
  911.      */
  912.     public static FactoryManagedFrame getTOD(final IERSConventions conventions,
  913.                                              final boolean simpleEOP)
  914.         throws OrekitException {
  915.         return getTOD(conventions, true, simpleEOP);
  916.     }

  917.     /** Get the TOD reference frame.
  918.      * <p>
  919.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  920.      * consistency with legacy software that don't handle EOP correction parameters.
  921.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  922.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  923.      * For this reason, setting this parameter to false is restricted to {@link
  924.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
  925.      * </p>
  926.      * @param conventions IERS conventions to apply
  927.      * @param applyEOPCorr if true, EOP corrections are applied (here, nutation)
  928.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  929.      * @return the selected reference frame singleton.
  930.      * @exception OrekitException if data embedded in the library cannot be read
  931.      */
  932.     private static FactoryManagedFrame getTOD(final IERSConventions conventions,
  933.                                               final boolean applyEOPCorr,
  934.                                               final boolean simpleEOP)
  935.         throws OrekitException {

  936.         synchronized (FramesFactory.class) {

  937.             // try to find an already built frame
  938.             final Predefined factoryKey;
  939.             switch (conventions) {
  940.                 case IERS_1996 :
  941.                     factoryKey = applyEOPCorr ?
  942.                                  (simpleEOP ? Predefined.TOD_CONVENTIONS_1996_SIMPLE_EOP : Predefined.TOD_CONVENTIONS_1996_ACCURATE_EOP) :
  943.                                  Predefined.TOD_WITHOUT_EOP_CORRECTIONS;
  944.                     break;
  945.                 case IERS_2003 :
  946.                     factoryKey = simpleEOP ?
  947.                                  Predefined.TOD_CONVENTIONS_2003_SIMPLE_EOP :
  948.                                      Predefined.TOD_CONVENTIONS_2003_ACCURATE_EOP;
  949.                     break;
  950.                 case IERS_2010 :
  951.                     factoryKey = simpleEOP ?
  952.                                  Predefined.TOD_CONVENTIONS_2010_SIMPLE_EOP :
  953.                                  Predefined.TOD_CONVENTIONS_2010_ACCURATE_EOP;
  954.                     break;
  955.                 default :
  956.                     // this should never happen
  957.                     throw new OrekitInternalError(null);
  958.             }
  959.             final int interpolationPoints;
  960.             final int pointsPerDay;
  961.             if (applyEOPCorr) {
  962.                 interpolationPoints = 6;
  963.                 pointsPerDay        = 24;
  964.             } else {
  965.                 interpolationPoints = 6;
  966.                 pointsPerDay        = 8;
  967.             }
  968.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  969.             if (frame == null) {
  970.                 // it's the first time we need this frame, build it and store it
  971.                 final EOPHistory eopHistory = applyEOPCorr ? getEOPHistory(conventions, simpleEOP) : null;
  972.                 final TransformProvider shifting =
  973.                         new ShiftingTransformProvider(new TODProvider(conventions, eopHistory),
  974.                                                       CartesianDerivativesFilter.USE_PVA,
  975.                                                       AngularDerivativesFilter.USE_R,
  976.                                                       AbsoluteDate.PAST_INFINITY, AbsoluteDate.FUTURE_INFINITY,
  977.                                                       interpolationPoints, Constants.JULIAN_DAY / pointsPerDay,
  978.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  979.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
  980.                 frame = new FactoryManagedFrame(getMOD(conventions, applyEOPCorr), shifting, true, factoryKey);
  981.                 FRAMES.put(factoryKey, frame);
  982.             }

  983.             return frame;

  984.         }
  985.     }

  986.     /** Get the MOD reference frame.
  987.      * <p>
  988.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  989.      * consistency with legacy software that don't handle EOP correction parameters.
  990.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  991.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  992.      * For this reason, setting this parameter to false is restricted to {@link
  993.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  994.      * IERSConventions IERS conventions} cannot be freely chosen here.
  995.      * </p>
  996.      * @param applyEOPCorr if true, EOP corrections are applied (EME2000/GCRF bias compensation)
  997.      * @return the selected reference frame singleton.
  998.      * @exception OrekitException if data embedded in the library cannot be read
  999.      */
  1000.     public static FactoryManagedFrame getMOD(final boolean applyEOPCorr)
  1001.         throws OrekitException {
  1002.         return getMOD(IERSConventions.IERS_1996, applyEOPCorr);
  1003.     }

  1004.     /** Get the MOD reference frame.
  1005.      * @param conventions IERS conventions to apply
  1006.      * @return the selected reference frame singleton.
  1007.      * @exception OrekitException if data embedded in the library cannot be read
  1008.      */
  1009.     public static FactoryManagedFrame getMOD(final IERSConventions conventions)
  1010.         throws OrekitException {
  1011.         return getMOD(conventions, true);
  1012.     }

  1013.     /** Get the MOD reference frame.
  1014.      * <p>
  1015.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  1016.      * consistency with legacy software that don't handle EOP correction parameters.
  1017.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  1018.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  1019.      * For this reason, setting this parameter to false is restricted to {@link
  1020.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
  1021.      * </p>
  1022.      * @param conventions IERS conventions to apply
  1023.      * @param applyEOPCorr if true, EOP corrections are applied (EME2000/GCRF bias compensation)
  1024.      * @return the selected reference frame singleton.
  1025.      * @exception OrekitException if data embedded in the library cannot be read
  1026.      */
  1027.     private static FactoryManagedFrame getMOD(final IERSConventions conventions, final boolean applyEOPCorr)
  1028.         throws OrekitException {

  1029.         synchronized (FramesFactory.class) {

  1030.             final Predefined factoryKey;
  1031.             final Frame parent;
  1032.             switch (conventions) {
  1033.                 case IERS_1996 :
  1034.                     factoryKey = applyEOPCorr ? Predefined.MOD_CONVENTIONS_1996 : Predefined.MOD_WITHOUT_EOP_CORRECTIONS;
  1035.                     parent     = applyEOPCorr ? FramesFactory.getGCRF() : FramesFactory.getEME2000();
  1036.                     break;
  1037.                 case IERS_2003 :
  1038.                     factoryKey = Predefined.MOD_CONVENTIONS_2003;
  1039.                     // in IERS conventions 2003, the precession angles zetaA, thetaA and zA
  1040.                     // from equation 33 are computed from EME2000, not from GCRF
  1041.                     parent     = FramesFactory.getEME2000();
  1042.                     break;
  1043.                 case IERS_2010 :
  1044.                     factoryKey = Predefined.MOD_CONVENTIONS_2010;
  1045.                     // precession angles epsilon0, psiA, omegaA and chiA
  1046.                     // from equations 5.39 and 5.40 are computed from EME2000
  1047.                     parent     = FramesFactory.getEME2000();
  1048.                     break;
  1049.                 default :
  1050.                     // this should never happen
  1051.                     throw new OrekitInternalError(null);
  1052.             }

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

  1055.             if (frame == null) {
  1056.                 // it's the first time we need this frame, build it and store it
  1057.                 frame = new FactoryManagedFrame(parent, new MODProvider(conventions), true, factoryKey);
  1058.                 FRAMES.put(factoryKey, frame);
  1059.             }

  1060.             return frame;

  1061.         }
  1062.     }

  1063.     /** Get the TEME reference frame.
  1064.      * <p>
  1065.      * The TEME frame is used for the SGP4 model in TLE propagation. This frame has <em>no</em>
  1066.      * official definition and there are some ambiguities about whether it should be used
  1067.      * as "of date" or "of epoch". This frame should therefore be used <em>only</em> for
  1068.      * TLE propagation and not for anything else, as recommended by the CCSDS Orbit Data Message
  1069.      * blue book.
  1070.      * </p>
  1071.      * @return the selected reference frame singleton.
  1072.      * @exception OrekitException if data embedded in the library cannot be read
  1073.      */
  1074.     public static FactoryManagedFrame getTEME() throws OrekitException {
  1075.         synchronized (FramesFactory.class) {

  1076.             // try to find an already built frame
  1077.             final Predefined factoryKey = Predefined.TEME;
  1078.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

  1079.             if (frame == null) {
  1080.                 // it's the first time we need this frame, build it and store it
  1081.                 final Frame tod = getTOD(IERSConventions.IERS_1996, false, true);
  1082.                 final ShiftingTransformProvider todShifting =
  1083.                         (ShiftingTransformProvider) tod.getTransformProvider();
  1084.                 final TEMEProvider temeRaw = new TEMEProvider(IERSConventions.IERS_1996, null);
  1085.                 final TransformProvider temeShifting =
  1086.                         new ShiftingTransformProvider(temeRaw,
  1087.                                                       CartesianDerivativesFilter.USE_PVA,
  1088.                                                       AngularDerivativesFilter.USE_R,
  1089.                                                       AbsoluteDate.PAST_INFINITY, AbsoluteDate.FUTURE_INFINITY,
  1090.                                                       todShifting.getGridPoints(), todShifting.getStep(),
  1091.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  1092.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);

  1093.                 frame = new FactoryManagedFrame(tod, temeShifting, true, factoryKey);
  1094.                 FRAMES.put(factoryKey, frame);
  1095.             }

  1096.             return frame;

  1097.         }
  1098.     }

  1099.     /** Get the transform between two frames, suppressing all interpolation.
  1100.      * <p>
  1101.      * This method is similar to {@link Frame#getTransformTo(Frame, AbsoluteDate)}
  1102.      * except it removes the performance enhancing interpolation features that are
  1103.      * added by the {@link FramesFactory factory} to some frames, in order to focus
  1104.      * on accuracy. The interpolation features are intended to save processing time
  1105.      * by avoiding doing some lengthy computation like nutation evaluation at each
  1106.      * time step and caching some results. This method can be used to avoid this,
  1107.      * when very high accuracy is desired, or for testing purposes. It should be
  1108.      * used with care, as doing the full computation is <em>really</em> costly for
  1109.      * some frames.
  1110.      * </p>
  1111.      * @param from frame from which transformation starts
  1112.      * @param to frame to which transformation ends
  1113.      * @param date date of the transform
  1114.      * @return transform between the two frames, avoiding interpolation
  1115.      * @throws OrekitException if transform cannot be computed at this date
  1116.      */
  1117.     public static Transform getNonInterpolatingTransform(final Frame from, final Frame to,
  1118.                                                          final AbsoluteDate date)
  1119.         throws OrekitException {

  1120.         // common ancestor to both frames in the frames tree
  1121.         Frame currentF = from.getDepth() > to.getDepth() ? from.getAncestor(from.getDepth() - to.getDepth()) : from;
  1122.         Frame currentT = from.getDepth() > to.getDepth() ? to : to.getAncestor(to.getDepth() - from.getDepth());
  1123.         while (currentF != currentT) {
  1124.             currentF = currentF.getParent();
  1125.             currentT = currentT.getParent();
  1126.         }
  1127.         final Frame common = currentF;

  1128.         // transform from common to origin
  1129.         Transform commonToOrigin = Transform.IDENTITY;
  1130.         for (Frame frame = from; frame != common; frame = frame.getParent()) {
  1131.             commonToOrigin = new Transform(date,
  1132.                                              peel(frame.getTransformProvider()).getTransform(date),
  1133.                                              commonToOrigin);
  1134.         }

  1135.         // transform from destination up to common
  1136.         Transform commonToDestination = Transform.IDENTITY;
  1137.         for (Frame frame = to; frame != common; frame = frame.getParent()) {
  1138.             commonToDestination = new Transform(date,
  1139.                                                 peel(frame.getTransformProvider()).getTransform(date),
  1140.                                                 commonToDestination);
  1141.         }

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

  1144.     }

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

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

  1176.         // transform from common to origin
  1177.         FieldTransform<T> commonToOrigin = FieldTransform.getIdentity(date.getField());
  1178.         for (Frame frame = from; frame != common; frame = frame.getParent()) {
  1179.             commonToOrigin = new FieldTransform<>(date,
  1180.                                                    peel(frame.getTransformProvider()).getTransform(date),
  1181.                                                    commonToOrigin);
  1182.         }

  1183.         // transform from destination up to common
  1184.         FieldTransform<T> commonToDestination = FieldTransform.getIdentity(date.getField());
  1185.         for (Frame frame = to; frame != common; frame = frame.getParent()) {
  1186.             commonToDestination = new FieldTransform<>(date,
  1187.                                                        peel(frame.getTransformProvider()).getTransform(date),
  1188.                                                        commonToDestination);
  1189.         }

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

  1192.     }

  1193.     /** Peel interpolation and shifting from a transform provider.
  1194.      * @param provider transform provider to peel
  1195.      * @return peeled transform provider
  1196.      * @exception OrekitException if EOP cannot be retrieved
  1197.      */
  1198.     private static TransformProvider peel(final TransformProvider provider)
  1199.         throws OrekitException {

  1200.         TransformProvider peeled = provider;

  1201.         boolean peeling = true;
  1202.         while (peeling) {
  1203.             if (peeled instanceof InterpolatingTransformProvider) {
  1204.                 peeled = ((InterpolatingTransformProvider) peeled).getRawProvider();
  1205.             } else if (peeled instanceof ShiftingTransformProvider) {
  1206.                 peeled = ((ShiftingTransformProvider) peeled).getRawProvider();
  1207.             } else if (peeled instanceof EOPBasedTransformProvider &&
  1208.                        ((EOPBasedTransformProvider) peeled).getEOPHistory() != null &&
  1209.                        ((EOPBasedTransformProvider) peeled).getEOPHistory().usesInterpolation()) {
  1210.                 peeled = ((EOPBasedTransformProvider) peeled).getNonInterpolatingProvider();
  1211.             } else {
  1212.                 peeling = false;
  1213.             }
  1214.         }

  1215.         return peeled;

  1216.     }

  1217. }