1   /* Copyright 2002-2021 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  
19  import org.hipparchus.CalculusFieldElement;
20  import org.orekit.annotation.DefaultDataContext;
21  import org.orekit.data.DataContext;
22  import org.orekit.time.AbsoluteDate;
23  import org.orekit.time.FieldAbsoluteDate;
24  import org.orekit.utils.IERSConventions;
25  
26  
27  /** Factory for predefined reference frames.
28   *
29   * <h2> FramesFactory Presentation </h2>
30   * <p>
31   * Several predefined reference {@link Frame frames} are implemented in OREKIT.
32   * They are linked together in a tree with the <i>Geocentric
33   * Celestial Reference Frame</i> (GCRF) as the root of the tree.
34   * This factory is designed to:
35   * </p>
36   * <ul>
37   *   <li>build the frames tree consistently,</li>
38   *   <li>avoid rebuilding some frames that may be costly to recreate all the time,</li>
39   *   <li>set up interpolation/caching features for some frames that may induce costly computation</li>
40   *   <li>streamline the {@link EOPHistory Earth Orientation Parameters} history loading.</li>
41   * </ul>
42   * <h2> Reference Frames </h2>
43   * <p>
44   * The user can retrieve those reference frames using various static methods, the most
45   * important ones being: {@link #getFrame(Predefined)}, {@link #getGCRF()},
46   * {@link #getCIRF(IERSConventions, boolean)} {@link #getTIRF(IERSConventions, boolean)},
47   * {@link #getITRF(IERSConventions, boolean)}, {@link #getITRF(ITRFVersion, IERSConventions, boolean)},
48   * {@link #getEME2000()}, {@link #getMOD(IERSConventions)}, {@link #getTOD(IERSConventions, boolean)},
49   * {@link #getGTOD(IERSConventions, boolean)}, {@link #getITRFEquinox(IERSConventions, boolean)},
50   * {@link #getTEME()} and {@link #getVeis1950()}.
51   * </p>
52   * <h2> International Terrestrial Reference Frame</h2>
53   * <p>
54   * This frame is the current (as of 2013) reference realization of
55   * the International Terrestrial Reference System produced by IERS.
56   * It is described in <a href="ftp://tai.bipm.org/iers/conv2010/tn36.pdf">
57   * IERS conventions (2010)</a>. It replaces the Earth Centered Earth Fixed
58   * frame which is the reference frame for GPS satellites.
59   * </p>
60   * <p>
61   * This frame is used to define position on solid Earth. It rotates with
62   * the Earth and includes the pole motion with respect to Earth crust as
63   * provided by IERS {@link EOPHistory Earth Orientation Parameters}.
64   * Its pole axis is the IERS Reference Pole (IRP).
65   * </p>
66   * <p>
67   * Depending on the  {@link EOPHistory Earth Orientation Parameters} source,
68   * different ITRS realization may be returned by {@link #getITRF(IERSConventions, boolean)},
69   * and if EOP are mixed, the ITRF may even jump from one realization to another one.
70   * This is not a problem for most users as different ITRS realizations are very close
71   * to each other (a few millimeters at Earth surface). If however a specific ITRF version
72   * (i.e. an ITRS realization) is needed for very high accuracy, Orekit provides the
73   * {@link FramesFactory#getITRF(ITRFVersion, IERSConventions, boolean)} method
74   * to get it and take care of jumps in EOP.
75   * </p>
76   * <p>
77   * ITRF can be built using the new non-rotating origin paradigm
78   * mandated by IAU 2000 resolution B1.8 and any supported {@link IERSConventions
79   * IERS conventions} (even IERS 1996 can be used with non-rotating origin paradigm,
80   * despite the resolution was not yet adopted at conventions publication time).
81   * </p>
82   * <p>
83   * ITRF can also be built using the classical equinox paradigm used prior to IAU 2000
84   * resolution B1.8 and any supported {@link IERSConventions IERS conventions} (even
85   * IERS 2003 and 2010 can be used with equinox paradigm, despite the resolution is
86   * in effect now). The choice of paradigm (non-rotating origin or equinox) and the
87   * choice of IERS conventions (i.e. the choice of precession/nutation models) can
88   * be made independently by user, Orekit provides all alternatives.
89   * </p>
90   * <h2>Intermediate frames</h2>
91   * <p>
92   * Orekit also provides all the intermediate frames that are needed to transform
93   * between GCRF and ITRF, along the two paths: ITRF/TIRF/CIRF/GCRF for the
94   * non-rotating origin paradigm and ITRF/GTOD/TOD/MOD/EME2000/GCRF for the equinox
95   * paradigm.
96   * </p>
97   * <h2> Earth Orientation Parameters </h2>
98   * <p>
99   * This factory also handles loading of Earth Orientation Parameters (EOP) needed
100  * for accurate transformations between inertial and Earth fixed frames, using
101  * {@link org.orekit.data.DataProvidersManager} features. EOP are IERS conventions
102  * dependent, because they correspond to correction to the precession/nutation
103  * models. When EOP should be applied, but EOP data are not available, then a null
104  * (0.0) correction is used. This can occur when no EOP data is loaded, or when the
105  * requested date is beyond the time span of the loaded EOP data. Using a null
106  * correction can result in coarse accuracy. To check the time span covered by EOP data use
107  * {@link #getEOPHistory(IERSConventions, boolean)}, {@link EOPHistory#getStartDate()},
108  * and {@link EOPHistory#getEndDate()}.
109  * <p>
110  * For more information on configuring the EOP data Orekit uses see
111  * <a href="https://gitlab.orekit.org/orekit/orekit/blob/master/src/site/markdown/configuration.md">
112  * https://gitlab.orekit.org/orekit/orekit/blob/master/src/site/markdown/configuration.md</a>.
113  * <p>
114  * Here is a schematic representation of the predefined reference frames tree:
115  * </p>
116  * <pre>
117  *                                                                  GCRF
118  *                                                                    |
119  *                                                 |-----------------------------------------------
120  *                                                 |                         |     Frame bias     |
121  *                                                 |                         |                 EME2000
122  *                                                 |                         |                    |
123  *                                                 |                         | Precession effects |
124  *                                                 |                         |                    |
125  *           Bias, Precession and Nutation effects |                        MOD                  MOD  (Mean Equator Of Date)
126  *                                                 |                         |             w/o EOP corrections
127  *                                                 |                         |  Nutation effects  |
128  *    (Celestial Intermediate Reference Frame)   CIRF                        |                    |
129  *                                                 |                        TOD                  TOD  (True Equator Of Date)
130  *                          Earth natural rotation |                         |             w/o EOP corrections
131  *                                                 |-------------            |    Sidereal Time   |
132  *                                                 |            |            |                    |
133  *  (Terrestrial Intermediate Reference Frame)   TIRF         TIRF         GTOD                 GTOD  (Greenwich True Of Date)
134  *                                                 |    w/o tidal effects                  w/o EOP corrections
135  *                                     Pole motion |            |                                 |
136  *                                                 |            |                                 |-------------
137  *                                                 |            |                                 |            |
138  * (International Terrestrial Reference Frame)   ITRF         ITRF                              ITRF        VEIS1950
139  *                                                 |    w/o tidal effects                   equinox-based
140  *                                                 |            |
141  *                                           other ITRF     other ITRF
142  *                                                      w/o tidal effects
143  * </pre>
144  * <p>
145  * This is a utility class, so its constructor is private.
146  * </p>
147  * @author Guylaine Prat
148  * @author Luc Maisonobe
149  * @author Pascal Parraud
150  * @see Frames
151  */
152 public class FramesFactory {
153 
154     /* These constants were left here instead of being moved to LazyLoadedFrames because
155      * they are public.
156      */
157 
158     /** Default regular expression for the Rapid Data and Prediction EOP columns files (IAU1980 compatibles). */
159     public static final String RAPID_DATA_PREDICTION_COLUMNS_1980_FILENAME = "^finals\\.[^.]*$";
160 
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 
164     /** Default regular expression for the EOPC04 files (IAU1980 compatibles). */
165     public static final String EOPC04_1980_FILENAME = "^eopc04_\\d\\d\\.(\\d\\d)$";
166 
167     /** Default regular expression for the BulletinB files (IAU1980 compatibles). */
168     public static final String BULLETINB_1980_FILENAME = "^bulletinb(_IAU1980)?((-\\d\\d\\d\\.txt)|(\\.\\d\\d\\d))$";
169 
170     /** Default regular expression for the Rapid Data and Prediction EOP columns files (IAU2000 compatibles). */
171     public static final String RAPID_DATA_PREDICITON_COLUMNS_2000_FILENAME = "^finals2000A\\.[^.]*$";
172 
173     /** Default regular expression for the Rapid Data and Prediction EOP XML files (IAU2000 compatibles). */
174     public static final String RAPID_DATA_PREDICITON_XML_2000_FILENAME = "^finals2000A\\..*\\.xml$";
175 
176     /** Default regular expression for the EOPC04 files (IAU2000 compatibles). */
177     public static final String EOPC04_2000_FILENAME = "^eopc04_\\d\\d_IAU2000\\.(\\d\\d)$";
178 
179     /** Default regular expression for the BulletinB files (IAU2000 compatibles). */
180     public static final String BULLETINB_2000_FILENAME = "^bulletinb(_IAU2000)?((-\\d\\d\\d\\.txt)|(\\.\\d\\d\\d))$";
181 
182     /** Default regular expression for the BulletinA files (IAU1980 and IAU2000 compatibles). */
183     public static final String BULLETINA_FILENAME = "^bulletina-[ivxlcdm]+-\\d\\d\\d\\.txt$";
184 
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 
193     /**
194      * Get the instance of {@link Frames} that is called by the static methods in this
195      * class.
196      *
197      * @return the reference frames used by this factory.
198      */
199     @DefaultDataContext
200     public static LazyLoadedFrames getFrames() {
201         return DataContext.getDefault().getFrames();
202     }
203 
204     /** Add the default loaders EOP history (IAU 1980 precession/nutation).
205      * <p>
206      * The default loaders look for IERS EOP C04 and bulletins B files. They
207      * correspond to {@link IERSConventions#IERS_1996 IERS 1996} conventions.
208      * </p>
209      * @param rapidDataColumnsSupportedNames regular expression for supported
210      * rapid data columns EOP files names
211      * (may be null if the default IERS file names are used)
212      * @param rapidDataXMLSupportedNames regular expression for supported
213      * rapid data XML EOP files names
214      * (may be null if the default IERS file names are used)
215      * @param eopC04SupportedNames regular expression for supported EOP C04 files names
216      * (may be null if the default IERS file names are used)
217      * @param bulletinBSupportedNames regular expression for supported bulletin B files names
218      * (may be null if the default IERS file names are used)
219      * @param bulletinASupportedNames regular expression for supported bulletin A files names
220      * (may be null if the default IERS file names are used)
221      * @see <a href="http://hpiers.obspm.fr/eoppc/eop/eopc04/">IERS EOP C04 files</a>
222      * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader)
223      * @see #clearEOPHistoryLoaders()
224      * @see #addDefaultEOP2000HistoryLoaders(String, String, String, String, String)
225      */
226     @DefaultDataContext
227     public static void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupportedNames,
228                                                        final String rapidDataXMLSupportedNames,
229                                                        final String eopC04SupportedNames,
230                                                        final String bulletinBSupportedNames,
231                                                        final String bulletinASupportedNames) {
232         getFrames().addDefaultEOP1980HistoryLoaders(
233                 rapidDataColumnsSupportedNames,
234                 rapidDataXMLSupportedNames,
235                 eopC04SupportedNames,
236                 bulletinBSupportedNames,
237                 bulletinASupportedNames);
238     }
239 
240     /** Add the default loaders for EOP history (IAU 2000/2006 precession/nutation).
241      * <p>
242      * The default loaders look for IERS EOP C04 and bulletins B files. They
243      * correspond to both {@link IERSConventions#IERS_2003 IERS 2003} and {@link
244      * IERSConventions#IERS_2010 IERS 2010} conventions.
245      * </p>
246      * @param rapidDataColumnsSupportedNames regular expression for supported
247      * rapid data columns EOP files names
248      * (may be null if the default IERS file names are used)
249      * @param rapidDataXMLSupportedNames regular expression for supported
250      * rapid data XML EOP files names
251      * (may be null if the default IERS file names are used)
252      * @param eopC04SupportedNames regular expression for supported EOP C04 files names
253      * (may be null if the default IERS file names are used)
254      * @param bulletinBSupportedNames regular expression for supported bulletin B files names
255      * (may be null if the default IERS file names are used)
256      * @param bulletinASupportedNames regular expression for supported bulletin A files names
257      * (may be null if the default IERS file names are used)
258      * @see <a href="http://hpiers.obspm.fr/eoppc/eop/eopc04/">IERS EOP C04 files</a>
259      * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader)
260      * @see #clearEOPHistoryLoaders()
261      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String)
262      */
263     @DefaultDataContext
264     public static void addDefaultEOP2000HistoryLoaders(final String rapidDataColumnsSupportedNames,
265                                                        final String rapidDataXMLSupportedNames,
266                                                        final String eopC04SupportedNames,
267                                                        final String bulletinBSupportedNames,
268                                                        final String bulletinASupportedNames) {
269         getFrames().addDefaultEOP2000HistoryLoaders(
270                 rapidDataColumnsSupportedNames,
271                 rapidDataXMLSupportedNames,
272                 eopC04SupportedNames,
273                 bulletinBSupportedNames,
274                 bulletinASupportedNames);
275     }
276 
277     /** Add a loader for Earth Orientation Parameters history.
278      * @param conventions IERS conventions to which EOP history applies
279      * @param loader custom loader to add for the EOP history
280      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String)
281      * @see #clearEOPHistoryLoaders()
282      */
283     @DefaultDataContext
284     public static void addEOPHistoryLoader(final IERSConventions conventions, final EOPHistoryLoader loader) {
285         getFrames().addEOPHistoryLoader(conventions, loader);
286     }
287 
288     /** Clear loaders for Earth Orientation Parameters history.
289      * @see #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader)
290      * @see #addDefaultEOP1980HistoryLoaders(String, String, String, String, String)
291      */
292     @DefaultDataContext
293     public static void clearEOPHistoryLoaders() {
294         getFrames().clearEOPHistoryLoaders();
295     }
296 
297     /** Set the threshold to check EOP continuity.
298      * <p>
299      * The default threshold (used if this method is never called)
300      * is 5 Julian days. If after loading EOP entries some holes
301      * between entries exceed this threshold, an exception will
302      * be triggered.
303      * </p>
304      * <p>
305      * One case when calling this method is really useful is for
306      * applications that use a single Bulletin A, as these bulletins
307      * have a roughly one month wide hole for the first bulletin of
308      * each month, which contains older final data in addition to the
309      * rapid data and the predicted data.
310      * </p>
311      * @param threshold threshold to use for checking EOP continuity (in seconds)
312      */
313     @DefaultDataContext
314     public static void setEOPContinuityThreshold(final double threshold) {
315         getFrames().setEOPContinuityThreshold(threshold);
316     }
317 
318     /** Get Earth Orientation Parameters history.
319      * <p>
320      * If no {@link EOPHistoryLoader} has been added by calling {@link
321      * #addEOPHistoryLoader(IERSConventions, EOPHistoryLoader) addEOPHistoryLoader}
322      * or if {@link #clearEOPHistoryLoaders() clearEOPHistoryLoaders} has been
323      * called afterwards, the {@link #addDefaultEOP1980HistoryLoaders(String, String,
324      * String, String, String)} and {@link #addDefaultEOP2000HistoryLoaders(String,
325      * String, String, String, String)} methods will be called automatically with
326      * supported file names parameters all set to null, in order to get the default
327      * loaders configuration.
328      * </p>
329      * @param conventions conventions for which EOP history is requested
330      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
331      * @return Earth Orientation Parameters history
332      */
333     @DefaultDataContext
334     public static EOPHistory getEOPHistory(final IERSConventions conventions, final boolean simpleEOP) {
335         return getFrames().getEOPHistory(conventions, simpleEOP);
336     }
337 
338     /** Get one of the predefined frames.
339      * @param factoryKey key of the frame within the factory
340      * @return the predefined frame
341      */
342     @DefaultDataContext
343     public static Frame getFrame(final Predefined factoryKey) {
344         return getFrames().getFrame(factoryKey);
345     }
346 
347     /** Get the unique GCRF frame.
348      * <p>The GCRF frame is the root frame in the frame tree.</p>
349      * @return the unique instance of the GCRF frame
350      */
351     @DefaultDataContext
352     public static Frame getGCRF() {
353         return getFrames().getGCRF();
354     }
355 
356     /** Get the unique ICRF frame.
357      * <p>The ICRF frame is centered at solar system barycenter and aligned
358      * with GCRF.</p>
359      * @return the unique instance of the ICRF frame
360      */
361     @DefaultDataContext
362     public static Frame getICRF() {
363         return getFrames().getICRF();
364     }
365 
366     /** Get the ecliptic frame.
367      * The IAU defines the ecliptic as "the plane perpendicular to the mean heliocentric
368      * orbital angular momentum vector of the Earth-Moon barycentre in the BCRS (IAU 2006
369      * Resolution B1)." The +z axis is aligned with the angular momentum vector, and the +x
370      * axis is aligned with +x axis of {@link FramesFactory#getMOD(IERSConventions) MOD}.
371      *
372      * <p> This implementation agrees with the JPL 406 ephemerides to within 0.5 arc seconds.
373      * @param conventions IERS conventions to apply
374      * @return the selected reference frame singleton.
375      */
376     @DefaultDataContext
377     public static Frame getEcliptic(final IERSConventions conventions) {
378         return getFrames().getEcliptic(conventions);
379     }
380 
381     /** Get the unique EME2000 frame.
382      * <p>The EME2000 frame is also called the J2000 frame.
383      * The former denomination is preferred in Orekit.</p>
384      * @return the unique instance of the EME2000 frame
385      */
386     @DefaultDataContext
387     public static FactoryManagedFrame getEME2000() {
388         return getFrames().getEME2000();
389     }
390 
391     /** Get an unspecified International Terrestrial Reference Frame.
392      * <p>
393      * The frame returned uses the {@link EOPEntry Earth Orientation Parameters}
394      * blindly. So if for example one loads only EOP 14 C04 files to retrieve
395      * the parameters, the frame will be an {@link ITRFVersion#ITRF_2014}. However,
396      * if parameters are loaded from different files types, or even for file
397      * types that changed their reference (like Bulletin A switching from
398      * {@link ITRFVersion#ITRF_2008} to {@link ITRFVersion#ITRF_2014} starting
399      * with Vol. XXXI No. 013 published on 2018-03-29), then the ITRF returned
400      * by this method will jump from one version to another version.
401      * </p>
402      * <p>
403      * IF a specific version of ITRF is needed, then {@link #getITRF(ITRFVersion,
404      * IERSConventions, boolean)} should be used instead.
405      * </p>
406      * @param conventions IERS conventions to apply
407      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
408      * @return the selected reference frame singleton.
409           * @see #getITRF(ITRFVersion, IERSConventions, boolean)
410      * @since 6.1
411      */
412     @DefaultDataContext
413     public static FactoryManagedFrame getITRF(final IERSConventions conventions,
414                                               final boolean simpleEOP) {
415         return getFrames().getITRF(conventions, simpleEOP);
416     }
417 
418     /** Get the TIRF reference frame, ignoring tidal effects.
419      * @param conventions IERS conventions to apply
420      * @return the selected reference frame singleton.
421           * library cannot be read.
422      */
423     @DefaultDataContext
424     public static FactoryManagedFrame getTIRF(final IERSConventions conventions) {
425         return getFrames().getTIRF(conventions);
426     }
427 
428     /** Get an specific International Terrestrial Reference Frame.
429      * <p>
430      * Note that if a specific version of ITRF is required, then {@code simpleEOP}
431      * should most probably be set to {@code false}, as ignoring tidal effects
432      * has an effect of the same order of magnitude as the differences between
433      * the various {@link ITRFVersion ITRF versions}.
434      * </p>
435      * @param version ITRF version
436      * @param conventions IERS conventions to apply
437      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
438      * @return the selected reference frame singleton.
439           * @since 9.2
440      */
441     @DefaultDataContext
442     public static VersionedITRF getITRF(final ITRFVersion version,
443                                         final IERSConventions conventions,
444                                         final boolean simpleEOP) {
445         return getFrames().getITRF(version, conventions, simpleEOP);
446     }
447 
448     /** Get the TIRF reference frame.
449      * @param conventions IERS conventions to apply
450      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
451      * @return the selected reference frame singleton.
452      * @since 6.1
453      */
454     @DefaultDataContext
455     public static FactoryManagedFrame getTIRF(final IERSConventions conventions,
456                                               final boolean simpleEOP) {
457         return getFrames().getTIRF(conventions, simpleEOP);
458     }
459 
460     /** Get the CIRF2000 reference frame.
461      * @param conventions IERS conventions to apply
462      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
463      * @return the selected reference frame singleton.
464      */
465     @DefaultDataContext
466     public static FactoryManagedFrame getCIRF(final IERSConventions conventions,
467                                               final boolean simpleEOP) {
468         return getFrames().getCIRF(conventions, simpleEOP);
469     }
470 
471     /** Get the VEIS 1950 reference frame.
472      * <p>Its parent frame is the GTOD frame with IERS 1996 conventions without EOP corrections.<p>
473      * @return the selected reference frame singleton.
474      */
475     @DefaultDataContext
476     public static FactoryManagedFrame getVeis1950() {
477         return getFrames().getVeis1950();
478     }
479 
480     /** Get the equinox-based ITRF reference frame.
481      * @param conventions IERS conventions to apply
482      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
483      * @return the selected reference frame singleton.
484           * @since 6.1
485      */
486     @DefaultDataContext
487     public static FactoryManagedFrame getITRFEquinox(final IERSConventions conventions,
488                                                      final boolean simpleEOP) {
489         return getFrames().getITRFEquinox(conventions, simpleEOP);
490     }
491 
492     /** Get the GTOD reference frame.
493      * <p>
494      * The applyEOPCorr parameter is available mainly for testing purposes or for
495      * consistency with legacy software that don't handle EOP correction parameters.
496      * Beware that setting this parameter to {@code false} leads to crude accuracy
497      * (order of magnitudes for errors might be above 250m in LEO and 1400m in GEO).
498      * For this reason, setting this parameter to false is restricted to {@link
499      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
500      * IERSConventions IERS conventions} cannot be freely chosen here.
501      * </p>
502      * @param applyEOPCorr if true, EOP corrections are applied (here, dut1 and lod)
503      * @return the selected reference frame singleton.
504      */
505     @DefaultDataContext
506     public static FactoryManagedFrame getGTOD(final boolean applyEOPCorr) {
507         return getFrames().getGTOD(applyEOPCorr);
508     }
509 
510     /** Get the GTOD reference frame.
511      * @param conventions IERS conventions to apply
512      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
513      * @return the selected reference frame singleton.
514      */
515     @DefaultDataContext
516     public static FactoryManagedFrame getGTOD(final IERSConventions conventions,
517                                               final boolean simpleEOP) {
518         return getFrames().getGTOD(conventions, simpleEOP);
519     }
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 
539     /** Get the TOD reference frame.
540      * @param conventions IERS conventions to apply
541      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
542      * @return the selected reference frame singleton.
543      */
544     @DefaultDataContext
545     public static FactoryManagedFrame getTOD(final IERSConventions conventions,
546                                              final boolean simpleEOP) {
547         return getFrames().getTOD(conventions, simpleEOP);
548     }
549 
550     /** Get the MOD reference frame.
551      * <p>
552      * The applyEOPCorr parameter is available mainly for testing purposes or for
553      * consistency with legacy software that don't handle EOP correction parameters.
554      * Beware that setting this parameter to {@code false} leads to crude accuracy
555      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
556      * For this reason, setting this parameter to false is restricted to {@link
557      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence the {@link
558      * IERSConventions IERS conventions} cannot be freely chosen here.
559      * </p>
560      * @param applyEOPCorr if true, EOP corrections are applied (EME2000/GCRF bias compensation)
561      * @return the selected reference frame singleton.
562      */
563     @DefaultDataContext
564     public static FactoryManagedFrame getMOD(final boolean applyEOPCorr) {
565         return getFrames().getMOD(applyEOPCorr);
566     }
567 
568     /** Get the MOD reference frame.
569      * @param conventions IERS conventions to apply
570      * @return the selected reference frame singleton.
571      */
572     @DefaultDataContext
573     public static FactoryManagedFrame getMOD(final IERSConventions conventions) {
574         return getFrames().getMOD(conventions);
575     }
576 
577     /** Get the TEME reference frame.
578      * <p>
579      * The TEME frame is used for the SGP4 model in TLE propagation. This frame has <em>no</em>
580      * official definition and there are some ambiguities about whether it should be used
581      * as "of date" or "of epoch". This frame should therefore be used <em>only</em> for
582      * TLE propagation and not for anything else, as recommended by the CCSDS Orbit Data Message
583      * blue book.
584      * </p>
585      * @return the selected reference frame singleton.
586      */
587     @DefaultDataContext
588     public static FactoryManagedFrame getTEME() {
589         return getFrames().getTEME();
590     }
591 
592     /** Get the PZ-90.11 (Parametry Zemly  – 1990.11) reference frame.
593      * <p>
594      * The PZ-90.11 reference system was updated on all operational
595      * GLONASS satellites starting from 3:00 pm on December 31, 2013.
596      * </p>
597      * <p>
598      * The transition between parent frame (ITRF-2008) and PZ-90.11 frame is performed using
599      * a seven parameters Helmert transformation.
600      * <pre>
601      *    From       To      ΔX(m)   ΔY(m)   ΔZ(m)   RX(mas)   RY(mas)  RZ(mas)   Epoch
602      * ITRF-2008  PZ-90.11  +0.003  +0.001  -0.000   +0.019    -0.042   +0.002     2010
603      * </pre>
604      * @see "Springer Handbook of Global Navigation Satellite Systems, Peter Teunissen & Oliver Montenbruck"
605      *
606      * @param convention IERS conventions to apply
607      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
608      * @return the selected reference frame singleton.
609      */
610     @DefaultDataContext
611     public static FactoryManagedFrame getPZ9011(final IERSConventions convention,
612                                                 final boolean simpleEOP) {
613         return getFrames().getPZ9011(convention, simpleEOP);
614     }
615 
616     /** Get the transform between two frames, suppressing all interpolation.
617      * <p>
618      * This method is similar to {@link Frame#getTransformTo(Frame, AbsoluteDate)}
619      * except it removes the performance enhancing interpolation features that are
620      * added by the {@link FramesFactory factory} to some frames, in order to focus
621      * on accuracy. The interpolation features are intended to save processing time
622      * by avoiding doing some lengthy computation like nutation evaluation at each
623      * time step and caching some results. This method can be used to avoid this,
624      * when very high accuracy is desired, or for testing purposes. It should be
625      * used with care, as doing the full computation is <em>really</em> costly for
626      * some frames.
627      * </p>
628      * @param from frame from which transformation starts
629      * @param to frame to which transformation ends
630      * @param date date of the transform
631      * @return transform between the two frames, avoiding interpolation
632      */
633     public static Transform getNonInterpolatingTransform(final Frame from, final Frame to,
634                                                          final AbsoluteDate date) {
635 
636         // common ancestor to both frames in the frames tree
637         Frame currentF = from.getDepth() > to.getDepth() ? from.getAncestor(from.getDepth() - to.getDepth()) : from;
638         Frame currentT = from.getDepth() > to.getDepth() ? to : to.getAncestor(to.getDepth() - from.getDepth());
639         while (currentF != currentT) {
640             currentF = currentF.getParent();
641             currentT = currentT.getParent();
642         }
643         final Frame common = currentF;
644 
645         // transform from common to origin
646         Transform commonToOrigin = Transform.IDENTITY;
647         for (Frame frame = from; frame != common; frame = frame.getParent()) {
648             commonToOrigin = new Transform(date,
649                                              peel(frame.getTransformProvider()).getTransform(date),
650                                              commonToOrigin);
651         }
652 
653         // transform from destination up to common
654         Transform commonToDestination = Transform.IDENTITY;
655         for (Frame frame = to; frame != common; frame = frame.getParent()) {
656             commonToDestination = new Transform(date,
657                                                 peel(frame.getTransformProvider()).getTransform(date),
658                                                 commonToDestination);
659         }
660 
661         // transform from origin to destination via common
662         return new Transform(date, commonToOrigin.getInverse(), commonToDestination);
663 
664     }
665 
666     /* The methods below are static helper methods for Frame and TransformProvider. */
667 
668     /** Get the transform between two frames, suppressing all interpolation.
669      * <p>
670      * This method is similar to {@link Frame#getTransformTo(Frame, AbsoluteDate)}
671      * except it removes the performance enhancing interpolation features that are
672      * added by the {@link FramesFactory factory} to some frames, in order to focus
673      * on accuracy. The interpolation features are intended to save processing time
674      * by avoiding doing some lengthy computation like nutation evaluation at each
675      * time step and caching some results. This method can be used to avoid this,
676      * when very high accuracy is desired, or for testing purposes. It should be
677      * used with care, as doing the full computation is <em>really</em> costly for
678      * some frames.
679      * </p>
680      * @param from frame from which transformation starts
681      * @param to frame to which transformation ends
682      * @param date date of the transform
683      * @param <T> type of the field elements
684      * @return transform between the two frames, avoiding interpolation
685      * @since 9.0
686      */
687     public static <T extends CalculusFieldElement<T>> FieldTransform<T> getNonInterpolatingTransform(final Frame from, final Frame to,
688                                                                                                  final FieldAbsoluteDate<T> date) {
689 
690         // common ancestor to both frames in the frames tree
691         Frame currentF = from.getDepth() > to.getDepth() ? from.getAncestor(from.getDepth() - to.getDepth()) : from;
692         Frame currentT = from.getDepth() > to.getDepth() ? to : to.getAncestor(to.getDepth() - from.getDepth());
693         while (currentF != currentT) {
694             currentF = currentF.getParent();
695             currentT = currentT.getParent();
696         }
697         final Frame common = currentF;
698 
699         // transform from common to origin
700         FieldTransform<T> commonToOrigin = FieldTransform.getIdentity(date.getField());
701         for (Frame frame = from; frame != common; frame = frame.getParent()) {
702             commonToOrigin = new FieldTransform<>(date,
703                                                    peel(frame.getTransformProvider()).getTransform(date),
704                                                    commonToOrigin);
705         }
706 
707         // transform from destination up to common
708         FieldTransform<T> commonToDestination = FieldTransform.getIdentity(date.getField());
709         for (Frame frame = to; frame != common; frame = frame.getParent()) {
710             commonToDestination = new FieldTransform<>(date,
711                                                        peel(frame.getTransformProvider()).getTransform(date),
712                                                        commonToDestination);
713         }
714 
715         // transform from origin to destination via common
716         return new FieldTransform<>(date, commonToOrigin.getInverse(), commonToDestination);
717 
718     }
719 
720     /** Retrieve EOP from a frame hierarchy.
721      * <p>
722      * The frame hierarchy tree is walked from specified frame up to root
723      * traversing parent frames, and the providers are checked to see if they
724      * reference EOP history. The first EOP history found is returned.
725      * </p>
726      * @param start frame from which to start search, will typically be some
727      * Earth related frame, like a topocentric frame or an ITRF frame
728      * @return EOP history found while walking the frames tree, or null if
729      * no EOP history is found
730      * @since 9.1
731      */
732     public static EOPHistory findEOP(final Frame start) {
733 
734         for (Frame frame = start; frame != null; frame = frame.getParent()) {
735 
736             TransformProvider peeled = frame.getTransformProvider();
737 
738             boolean peeling = true;
739             while (peeling) {
740                 if (peeled instanceof InterpolatingTransformProvider) {
741                     peeled = ((InterpolatingTransformProvider) peeled).getRawProvider();
742                 } else if (peeled instanceof ShiftingTransformProvider) {
743                     peeled = ((ShiftingTransformProvider) peeled).getRawProvider();
744                 } else if (peeled instanceof EOPBasedTransformProvider &&
745                            ((EOPBasedTransformProvider) peeled).getEOPHistory() != null) {
746                     return ((EOPBasedTransformProvider) peeled).getEOPHistory();
747                 } else {
748                     peeling = false;
749                 }
750             }
751 
752         }
753 
754         // no history found
755         return null;
756 
757     }
758 
759     /** Peel interpolation and shifting from a transform provider.
760      * @param provider transform provider to peel
761      * @return peeled transform provider
762      */
763     private static TransformProvider peel(final TransformProvider provider) {
764 
765         TransformProvider peeled = provider;
766 
767         boolean peeling = true;
768         while (peeling) {
769             if (peeled instanceof InterpolatingTransformProvider) {
770                 peeled = ((InterpolatingTransformProvider) peeled).getRawProvider();
771             } else if (peeled instanceof ShiftingTransformProvider) {
772                 peeled = ((ShiftingTransformProvider) peeled).getRawProvider();
773             } else if (peeled instanceof EOPBasedTransformProvider &&
774                        ((EOPBasedTransformProvider) peeled).getEOPHistory() != null &&
775                        ((EOPBasedTransformProvider) peeled).getEOPHistory().usesInterpolation()) {
776                 peeled = ((EOPBasedTransformProvider) peeled).getNonInterpolatingProvider();
777             } else {
778                 peeling = false;
779             }
780         }
781 
782         return peeled;
783 
784     }
785 
786 }