FramesFactory.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (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 org.hipparchus.CalculusFieldElement;
  19. import org.orekit.annotation.DefaultDataContext;
  20. import org.orekit.bodies.CelestialBodies;
  21. import org.orekit.data.DataContext;
  22. import org.orekit.time.AbsoluteDate;
  23. import org.orekit.time.FieldAbsoluteDate;
  24. import org.orekit.time.TimeScales;
  25. import org.orekit.time.UT1Scale;
  26. import org.orekit.time.UTCScale;
  27. import org.orekit.utils.IERSConventions;


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

  154.     /* These constants were left here instead of being moved to LazyLoadedFrames because
  155.      * they are public.
  156.      */

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

  159.     /** Default regular expression for the EOP XML files (IAU1980 compatibles). */
  160.     public static final String XML_1980_FILENAME = "^(:finals|eopc04_\\d\\d)\\..*\\.xml$";

  161.     /** Default regular expression for the EOPC04 files (IAU1980 compatibles). */
  162.     public static final String EOPC04_1980_FILENAME = "^eopc04(_\\d\\d)?\\.\\d\\d$";

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

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

  167.     /** Default regular expression for the EOP XML files (IAU2000 compatibles). */
  168.     public static final String XML_2000_FILENAME = "^(:finals2000A|eopc04_\\d\\d_IAU2000)\\..*\\.xml$";

  169.     /** Default regular expression for the EOPC04 files (IAU2000 compatibles). */
  170.     public static final String EOPC04_2000_FILENAME = "^eopc04(_\\d\\d_IAU2000)?\\.\\d\\d$";

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

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

  175.     /** Default regular expression for the csv files (IAU1980 and IAU2000 compatibles).
  176.      * @since 12.0
  177.      */
  178.     public static final String CSV_FILENAME = "^(?:eopc04|bulletina|bulletinb).*\\.csv$";

  179.     /** Private constructor.
  180.      * <p>This class is a utility class, it should neither have a public
  181.      * nor a default constructor. This private constructor prevents
  182.      * the compiler from generating one automatically.</p>
  183.      */
  184.     private FramesFactory() {
  185.     }

  186.     /**
  187.      * Get the instance of {@link Frames} that is called by the static methods in this
  188.      * class.
  189.      *
  190.      * @return the reference frames used by this factory.
  191.      */
  192.     @DefaultDataContext
  193.     public static LazyLoadedFrames getFrames() {
  194.         return DataContext.getDefault().getFrames();
  195.     }

  196.     /** Add the default loaders EOP history (IAU 1980 precession/nutation).
  197.      * <p>
  198.      * The default loaders look for IERS EOP C04 and bulletins B files. They
  199.      * correspond to {@link IERSConventions#IERS_1996 IERS 1996} conventions.
  200.      * </p>
  201.      * @param rapidDataColumnsSupportedNames regular expression for supported
  202.      * rapid data columns EOP files names
  203.      * (may be null if the default IERS file names are used)
  204.      * @param rapidDataXMLSupportedNames regular expression for supported XML EOP files names
  205.      * (may be null if the default IERS file names are used)
  206.      * @param eopC04SupportedNames regular expression for supported EOP C04 files names
  207.      * (may be null if the default IERS file names are used)
  208.      * @param bulletinBSupportedNames regular expression for supported bulletin B files names
  209.      * (may be null if the default IERS file names are used)
  210.      * @param bulletinASupportedNames regular expression for supported bulletin A files names
  211.      * (may be null if the default IERS file names are used)
  212.      * @param csvSupportedNames regular expression for supported csv files names
  213.      * (may be null if the default IERS file names are used)
  214.      * @see <a href="http://hpiers.obspm.fr/eoppc/eop/eopc04/">IERS EOP C04 files</a>
  215.      * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader)
  216.      * @see #clearEOPHistoryLoaders()
  217.      * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String, String)
  218.      * @since 12.0
  219.      */
  220.     @DefaultDataContext
  221.     public static void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupportedNames,
  222.                                                        final String rapidDataXMLSupportedNames,
  223.                                                        final String eopC04SupportedNames,
  224.                                                        final String bulletinBSupportedNames,
  225.                                                        final String bulletinASupportedNames,
  226.                                                        final String csvSupportedNames) {
  227.         getFrames().addDefaultEOP1980HistoryLoaders(
  228.                 rapidDataColumnsSupportedNames,
  229.                 rapidDataXMLSupportedNames,
  230.                 eopC04SupportedNames,
  231.                 bulletinBSupportedNames,
  232.                 bulletinASupportedNames,
  233.                 csvSupportedNames);
  234.     }

  235.     /** Add the default loaders for EOP history (IAU 2000/2006 precession/nutation).
  236.      * <p>
  237.      * The default loaders look for IERS EOP C04 and bulletins B files. They
  238.      * correspond to both {@link IERSConventions#IERS_2003 IERS 2003} and {@link
  239.      * IERSConventions#IERS_2010 IERS 2010} conventions.
  240.      * </p>
  241.      * @param rapidDataColumnsSupportedNames regular expression for supported
  242.      * rapid data columns EOP files names
  243.      * (may be null if the default IERS file names are used)
  244.      * @param xmlSupportedNames regular expression for supported XML EOP files names
  245.      * (may be null if the default IERS file names are used)
  246.      * @param eopC04SupportedNames regular expression for supported EOP C04 files names
  247.      * (may be null if the default IERS file names are used)
  248.      * @param bulletinBSupportedNames regular expression for supported bulletin B files names
  249.      * (may be null if the default IERS file names are used)
  250.      * @param bulletinASupportedNames regular expression for supported bulletin A files names
  251.      * (may be null if the default IERS file names are used)
  252.      * @param csvSupportedNames regular expression for supported csv files names
  253.      * (may be null if the default IERS file names are used)
  254.      * @see <a href="http://hpiers.obspm.fr/eoppc/eop/eopc04/">IERS EOP C04 files</a>
  255.      * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader)
  256.      * @see #clearEOPHistoryLoaders()
  257.      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String)
  258.      * @since 12.0
  259.      */
  260.     @DefaultDataContext
  261.     public static void addDefaultEOP2000HistoryLoaders(final String rapidDataColumnsSupportedNames,
  262.                                                        final String xmlSupportedNames,
  263.                                                        final String eopC04SupportedNames,
  264.                                                        final String bulletinBSupportedNames,
  265.                                                        final String bulletinASupportedNames,
  266.                                                        final String csvSupportedNames) {
  267.         getFrames().addDefaultEOP2000HistoryLoaders(
  268.                 rapidDataColumnsSupportedNames,
  269.                 xmlSupportedNames,
  270.                 eopC04SupportedNames,
  271.                 bulletinBSupportedNames,
  272.                 bulletinASupportedNames,
  273.                 csvSupportedNames);
  274.     }

  275.     /** Add a loader for Earth Orientation Parameters history.
  276.      * @param conventions IERS conventions to which EOP history applies
  277.      * @param loader custom loader to add for the EOP history
  278.      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String)
  279.      * @see #clearEOPHistoryLoaders()
  280.      */
  281.     @DefaultDataContext
  282.     public static void addEOPHistoryLoader(final IERSConventions conventions, final EopHistoryLoader loader) {
  283.         getFrames().addEOPHistoryLoader(conventions, loader);
  284.     }

  285.     /** Clear loaders for Earth Orientation Parameters history.
  286.      * @see #addEOPHistoryLoader(IERSConventions, EopHistoryLoader)
  287.      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String, String)
  288.      */
  289.     @DefaultDataContext
  290.     public static void clearEOPHistoryLoaders() {
  291.         getFrames().clearEOPHistoryLoaders();
  292.     }

  293.     /** Set the threshold to check EOP continuity.
  294.      * <p>
  295.      * The default threshold (used if this method is never called)
  296.      * is 5 Julian days. If after loading EOP entries some holes
  297.      * between entries exceed this threshold, an exception will
  298.      * be triggered.
  299.      * </p>
  300.      * <p>
  301.      * One case when calling this method is really useful is for
  302.      * applications that use a single Bulletin A, as these bulletins
  303.      * have a roughly one month wide hole for the first bulletin of
  304.      * each month, which contains older final data in addition to the
  305.      * rapid data and the predicted data.
  306.      * </p>
  307.      * @param threshold threshold to use for checking EOP continuity (in seconds)
  308.      */
  309.     @DefaultDataContext
  310.     public static void setEOPContinuityThreshold(final double threshold) {
  311.         getFrames().setEOPContinuityThreshold(threshold);
  312.     }

  313.     /** Get Earth Orientation Parameters history.
  314.      * <p>
  315.      * If no {@link EopHistoryLoader} has been added by calling {@link
  316.      * #addEOPHistoryLoader(IERSConventions, EopHistoryLoader) addEOPHistoryLoader}
  317.      * or if {@link #clearEOPHistoryLoaders() clearEOPHistoryLoaders} has been
  318.      * called afterwards, the {@link #addDefaultEOP1980HistoryLoaders(String, String,
  319.      * String, String, String, String)} and {@link #addDefaultEOP2000HistoryLoaders(String,
  320.      * String, String, String, String, String)} methods will be called automatically with
  321.      * supported file names parameters all set to null, in order to get the default
  322.      * loaders configuration.
  323.      * </p>
  324.      * @param conventions conventions for which EOP history is requested
  325.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  326.      * @return Earth Orientation Parameters history
  327.      */
  328.     @DefaultDataContext
  329.     public static EOPHistory getEOPHistory(final IERSConventions conventions, final boolean simpleEOP) {
  330.         return getFrames().getEOPHistory(conventions, simpleEOP);
  331.     }

  332.     /** Get one of the predefined frames.
  333.      * @param factoryKey key of the frame within the factory
  334.      * @return the predefined frame
  335.      */
  336.     @DefaultDataContext
  337.     public static Frame getFrame(final Predefined factoryKey) {
  338.         return getFrames().getFrame(factoryKey);
  339.     }

  340.     /** Get the unique GCRF frame.
  341.      * <p>The GCRF frame is the root frame in the frame tree.</p>
  342.      * @return the unique instance of the GCRF frame
  343.      */
  344.     @DefaultDataContext
  345.     public static Frame getGCRF() {
  346.         return getFrames().getGCRF();
  347.     }

  348.     /** Get the unique ICRF frame.
  349.      * <p>The ICRF frame is centered at solar system barycenter and aligned
  350.      * with GCRF.</p>
  351.      * @return the unique instance of the ICRF frame
  352.      */
  353.     @DefaultDataContext
  354.     public static Frame getICRF() {
  355.         return getFrames().getICRF();
  356.     }

  357.     /** Get the ecliptic frame.
  358.      * The IAU defines the ecliptic as "the plane perpendicular to the mean heliocentric
  359.      * orbital angular momentum vector of the Earth-Moon barycentre in the BCRS (IAU 2006
  360.      * Resolution B1)." The +z axis is aligned with the angular momentum vector, and the +x
  361.      * axis is aligned with +x axis of {@link FramesFactory#getMOD(IERSConventions) MOD}.
  362.      *
  363.      * <p> This implementation agrees with the JPL 406 ephemerides to within 0.5 arc seconds.
  364.      * @param conventions IERS conventions to apply
  365.      * @return the selected reference frame singleton.
  366.      */
  367.     @DefaultDataContext
  368.     public static Frame getEcliptic(final IERSConventions conventions) {
  369.         return getFrames().getEcliptic(conventions);
  370.     }

  371.     /** Get the unique EME2000 frame.
  372.      * <p>The EME2000 frame is also called the J2000 frame.
  373.      * The former denomination is preferred in Orekit.</p>
  374.      * @return the unique instance of the EME2000 frame
  375.      */
  376.     @DefaultDataContext
  377.     public static FactoryManagedFrame getEME2000() {
  378.         return getFrames().getEME2000();
  379.     }

  380.     /** Get an unspecified International Terrestrial Reference Frame.
  381.      * <p>
  382.      * The frame returned uses the {@link EOPEntry Earth Orientation Parameters}
  383.      * blindly. So if for example one loads only EOP 14 C04 files to retrieve
  384.      * the parameters, the frame will be an {@link ITRFVersion#ITRF_2014}. However,
  385.      * if parameters are loaded from different files types, or even for file
  386.      * types that changed their reference (like Bulletin A switching from
  387.      * {@link ITRFVersion#ITRF_2008} to {@link ITRFVersion#ITRF_2014} starting
  388.      * with Vol. XXXI No. 013 published on 2018-03-29), then the ITRF returned
  389.      * by this method will jump from one version to another version.
  390.      * </p>
  391.      * <p>
  392.      * IF a specific version of ITRF is needed, then {@link #getITRF(ITRFVersion,
  393.      * IERSConventions, boolean)} should be used instead.
  394.      * </p>
  395.      * @param conventions IERS conventions to apply
  396.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  397.      * @return the selected reference frame singleton.
  398.           * @see #getITRF(ITRFVersion, IERSConventions, boolean)
  399.      * @since 6.1
  400.      */
  401.     @DefaultDataContext
  402.     public static FactoryManagedFrame getITRF(final IERSConventions conventions,
  403.                                               final boolean simpleEOP) {
  404.         return getFrames().getITRF(conventions, simpleEOP);
  405.     }

  406.     /** Get the TIRF reference frame, ignoring tidal effects.
  407.      * @param conventions IERS conventions to apply
  408.      * @return the selected reference frame singleton.
  409.           * library cannot be read.
  410.      */
  411.     @DefaultDataContext
  412.     public static FactoryManagedFrame getTIRF(final IERSConventions conventions) {
  413.         return getFrames().getTIRF(conventions);
  414.     }

  415.     /** Get a specific International Terrestrial Reference Frame.
  416.      * <p>
  417.      * Note that if a specific version of ITRF is required, then {@code simpleEOP}
  418.      * should most probably be set to {@code false}, as ignoring tidal effects
  419.      * has an effect of the same order of magnitude as the differences between
  420.      * the various {@link ITRFVersion ITRF versions}.
  421.      * </p>
  422.      * @param version ITRF version
  423.      * @param conventions IERS conventions to apply
  424.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  425.      * @return the selected reference frame singleton.
  426.           * @since 9.2
  427.      */
  428.     @DefaultDataContext
  429.     public static VersionedITRF getITRF(final ITRFVersion version,
  430.                                         final IERSConventions conventions,
  431.                                         final boolean simpleEOP) {
  432.         return getFrames().getITRF(version, conventions, simpleEOP);
  433.     }

  434.     /** Build an uncached International Terrestrial Reference Frame with specific {@link EOPHistory EOP history}.
  435.      * <p>
  436.      * This frame and its parent frames (TIRF and CIRF) will <em>not</em> be cached, they are
  437.      * rebuilt from scratch each time this method is called. This factory method is intended
  438.      * to be used when EOP history is changed at run time. For regular ITRF use, the
  439.      * {@link #getITRF(IERSConventions, boolean)} and {link {@link #getITRF(ITRFVersion, IERSConventions, boolean)}
  440.      * are more suitable.
  441.      * </p>
  442.      * @param eopHistory EOP history
  443.      * @param utc UTC time scale
  444.      * @return an ITRF frame with specified EOP history
  445.      * @since 12.0
  446.      */
  447.     public static Frame buildUncachedITRF(final EOPHistory eopHistory, final UTCScale utc) {
  448.         final TimeScales timeScales = TimeScales.of(utc.getBaseOffsets(),
  449.                                                     (conventions, timescales) -> eopHistory.getEntries());
  450.         final UT1Scale   ut1        = timeScales.getUT1(eopHistory.getConventions(), eopHistory.isSimpleEop());
  451.         final Frames     frames     = Frames.of(timeScales, (CelestialBodies) null);
  452.         return frames.buildUncachedITRF(ut1);
  453.     }

  454.     /** Get the TIRF reference frame.
  455.      * @param conventions IERS conventions to apply
  456.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  457.      * @return the selected reference frame singleton.
  458.      * @since 6.1
  459.      */
  460.     @DefaultDataContext
  461.     public static FactoryManagedFrame getTIRF(final IERSConventions conventions,
  462.                                               final boolean simpleEOP) {
  463.         return getFrames().getTIRF(conventions, simpleEOP);
  464.     }

  465.     /** Get the CIRF2000 reference frame.
  466.      * @param conventions IERS conventions to apply
  467.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  468.      * @return the selected reference frame singleton.
  469.      */
  470.     @DefaultDataContext
  471.     public static FactoryManagedFrame getCIRF(final IERSConventions conventions,
  472.                                               final boolean simpleEOP) {
  473.         return getFrames().getCIRF(conventions, simpleEOP);
  474.     }

  475.     /** Get the VEIS 1950 reference frame.
  476.      * <p>Its parent frame is the GTOD frame with IERS 1996 conventions without EOP corrections.</p>
  477.      * @return the selected reference frame singleton.
  478.      */
  479.     @DefaultDataContext
  480.     public static FactoryManagedFrame getVeis1950() {
  481.         return getFrames().getVeis1950();
  482.     }

  483.     /** Get the equinox-based ITRF reference frame.
  484.      * @param conventions IERS conventions to apply
  485.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  486.      * @return the selected reference frame singleton.
  487.      * @since 6.1
  488.      */
  489.     @DefaultDataContext
  490.     public static FactoryManagedFrame getITRFEquinox(final IERSConventions conventions,
  491.                                                      final boolean simpleEOP) {
  492.         return getFrames().getITRFEquinox(conventions, simpleEOP);
  493.     }

  494.     /** Get the GTOD reference frame.
  495.      * <p>
  496.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  497.      * consistency with legacy software that don't handle EOP correction parameters.
  498.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  499.      * (order of magnitudes for errors might be above 250m in LEO and 1400m in GEO).
  500.      * For this reason, setting this parameter to false is restricted to {@link
  501.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  502.      * IERSConventions IERS conventions} cannot be freely chosen here.
  503.      * </p>
  504.      * @param applyEOPCorr if true, EOP corrections are applied (here, dut1 and lod)
  505.      * @return the selected reference frame singleton.
  506.      */
  507.     @DefaultDataContext
  508.     public static FactoryManagedFrame getGTOD(final boolean applyEOPCorr) {
  509.         return getFrames().getGTOD(applyEOPCorr);
  510.     }

  511.     /** Get the GTOD reference frame.
  512.      * @param conventions IERS conventions to apply
  513.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  514.      * @return the selected reference frame singleton.
  515.      */
  516.     @DefaultDataContext
  517.     public static FactoryManagedFrame getGTOD(final IERSConventions conventions,
  518.                                               final boolean simpleEOP) {
  519.         return getFrames().getGTOD(conventions, simpleEOP);
  520.     }

  521.     /** Get the TOD reference frame.
  522.      * <p>
  523.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  524.      * consistency with legacy software that don't handle EOP correction parameters.
  525.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  526.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  527.      * For this reason, setting this parameter to false is restricted to {@link
  528.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  529.      * IERSConventions IERS conventions} cannot be freely chosen here.
  530.      * </p>
  531.      * @param applyEOPCorr if true, EOP corrections are applied (here, nutation)
  532.      * @return the selected reference frame singleton.
  533.      */
  534.     @DefaultDataContext
  535.     public static FactoryManagedFrame getTOD(final boolean applyEOPCorr) {
  536.         return getFrames().getTOD(applyEOPCorr);
  537.     }

  538.     /** Get the TOD reference frame.
  539.      * @param conventions IERS conventions to apply
  540.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  541.      * @return the selected reference frame singleton.
  542.      */
  543.     @DefaultDataContext
  544.     public static FactoryManagedFrame getTOD(final IERSConventions conventions,
  545.                                              final boolean simpleEOP) {
  546.         return getFrames().getTOD(conventions, simpleEOP);
  547.     }

  548.     /** Get the MOD reference frame.
  549.      * <p>
  550.      * The applyEOPCorr parameter is available mainly for testing purposes or for
  551.      * consistency with legacy software that don't handle EOP correction parameters.
  552.      * Beware that setting this parameter to {@code false} leads to crude accuracy
  553.      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
  554.      * For this reason, setting this parameter to false is restricted to {@link
  555.      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
  556.      * IERSConventions IERS conventions} cannot be freely chosen here.
  557.      * </p>
  558.      * @param applyEOPCorr if true, EOP corrections are applied (EME2000/GCRF bias compensation)
  559.      * @return the selected reference frame singleton.
  560.      */
  561.     @DefaultDataContext
  562.     public static FactoryManagedFrame getMOD(final boolean applyEOPCorr) {
  563.         return getFrames().getMOD(applyEOPCorr);
  564.     }

  565.     /** Get the MOD reference frame.
  566.      * @param conventions IERS conventions to apply
  567.      * @return the selected reference frame singleton.
  568.      */
  569.     @DefaultDataContext
  570.     public static FactoryManagedFrame getMOD(final IERSConventions conventions) {
  571.         return getFrames().getMOD(conventions);
  572.     }

  573.     /** Get the TEME reference frame.
  574.      * <p>
  575.      * The TEME frame is used for the SGP4 model in TLE propagation. This frame has <em>no</em>
  576.      * official definition and there are some ambiguities about whether it should be used
  577.      * as "of date" or "of epoch". This frame should therefore be used <em>only</em> for
  578.      * TLE propagation and not for anything else, as recommended by the CCSDS Orbit Data Message
  579.      * blue book.
  580.      * </p>
  581.      * @return the selected reference frame singleton.
  582.      */
  583.     @DefaultDataContext
  584.     public static FactoryManagedFrame getTEME() {
  585.         return getFrames().getTEME();
  586.     }

  587.     /** Get the PZ-90.11 (Parametry Zemly  – 1990.11) reference frame.
  588.      * <p>
  589.      * The PZ-90.11 reference system was updated on all operational
  590.      * GLONASS satellites starting from 3:00 pm on December 31, 2013.
  591.      * </p>
  592.      * <p>
  593.      * The transition between parent frame (ITRF-2008) and PZ-90.11 frame is performed using
  594.      * a seven parameters Helmert transformation.
  595.      * <pre>
  596.      *    From       To      ΔX(m)   ΔY(m)   ΔZ(m)   RX(mas)   RY(mas)  RZ(mas)   Epoch
  597.      * ITRF-2008  PZ-90.11  +0.003  +0.001  -0.000   +0.019    -0.042   +0.002     2010
  598.      * </pre>
  599.      * @see "Springer Handbook of Global Navigation Satellite Systems, Peter Teunissen & Oliver Montenbruck"
  600.      *
  601.      * @param convention IERS conventions to apply
  602.      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
  603.      * @return the selected reference frame singleton.
  604.      */
  605.     @DefaultDataContext
  606.     public static FactoryManagedFrame getPZ9011(final IERSConventions convention,
  607.                                                 final boolean simpleEOP) {
  608.         return getFrames().getPZ9011(convention, simpleEOP);
  609.     }

  610.     /** Get the transform between two frames, suppressing all interpolation.
  611.      * <p>
  612.      * This method is similar to {@link Frame#getTransformTo(Frame, AbsoluteDate)}
  613.      * except it removes the performance enhancing interpolation features that are
  614.      * added by the {@link FramesFactory factory} to some frames, in order to focus
  615.      * on accuracy. The interpolation features are intended to save processing time
  616.      * by avoiding doing some lengthy computation like nutation evaluation at each
  617.      * time step and caching some results. This method can be used to avoid this,
  618.      * when very high accuracy is desired, or for testing purposes. It should be
  619.      * used with care, as doing the full computation is <em>really</em> costly for
  620.      * some frames.
  621.      * </p>
  622.      * @param from frame from which transformation starts
  623.      * @param to frame to which transformation ends
  624.      * @param date date of the transform
  625.      * @return transform between the two frames, avoiding interpolation
  626.      */
  627.     public static Transform getNonInterpolatingTransform(final Frame from, final Frame to,
  628.                                                          final AbsoluteDate date) {

  629.         // common ancestor to both frames in the frames tree
  630.         Frame currentF = from.getDepth() > to.getDepth() ? from.getAncestor(from.getDepth() - to.getDepth()) : from;
  631.         Frame currentT = from.getDepth() > to.getDepth() ? to : to.getAncestor(to.getDepth() - from.getDepth());
  632.         while (currentF != currentT) {
  633.             currentF = currentF.getParent();
  634.             currentT = currentT.getParent();
  635.         }
  636.         final Frame common = currentF;

  637.         // transform from common to origin
  638.         Transform commonToOrigin = Transform.IDENTITY;
  639.         for (Frame frame = from; frame != common; frame = frame.getParent()) {
  640.             commonToOrigin = new Transform(date,
  641.                                              peel(frame.getTransformProvider()).getTransform(date),
  642.                                              commonToOrigin);
  643.         }

  644.         // transform from destination up to common
  645.         Transform commonToDestination = Transform.IDENTITY;
  646.         for (Frame frame = to; frame != common; frame = frame.getParent()) {
  647.             commonToDestination = new Transform(date,
  648.                                                 peel(frame.getTransformProvider()).getTransform(date),
  649.                                                 commonToDestination);
  650.         }

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

  653.     }

  654.     /* The methods below are static helper methods for Frame and TransformProvider. */

  655.     /** Get the transform between two frames, suppressing all interpolation.
  656.      * <p>
  657.      * This method is similar to {@link Frame#getTransformTo(Frame, AbsoluteDate)}
  658.      * except it removes the performance enhancing interpolation features that are
  659.      * added by the {@link FramesFactory factory} to some frames, in order to focus
  660.      * on accuracy. The interpolation features are intended to save processing time
  661.      * by avoiding doing some lengthy computation like nutation evaluation at each
  662.      * time step and caching some results. This method can be used to avoid this,
  663.      * when very high accuracy is desired, or for testing purposes. It should be
  664.      * used with care, as doing the full computation is <em>really</em> costly for
  665.      * some frames.
  666.      * </p>
  667.      * @param from frame from which transformation starts
  668.      * @param to frame to which transformation ends
  669.      * @param date date of the transform
  670.      * @param <T> type of the field elements
  671.      * @return transform between the two frames, avoiding interpolation
  672.      * @since 9.0
  673.      */
  674.     public static <T extends CalculusFieldElement<T>> FieldTransform<T> getNonInterpolatingTransform(final Frame from, final Frame to,
  675.                                                                                                  final FieldAbsoluteDate<T> date) {

  676.         // common ancestor to both frames in the frames tree
  677.         Frame currentF = from.getDepth() > to.getDepth() ? from.getAncestor(from.getDepth() - to.getDepth()) : from;
  678.         Frame currentT = from.getDepth() > to.getDepth() ? to : to.getAncestor(to.getDepth() - from.getDepth());
  679.         while (currentF != currentT) {
  680.             currentF = currentF.getParent();
  681.             currentT = currentT.getParent();
  682.         }
  683.         final Frame common = currentF;

  684.         // transform from common to origin
  685.         FieldTransform<T> commonToOrigin = FieldTransform.getIdentity(date.getField());
  686.         for (Frame frame = from; frame != common; frame = frame.getParent()) {
  687.             commonToOrigin = new FieldTransform<>(date,
  688.                                                    peel(frame.getTransformProvider()).getTransform(date),
  689.                                                    commonToOrigin);
  690.         }

  691.         // transform from destination up to common
  692.         FieldTransform<T> commonToDestination = FieldTransform.getIdentity(date.getField());
  693.         for (Frame frame = to; frame != common; frame = frame.getParent()) {
  694.             commonToDestination = new FieldTransform<>(date,
  695.                                                        peel(frame.getTransformProvider()).getTransform(date),
  696.                                                        commonToDestination);
  697.         }

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

  700.     }

  701.     /** Retrieve EOP from a frame hierarchy.
  702.      * <p>
  703.      * The frame hierarchy tree is walked from specified frame up to root
  704.      * traversing parent frames, and the providers are checked to see if they
  705.      * reference EOP history. The first EOP history found is returned.
  706.      * </p>
  707.      * @param start frame from which to start search, will typically be some
  708.      * Earth related frame, like a topocentric frame or an ITRF frame
  709.      * @return EOP history found while walking the frames tree, or null if
  710.      * no EOP history is found
  711.      * @since 9.1
  712.      */
  713.     public static EOPHistory findEOP(final Frame start) {

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

  715.             TransformProvider peeled = frame.getTransformProvider();

  716.             boolean peeling = true;
  717.             while (peeling) {
  718.                 if (peeled instanceof InterpolatingTransformProvider) {
  719.                     peeled = ((InterpolatingTransformProvider) peeled).getRawProvider();
  720.                 } else if (peeled instanceof ShiftingTransformProvider) {
  721.                     peeled = ((ShiftingTransformProvider) peeled).getRawProvider();
  722.                 } else if (peeled instanceof EOPBasedTransformProvider &&
  723.                            ((EOPBasedTransformProvider) peeled).getEOPHistory() != null) {
  724.                     return ((EOPBasedTransformProvider) peeled).getEOPHistory();
  725.                 } else {
  726.                     peeling = false;
  727.                 }
  728.             }

  729.         }

  730.         // no history found
  731.         return null;

  732.     }

  733.     /** Peel interpolation and shifting from a transform provider.
  734.      * @param provider transform provider to peel
  735.      * @return peeled transform provider
  736.      */
  737.     private static TransformProvider peel(final TransformProvider provider) {

  738.         TransformProvider peeled = provider;

  739.         boolean peeling = true;
  740.         while (peeling) {
  741.             if (peeled instanceof InterpolatingTransformProvider) {
  742.                 peeled = ((InterpolatingTransformProvider) peeled).getRawProvider();
  743.             } else if (peeled instanceof ShiftingTransformProvider) {
  744.                 peeled = ((ShiftingTransformProvider) peeled).getRawProvider();
  745.             } else if (peeled instanceof EOPBasedTransformProvider &&
  746.                        ((EOPBasedTransformProvider) peeled).getEOPHistory() != null &&
  747.                        ((EOPBasedTransformProvider) peeled).getEOPHistory().cachesTidalCorrection()) {
  748.                 peeled = ((EOPBasedTransformProvider) peeled).getNonInterpolatingProvider();
  749.             } else {
  750.                 peeling = false;
  751.             }
  752.         }

  753.         return peeled;

  754.     }

  755. }