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.                                                       6, Constants.JULIAN_DAY / 24,
  721.                                                       OrekitConfiguration.getCacheSlotsNumber(),
  722.                                                       Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
  723.                 frame = new FactoryManagedFrame(getGCRF(), shifting, true, factoryKey);
  724.                 FRAMES.put(factoryKey, frame);
  725.             }

  726.             return frame;

  727.         }
  728.     }

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

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

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

  745.             return frame;

  746.         }
  747.     }

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

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

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

  792.             return frame;

  793.         }
  794.     }

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

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

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

  842.         synchronized (FramesFactory.class) {

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

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

  883.             return frame;

  884.         }
  885.     }

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

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

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

  934.         synchronized (FramesFactory.class) {

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

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

  980.             return frame;

  981.         }
  982.     }

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

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

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

  1026.         synchronized (FramesFactory.class) {

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

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

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

  1057.             return frame;

  1058.         }
  1059.     }

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

  1073.             // try to find an already built frame
  1074.             final Predefined factoryKey = Predefined.TEME;
  1075.             FactoryManagedFrame frame = FRAMES.get(factoryKey);

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

  1089.                 frame = new FactoryManagedFrame(tod, temeShifting, true, factoryKey);
  1090.                 FRAMES.put(factoryKey, frame);
  1091.             }

  1092.             return frame;

  1093.         }
  1094.     }

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

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

  1124.         // transform from common to origin
  1125.         Transform commonToOrigin = Transform.IDENTITY;
  1126.         for (Frame frame = from; frame != common; frame = frame.getParent()) {
  1127.             commonToOrigin = new Transform(date,
  1128.                                              peel(frame.getTransformProvider()).getTransform(date),
  1129.                                              commonToOrigin);
  1130.         }

  1131.         // transform from destination up to common
  1132.         Transform commonToDestination = Transform.IDENTITY;
  1133.         for (Frame frame = to; frame != common; frame = frame.getParent()) {
  1134.             commonToDestination = new Transform(date,
  1135.                                                 peel(frame.getTransformProvider()).getTransform(date),
  1136.                                                 commonToDestination);
  1137.         }

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

  1140.     }

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

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

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

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

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

  1188.     }

  1189.     /** Retrieve EOP from a frame hierarchy.
  1190.      * <p>
  1191.      * The frame hierarchy tree is walked from specified frame up to root
  1192.      * goind though parent frames, and the providers are checked to see if they
  1193.      * reference EOP history.the first EOP history found is returned.
  1194.      * </p>
  1195.      * @param start frame from which to start search, will typically be some
  1196.      * Earth related frame, like a topocentric frame or an ITRF frame
  1197.      * @return EOP history found while walking the frames tree, or null if
  1198.      * no EOP history is found
  1199.      * @since 9.1
  1200.      */
  1201.     public static EOPHistory findEOP(final Frame start) {

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

  1203.             TransformProvider peeled = frame.getTransformProvider();

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

  1217.         }

  1218.         // no history found
  1219.         return null;

  1220.     }

  1221.     /** Peel interpolation and shifting from a transform provider.
  1222.      * @param provider transform provider to peel
  1223.      * @return peeled transform provider
  1224.      * @exception OrekitException if EOP cannot be retrieved
  1225.      */
  1226.     private static TransformProvider peel(final TransformProvider provider)
  1227.         throws OrekitException {

  1228.         TransformProvider peeled = provider;

  1229.         boolean peeling = true;
  1230.         while (peeling) {
  1231.             if (peeled instanceof InterpolatingTransformProvider) {
  1232.                 peeled = ((InterpolatingTransformProvider) peeled).getRawProvider();
  1233.             } else if (peeled instanceof ShiftingTransformProvider) {
  1234.                 peeled = ((ShiftingTransformProvider) peeled).getRawProvider();
  1235.             } else if (peeled instanceof EOPBasedTransformProvider &&
  1236.                        ((EOPBasedTransformProvider) peeled).getEOPHistory() != null &&
  1237.                        ((EOPBasedTransformProvider) peeled).getEOPHistory().usesInterpolation()) {
  1238.                 peeled = ((EOPBasedTransformProvider) peeled).getNonInterpolatingProvider();
  1239.             } else {
  1240.                 peeling = false;
  1241.             }
  1242.         }

  1243.         return peeled;

  1244.     }

  1245. }