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