1   /* Contributed in the public domain.
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 java.io.Serializable;
20  import java.util.HashMap;
21  import java.util.Map;
22  import java.util.function.Supplier;
23  
24  import org.orekit.bodies.CelestialBodies;
25  import org.orekit.errors.OrekitInternalError;
26  import org.orekit.time.AbsoluteDate;
27  import org.orekit.time.TimeScales;
28  import org.orekit.time.UT1Scale;
29  import org.orekit.utils.AngularDerivativesFilter;
30  import org.orekit.utils.CartesianDerivativesFilter;
31  import org.orekit.utils.Constants;
32  import org.orekit.utils.IERSConventions;
33  import org.orekit.utils.OrekitConfiguration;
34  
35  /**
36   * This class is an implementation of {@link Frames} that creates frames when they are
37   * first used and uses synchronization to ensure that each frame is only created once.
38   *
39   * @author Guylaine Prat
40   * @author Luc Maisonobe
41   * @author Pascal Parraud
42   * @author Evan Ward
43   * @see LazyLoadedFrames
44   * @see #getEOPHistory(IERSConventions, boolean)
45   * @since 10.1
46   */
47  public abstract class AbstractFrames implements Frames {
48  
49      /** Provider of common time scales. */
50      private final TimeScales timeScales;
51      /** Provider of the ICRF frame, usually delegated to {@link CelestialBodies}. */
52      private final Supplier<Frame> icrfSupplier;
53      /** Predefined frames. */
54      private final Map<Predefined, FactoryManagedFrame> frames;
55      /** Predefined versioned ITRF frames. */
56      private final Map<ITRFKey, VersionedITRF> versionedItrfFrames;
57  
58      /**
59       * Simple constructor.
60       *
61       * @param timeScales   to use when creating frames.
62       * @param icrfSupplier used to implement {@link #getICRF()};
63       */
64      protected AbstractFrames(final TimeScales timeScales,
65                               final Supplier<Frame> icrfSupplier) {
66          this.timeScales = timeScales;
67          this.icrfSupplier = icrfSupplier;
68          this.frames = new HashMap<>();
69          this.versionedItrfFrames = new HashMap<>();
70      }
71  
72      @Override
73      public Frame getFrame(final Predefined factoryKey) {
74          switch (factoryKey) {
75              case GCRF :
76                  return getGCRF();
77              case ICRF :
78                  return getICRF();
79              case ECLIPTIC_CONVENTIONS_1996 :
80                  return getEcliptic(IERSConventions.IERS_1996);
81              case ECLIPTIC_CONVENTIONS_2003 :
82                  return getEcliptic(IERSConventions.IERS_2003);
83              case ECLIPTIC_CONVENTIONS_2010 :
84                  return getEcliptic(IERSConventions.IERS_2010);
85              case EME2000 :
86                  return getEME2000();
87              case ITRF_CIO_CONV_2010_SIMPLE_EOP :
88                  return getITRF(IERSConventions.IERS_2010, true);
89              case ITRF_CIO_CONV_2010_ACCURATE_EOP :
90                  return getITRF(IERSConventions.IERS_2010, false);
91              case ITRF_CIO_CONV_2003_SIMPLE_EOP :
92                  return getITRF(IERSConventions.IERS_2003, true);
93              case ITRF_CIO_CONV_2003_ACCURATE_EOP :
94                  return getITRF(IERSConventions.IERS_2003, false);
95              case ITRF_CIO_CONV_1996_SIMPLE_EOP :
96                  return getITRF(IERSConventions.IERS_1996, true);
97              case ITRF_CIO_CONV_1996_ACCURATE_EOP :
98                  return getITRF(IERSConventions.IERS_1996, false);
99              case ITRF_EQUINOX_CONV_2010_SIMPLE_EOP :
100                 return getITRFEquinox(IERSConventions.IERS_2010, true);
101             case ITRF_EQUINOX_CONV_2010_ACCURATE_EOP :
102                 return getITRFEquinox(IERSConventions.IERS_2010, false);
103             case ITRF_EQUINOX_CONV_2003_SIMPLE_EOP :
104                 return getITRFEquinox(IERSConventions.IERS_2003, true);
105             case ITRF_EQUINOX_CONV_2003_ACCURATE_EOP :
106                 return getITRFEquinox(IERSConventions.IERS_2003, false);
107             case ITRF_EQUINOX_CONV_1996_SIMPLE_EOP :
108                 return getITRFEquinox(IERSConventions.IERS_1996, true);
109             case ITRF_EQUINOX_CONV_1996_ACCURATE_EOP :
110                 return getITRFEquinox(IERSConventions.IERS_1996, false);
111             case TIRF_CONVENTIONS_2010_SIMPLE_EOP :
112                 return getTIRF(IERSConventions.IERS_2010, true);
113             case TIRF_CONVENTIONS_2010_ACCURATE_EOP :
114                 return getTIRF(IERSConventions.IERS_2010, false);
115             case TIRF_CONVENTIONS_2003_SIMPLE_EOP :
116                 return getTIRF(IERSConventions.IERS_2003, true);
117             case TIRF_CONVENTIONS_2003_ACCURATE_EOP :
118                 return getTIRF(IERSConventions.IERS_2003, false);
119             case TIRF_CONVENTIONS_1996_SIMPLE_EOP :
120                 return getTIRF(IERSConventions.IERS_1996, true);
121             case TIRF_CONVENTIONS_1996_ACCURATE_EOP :
122                 return getTIRF(IERSConventions.IERS_1996, false);
123             case CIRF_CONVENTIONS_2010_ACCURATE_EOP :
124                 return getCIRF(IERSConventions.IERS_2010, false);
125             case CIRF_CONVENTIONS_2010_SIMPLE_EOP :
126                 return getCIRF(IERSConventions.IERS_2010, true);
127             case CIRF_CONVENTIONS_2003_ACCURATE_EOP :
128                 return getCIRF(IERSConventions.IERS_2003, false);
129             case CIRF_CONVENTIONS_2003_SIMPLE_EOP :
130                 return getCIRF(IERSConventions.IERS_2003, true);
131             case CIRF_CONVENTIONS_1996_ACCURATE_EOP :
132                 return getCIRF(IERSConventions.IERS_1996, false);
133             case CIRF_CONVENTIONS_1996_SIMPLE_EOP :
134                 return getCIRF(IERSConventions.IERS_1996, true);
135             case VEIS_1950 :
136                 return getVeis1950();
137             case GTOD_WITHOUT_EOP_CORRECTIONS :
138                 return getGTOD(IERSConventions.IERS_1996, false, true);
139             case GTOD_CONVENTIONS_2010_ACCURATE_EOP :
140                 return getGTOD(IERSConventions.IERS_2010, true, false);
141             case GTOD_CONVENTIONS_2010_SIMPLE_EOP :
142                 return getGTOD(IERSConventions.IERS_2010, true, true);
143             case GTOD_CONVENTIONS_2003_ACCURATE_EOP :
144                 return getGTOD(IERSConventions.IERS_2003, true, false);
145             case GTOD_CONVENTIONS_2003_SIMPLE_EOP :
146                 return getGTOD(IERSConventions.IERS_2003, true, true);
147             case GTOD_CONVENTIONS_1996_ACCURATE_EOP :
148                 return getGTOD(IERSConventions.IERS_1996, true, false);
149             case GTOD_CONVENTIONS_1996_SIMPLE_EOP :
150                 return getGTOD(IERSConventions.IERS_1996, true, true);
151             case TOD_WITHOUT_EOP_CORRECTIONS :
152                 return getTOD(IERSConventions.IERS_1996, false, true);
153             case TOD_CONVENTIONS_2010_ACCURATE_EOP :
154                 return getTOD(IERSConventions.IERS_2010, true, false);
155             case TOD_CONVENTIONS_2010_SIMPLE_EOP :
156                 return getTOD(IERSConventions.IERS_2010, true, true);
157             case TOD_CONVENTIONS_2003_ACCURATE_EOP :
158                 return getTOD(IERSConventions.IERS_2003, true, false);
159             case TOD_CONVENTIONS_2003_SIMPLE_EOP :
160                 return getTOD(IERSConventions.IERS_2003, true, true);
161             case TOD_CONVENTIONS_1996_ACCURATE_EOP :
162                 return getTOD(IERSConventions.IERS_1996, true, false);
163             case TOD_CONVENTIONS_1996_SIMPLE_EOP :
164                 return getTOD(IERSConventions.IERS_1996, true, true);
165             case MOD_WITHOUT_EOP_CORRECTIONS :
166                 return getMOD(IERSConventions.IERS_1996, false);
167             case MOD_CONVENTIONS_2010 :
168                 return getMOD(IERSConventions.IERS_2010, true);
169             case MOD_CONVENTIONS_2003 :
170                 return getMOD(IERSConventions.IERS_2003, true);
171             case MOD_CONVENTIONS_1996 :
172                 return getMOD(IERSConventions.IERS_1996, true);
173             case TEME :
174                 return getTEME();
175             case PZ90_11 :
176                 return getPZ9011(IERSConventions.IERS_2010, true);
177             default :
178                 // this should never happen
179                 throw new OrekitInternalError(null);
180         }
181     }
182 
183     @Override
184     public Frame getGCRF() {
185         return Frame.getRoot();
186     }
187 
188     @Override
189     public Frame getICRF() {
190         return icrfSupplier.get();
191     }
192 
193     @Override
194     public Frame getEcliptic(final IERSConventions conventions) {
195         synchronized (this) {
196 
197             final Predefined factoryKey;
198             switch (conventions) {
199                 case IERS_1996 :
200                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_1996;
201                     break;
202                 case IERS_2003 :
203                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_2003;
204                     break;
205                 case IERS_2010 :
206                     factoryKey = Predefined.ECLIPTIC_CONVENTIONS_2010;
207                     break;
208                 default :
209 
210                     // this should never happen
211                     throw new OrekitInternalError(null);
212             }
213             final Frame parent = getMOD(conventions);
214 
215             // try to find an already built frame
216             FactoryManagedFrame frame = frames.get(factoryKey);
217 
218             if (frame == null) {
219                 // it's the first time we need this frame, build it and store it
220                 final EclipticProvider provider =
221                         new EclipticProvider(conventions, getTimeScales());
222                 frame = new FactoryManagedFrame(parent, provider, true, factoryKey);
223                 frames.put(factoryKey, frame);
224             }
225 
226             return frame;
227 
228         }
229     }
230 
231     @Override
232     public FactoryManagedFrame getEME2000() {
233         synchronized (this) {
234 
235             // try to find an already built frame
236             FactoryManagedFrame frame = frames.get(Predefined.EME2000);
237 
238             if (frame == null) {
239                 // it's the first time we need this frame, build it and store it
240                 frame = new FactoryManagedFrame(getGCRF(), new EME2000Provider(), true, Predefined.EME2000);
241                 frames.put(Predefined.EME2000, frame);
242             }
243 
244             return frame;
245 
246         }
247     }
248 
249     @Override
250     public FactoryManagedFrame getITRF(final IERSConventions conventions,
251                                        final boolean simpleEOP) {
252         synchronized (this) {
253 
254             // try to find an already built frame
255             final Predefined factoryKey;
256             switch (conventions) {
257                 case IERS_1996 :
258                     factoryKey = simpleEOP ?
259                             Predefined.ITRF_CIO_CONV_1996_SIMPLE_EOP :
260                             Predefined.ITRF_CIO_CONV_1996_ACCURATE_EOP;
261                     break;
262                 case IERS_2003 :
263                     factoryKey = simpleEOP ?
264                             Predefined.ITRF_CIO_CONV_2003_SIMPLE_EOP :
265                             Predefined.ITRF_CIO_CONV_2003_ACCURATE_EOP;
266                     break;
267                 case IERS_2010 :
268                     factoryKey = simpleEOP ?
269                             Predefined.ITRF_CIO_CONV_2010_SIMPLE_EOP :
270                             Predefined.ITRF_CIO_CONV_2010_ACCURATE_EOP;
271                     break;
272                 default :
273                     // this should never happen
274                     throw new OrekitInternalError(null);
275             }
276             FactoryManagedFrame frame = frames.get(factoryKey);
277 
278             if (frame == null) {
279                 // it's the first time we need this frame, build it and store it
280                 final Frame tirfFrame = getTIRF(conventions, simpleEOP);
281                 final TIRFProvider tirfProvider = (TIRFProvider) tirfFrame.getTransformProvider();
282                 frame = new FactoryManagedFrame(tirfFrame,
283                         new ITRFProvider(tirfProvider.getEOPHistory()),
284                         false, factoryKey);
285                 frames.put(factoryKey, frame);
286             }
287 
288             return frame;
289 
290         }
291     }
292 
293     @Override
294     public VersionedITRF getITRF(final ITRFVersion version,
295                                  final IERSConventions conventions,
296                                  final boolean simpleEOP) {
297         synchronized (this) {
298             // try to find an already built frame
299             final ITRFKey key = new ITRFKey(version, conventions, simpleEOP);
300             VersionedITRF frame = versionedItrfFrames.get(key);
301 
302             if (frame == null) {
303                 // it's the first time we need this frame, build it and store it
304                 final FactoryManagedFrame rawITRF = getITRF(conventions, simpleEOP);
305                 frame = new VersionedITRF(rawITRF.getParent(), version,
306                         (ITRFProvider) rawITRF.getTransformProvider(),
307                         version.toString().replace('_', '-') +
308                                 "/" +
309                                 rawITRF.getName(),
310                         getTimeScales().getTT());
311                 versionedItrfFrames.put(key, frame);
312             }
313 
314             return frame;
315 
316         }
317     }
318 
319     @Override
320     public Frame buildUncachedITRF(final UT1Scale ut1) {
321 
322         // extract EOP history
323         final EOPHistory eopHistory = ut1.getEOPHistory();
324 
325         // build CIRF
326         final TransformProvider shifting =
327                         new ShiftingTransformProvider(new CIRFProvider(eopHistory),
328                                 CartesianDerivativesFilter.USE_PVA,
329                                 AngularDerivativesFilter.USE_R,
330                                 6, Constants.JULIAN_DAY / 24,
331                                 OrekitConfiguration.getCacheSlotsNumber(),
332                                 Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
333         final Frame cirf = new Frame(getGCRF(), shifting, "CIRF (uncached)", true);
334 
335         // build TIRF
336         final Frame tirf = new Frame(cirf, new TIRFProvider(eopHistory, ut1),
337                                           "TIRF (uncached)", false);
338 
339         // build ITRF
340         return new Frame(tirf, new ITRFProvider(eopHistory),
341                          "ITRF (uncached)", false);
342 
343     }
344 
345     @Override
346     public FactoryManagedFrame getTIRF(final IERSConventions conventions) {
347         return getTIRF(conventions, true);
348     }
349 
350     @Override
351     public FactoryManagedFrame getTIRF(final IERSConventions conventions,
352                                        final boolean simpleEOP) {
353         synchronized (this) {
354 
355             // try to find an already built frame
356             final Predefined factoryKey;
357             switch (conventions) {
358                 case IERS_1996 :
359                     factoryKey = simpleEOP ?
360                             Predefined.TIRF_CONVENTIONS_1996_SIMPLE_EOP :
361                             Predefined.TIRF_CONVENTIONS_1996_ACCURATE_EOP;
362                     break;
363                 case IERS_2003 :
364                     factoryKey = simpleEOP ?
365                             Predefined.TIRF_CONVENTIONS_2003_SIMPLE_EOP :
366                             Predefined.TIRF_CONVENTIONS_2003_ACCURATE_EOP;
367                     break;
368                 case IERS_2010 :
369                     factoryKey = simpleEOP ?
370                             Predefined.TIRF_CONVENTIONS_2010_SIMPLE_EOP :
371                             Predefined.TIRF_CONVENTIONS_2010_ACCURATE_EOP;
372                     break;
373                 default :
374                     // this should never happen
375                     throw new OrekitInternalError(null);
376             }
377             FactoryManagedFrame frame = frames.get(factoryKey);
378 
379             if (frame == null) {
380                 // it's the first time we need this frame, build it and store it
381                 final Frame cirf = getCIRF(conventions, simpleEOP);
382                 final ShiftingTransformProvider cirfInterpolating =
383                         (ShiftingTransformProvider) cirf.getTransformProvider();
384                 final CIRFProvider cirfRaw = (CIRFProvider) cirfInterpolating.getRawProvider();
385                 final EOPHistory eopHistory = cirfRaw.getEOPHistory();
386                 final TIRFProvider provider =
387                         new TIRFProvider(eopHistory, getTimeScales().getUT1(conventions, simpleEOP));
388                 frame = new FactoryManagedFrame(cirf, provider, false, factoryKey);
389                 frames.put(factoryKey, frame);
390             }
391 
392             return frame;
393 
394         }
395     }
396 
397     @Override
398     public FactoryManagedFrame getCIRF(final IERSConventions conventions,
399                                        final boolean simpleEOP) {
400         synchronized (this) {
401 
402             // try to find an already built frame
403             final Predefined factoryKey;
404             switch (conventions) {
405                 case IERS_1996 :
406                     factoryKey = simpleEOP ?
407                             Predefined.CIRF_CONVENTIONS_1996_SIMPLE_EOP :
408                             Predefined.CIRF_CONVENTIONS_1996_ACCURATE_EOP;
409                     break;
410                 case IERS_2003 :
411                     factoryKey = simpleEOP ?
412                             Predefined.CIRF_CONVENTIONS_2003_SIMPLE_EOP :
413                             Predefined.CIRF_CONVENTIONS_2003_ACCURATE_EOP;
414                     break;
415                 case IERS_2010 :
416                     factoryKey = simpleEOP ?
417                             Predefined.CIRF_CONVENTIONS_2010_SIMPLE_EOP :
418                             Predefined.CIRF_CONVENTIONS_2010_ACCURATE_EOP;
419                     break;
420                 default :
421                     // this should never happen
422                     throw new OrekitInternalError(null);
423             }
424             FactoryManagedFrame frame = frames.get(factoryKey);
425 
426             if (frame == null) {
427                 // it's the first time we need this frame, build it and store it
428                 final EOPHistory eopHistory = getEOPHistory(conventions, simpleEOP);
429                 final TransformProvider shifting =
430                         new ShiftingTransformProvider(new CIRFProvider(eopHistory),
431                                 CartesianDerivativesFilter.USE_PVA,
432                                 AngularDerivativesFilter.USE_R,
433                                 6, Constants.JULIAN_DAY / 24,
434                                 OrekitConfiguration.getCacheSlotsNumber(),
435                                 Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
436                 frame = new FactoryManagedFrame(getGCRF(), shifting, true, factoryKey);
437                 frames.put(factoryKey, frame);
438             }
439 
440             return frame;
441 
442         }
443     }
444 
445     @Override
446     public FactoryManagedFrame getVeis1950() {
447         synchronized (this) {
448 
449             // try to find an already built frame
450             final Predefined factoryKey = Predefined.VEIS_1950;
451             FactoryManagedFrame frame = frames.get(factoryKey);
452 
453             if (frame == null) {
454                 // it's the first time we need this frame, build it and store it
455                 frame = new FactoryManagedFrame(getGTOD(IERSConventions.IERS_1996, false, true),
456                         new VEISProvider(getTimeScales()), true, factoryKey);
457                 frames.put(factoryKey, frame);
458             }
459 
460             return frame;
461 
462         }
463     }
464 
465     @Override
466     public FactoryManagedFrame getITRFEquinox(final IERSConventions conventions,
467                                               final boolean simpleEOP) {
468         synchronized (this) {
469 
470             // try to find an already built frame
471             final Predefined factoryKey;
472             switch (conventions) {
473                 case IERS_1996 :
474                     factoryKey = simpleEOP ?
475                             Predefined.ITRF_EQUINOX_CONV_1996_SIMPLE_EOP :
476                             Predefined.ITRF_EQUINOX_CONV_1996_ACCURATE_EOP;
477                     break;
478                 case IERS_2003 :
479                     factoryKey = simpleEOP ?
480                             Predefined.ITRF_EQUINOX_CONV_2003_SIMPLE_EOP :
481                             Predefined.ITRF_EQUINOX_CONV_2003_ACCURATE_EOP;
482                     break;
483                 case IERS_2010 :
484                     factoryKey = simpleEOP ?
485                             Predefined.ITRF_EQUINOX_CONV_2010_SIMPLE_EOP :
486                             Predefined.ITRF_EQUINOX_CONV_2010_ACCURATE_EOP;
487                     break;
488                 default :
489                     // this should never happen
490                     throw new OrekitInternalError(null);
491             }
492             FactoryManagedFrame frame = frames.get(factoryKey);
493 
494             if (frame == null) {
495                 // it's the first time we need this frame, build it and store it
496                 final Frame gtod = getGTOD(conventions, true, simpleEOP);
497                 final ShiftingTransformProvider gtodShifting =
498                         (ShiftingTransformProvider) gtod.getTransformProvider();
499                 final GTODProvider gtodRaw    = (GTODProvider) gtodShifting.getRawProvider();
500                 final EOPHistory   eopHistory = gtodRaw.getEOPHistory();
501                 frame = new FactoryManagedFrame(gtod, new ITRFProvider(eopHistory), false, factoryKey);
502                 frames.put(factoryKey, frame);
503             }
504 
505             return frame;
506 
507         }
508     }
509 
510     @Override
511     public FactoryManagedFrame getGTOD(final boolean applyEOPCorr) {
512         return getGTOD(IERSConventions.IERS_1996, applyEOPCorr, true);
513     }
514 
515     @Override
516     public FactoryManagedFrame getGTOD(final IERSConventions conventions,
517                                        final boolean simpleEOP) {
518         return getGTOD(conventions, true, simpleEOP);
519     }
520 
521     /** Get the GTOD 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 250m in LEO and 1400m in GEO).
527      * For this reason, setting this parameter to false is restricted to {@link
528      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
529      * </p>
530      * @param conventions IERS conventions to apply
531      * @param applyEOPCorr if true, EOP corrections are applied (here, dut1 and lod)
532      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
533      * @return the selected reference frame singleton.
534      */
535     private FactoryManagedFrame getGTOD(final IERSConventions conventions,
536                                                final boolean applyEOPCorr,
537                                                final boolean simpleEOP) {
538 
539         synchronized (this) {
540 
541             // try to find an already built frame
542             final Predefined factoryKey;
543             switch (conventions) {
544                 case IERS_1996 :
545                     factoryKey = applyEOPCorr ?
546                             (simpleEOP ? Predefined.GTOD_CONVENTIONS_1996_SIMPLE_EOP : Predefined.GTOD_CONVENTIONS_1996_ACCURATE_EOP) :
547                             Predefined.GTOD_WITHOUT_EOP_CORRECTIONS;
548                     break;
549                 case IERS_2003 :
550                     factoryKey = simpleEOP ?
551                             Predefined.GTOD_CONVENTIONS_2003_SIMPLE_EOP :
552                             Predefined.GTOD_CONVENTIONS_2003_ACCURATE_EOP;
553                     break;
554                 case IERS_2010 :
555                     factoryKey = simpleEOP ? Predefined.GTOD_CONVENTIONS_2010_SIMPLE_EOP :
556                             Predefined.GTOD_CONVENTIONS_2010_ACCURATE_EOP;
557                     break;
558                 default :
559                     // this should never happen
560                     throw new OrekitInternalError(null);
561             }
562             FactoryManagedFrame frame = frames.get(factoryKey);
563 
564             if (frame == null) {
565                 // it's the first time we need this frame, build it and store it
566                 final Frame tod = getTOD(conventions, applyEOPCorr, simpleEOP);
567                 final ShiftingTransformProvider todInterpolating =
568                         (ShiftingTransformProvider) tod.getTransformProvider();
569                 final TODProvider       todRaw     = (TODProvider) todInterpolating.getRawProvider();
570                 final EOPHistory        eopHistory = todRaw.getEOPHistory();
571                 final GTODProvider      gtodRaw    =
572                         new GTODProvider(conventions, eopHistory, getTimeScales());
573                 final TransformProvider gtodShifting =
574                         new ShiftingTransformProvider(gtodRaw,
575                                 CartesianDerivativesFilter.USE_PVA,
576                                 AngularDerivativesFilter.USE_R,
577                                 todInterpolating.getGridPoints(), todInterpolating.getStep(),
578                                 OrekitConfiguration.getCacheSlotsNumber(),
579                                 Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
580                 frame = new FactoryManagedFrame(tod, gtodShifting, false, factoryKey);
581                 frames.put(factoryKey, frame);
582             }
583 
584             return frame;
585 
586         }
587     }
588 
589     @Override
590     public FactoryManagedFrame getTOD(final boolean applyEOPCorr) {
591         return getTOD(IERSConventions.IERS_1996, applyEOPCorr, false);
592     }
593 
594     @Override
595     public FactoryManagedFrame getTOD(final IERSConventions conventions,
596                                       final boolean simpleEOP) {
597         return getTOD(conventions, true, simpleEOP);
598     }
599 
600     /** Get the TOD reference frame.
601      * <p>
602      * The applyEOPCorr parameter is available mainly for testing purposes or for
603      * consistency with legacy software that don't handle EOP correction parameters.
604      * Beware that setting this parameter to {@code false} leads to crude accuracy
605      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
606      * For this reason, setting this parameter to false is restricted to {@link
607      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
608      * </p>
609      * @param conventions IERS conventions to apply
610      * @param applyEOPCorr if true, EOP corrections are applied (here, nutation)
611      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
612      * @return the selected reference frame singleton.
613      */
614     private FactoryManagedFrame getTOD(final IERSConventions conventions,
615                                               final boolean applyEOPCorr,
616                                               final boolean simpleEOP) {
617 
618         synchronized (this) {
619 
620             // try to find an already built frame
621             final Predefined factoryKey;
622             switch (conventions) {
623                 case IERS_1996 :
624                     factoryKey = applyEOPCorr ?
625                             (simpleEOP ? Predefined.TOD_CONVENTIONS_1996_SIMPLE_EOP : Predefined.TOD_CONVENTIONS_1996_ACCURATE_EOP) :
626                             Predefined.TOD_WITHOUT_EOP_CORRECTIONS;
627                     break;
628                 case IERS_2003 :
629                     factoryKey = simpleEOP ?
630                             Predefined.TOD_CONVENTIONS_2003_SIMPLE_EOP :
631                             Predefined.TOD_CONVENTIONS_2003_ACCURATE_EOP;
632                     break;
633                 case IERS_2010 :
634                     factoryKey = simpleEOP ?
635                             Predefined.TOD_CONVENTIONS_2010_SIMPLE_EOP :
636                             Predefined.TOD_CONVENTIONS_2010_ACCURATE_EOP;
637                     break;
638                 default :
639                     // this should never happen
640                     throw new OrekitInternalError(null);
641             }
642             final int interpolationPoints;
643             final int pointsPerDay;
644             if (applyEOPCorr) {
645                 interpolationPoints = 6;
646                 pointsPerDay        = 24;
647             } else {
648                 interpolationPoints = 6;
649                 pointsPerDay        = 8;
650             }
651             FactoryManagedFrame frame = frames.get(factoryKey);
652 
653             if (frame == null) {
654                 // it's the first time we need this frame, build it and store it
655                 final EOPHistory eopHistory = applyEOPCorr ?
656                         getEOPHistory(conventions, simpleEOP) :
657                         null;
658                 final TransformProvider shifting =
659                         new ShiftingTransformProvider(
660                                 new TODProvider(conventions, eopHistory, getTimeScales()),
661                                 CartesianDerivativesFilter.USE_PVA,
662                                 AngularDerivativesFilter.USE_R,
663                                 interpolationPoints, Constants.JULIAN_DAY / pointsPerDay,
664                                 OrekitConfiguration.getCacheSlotsNumber(),
665                                 Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
666                 frame = new FactoryManagedFrame(getMOD(conventions, applyEOPCorr), shifting, true, factoryKey);
667                 frames.put(factoryKey, frame);
668             }
669 
670             return frame;
671 
672         }
673     }
674 
675     @Override
676     public FactoryManagedFrame getMOD(final boolean applyEOPCorr) {
677         return getMOD(IERSConventions.IERS_1996, applyEOPCorr);
678     }
679 
680     @Override
681     public FactoryManagedFrame getMOD(final IERSConventions conventions) {
682         return getMOD(conventions, true);
683     }
684 
685     /** Get the MOD reference frame.
686      * <p>
687      * The applyEOPCorr parameter is available mainly for testing purposes or for
688      * consistency with legacy software that don't handle EOP correction parameters.
689      * Beware that setting this parameter to {@code false} leads to crude accuracy
690      * (order of magnitudes for errors might be above 1m in LEO and 10m in GEO).
691      * For this reason, setting this parameter to false is restricted to {@link
692      * IERSConventions#IERS_1996 IERS 1996} conventions, and hence this method is private.
693      * </p>
694      * @param conventions IERS conventions to apply
695      * @param applyEOPCorr if true, EOP corrections are applied (EME2000/GCRF bias compensation)
696      * @return the selected reference frame singleton.
697      */
698     private FactoryManagedFrame getMOD(final IERSConventions conventions, final boolean applyEOPCorr) {
699 
700         synchronized (this) {
701 
702             final Predefined factoryKey;
703             final Frame parent;
704             switch (conventions) {
705                 case IERS_1996 :
706                     factoryKey = applyEOPCorr ? Predefined.MOD_CONVENTIONS_1996 : Predefined.MOD_WITHOUT_EOP_CORRECTIONS;
707                     parent     = applyEOPCorr ? getGCRF() : getEME2000();
708                     break;
709                 case IERS_2003 :
710                     factoryKey = Predefined.MOD_CONVENTIONS_2003;
711                     // in IERS conventions 2003, the precession angles zetaA, thetaA and zA
712                     // from equation 33 are computed from EME2000, not from GCRF
713                     parent     = getEME2000();
714                     break;
715                 case IERS_2010 :
716                     factoryKey = Predefined.MOD_CONVENTIONS_2010;
717                     // precession angles epsilon0, psiA, omegaA and chiA
718                     // from equations 5.39 and 5.40 are computed from EME2000
719                     parent     = getEME2000();
720                     break;
721                 default :
722                     // this should never happen
723                     throw new OrekitInternalError(null);
724             }
725 
726             // try to find an already built frame
727             FactoryManagedFrame frame = frames.get(factoryKey);
728 
729             if (frame == null) {
730                 // it's the first time we need this frame, build it and store it
731                 final MODProvider provider = new MODProvider(conventions, getTimeScales());
732                 frame = new FactoryManagedFrame(parent, provider, true, factoryKey);
733                 frames.put(factoryKey, frame);
734             }
735 
736             return frame;
737 
738         }
739     }
740 
741     @Override
742     public FactoryManagedFrame getTEME() {
743         synchronized (this) {
744 
745             // try to find an already built frame
746             final Predefined factoryKey = Predefined.TEME;
747             FactoryManagedFrame frame = frames.get(factoryKey);
748 
749             if (frame == null) {
750                 // it's the first time we need this frame, build it and store it
751                 final Frame tod = getTOD(IERSConventions.IERS_1996, false, true);
752                 final ShiftingTransformProvider todShifting =
753                         (ShiftingTransformProvider) tod.getTransformProvider();
754                 final TEMEProvider temeRaw =
755                         new TEMEProvider(IERSConventions.IERS_1996, null, getTimeScales());
756                 final TransformProvider temeShifting =
757                         new ShiftingTransformProvider(temeRaw,
758                                 CartesianDerivativesFilter.USE_PVA,
759                                 AngularDerivativesFilter.USE_R,
760                                 todShifting.getGridPoints(), todShifting.getStep(),
761                                 OrekitConfiguration.getCacheSlotsNumber(),
762                                 Constants.JULIAN_YEAR, 30 * Constants.JULIAN_DAY);
763 
764                 frame = new FactoryManagedFrame(tod, temeShifting, true, factoryKey);
765                 frames.put(factoryKey, frame);
766             }
767 
768             return frame;
769 
770         }
771     }
772 
773     @Override
774     public FactoryManagedFrame getPZ9011(final IERSConventions convention,
775                                          final boolean simpleEOP) {
776         synchronized (this) {
777 
778             // try to find an already built frame
779             final Predefined factoryKey = Predefined.PZ90_11;
780             FactoryManagedFrame frame = frames.get(factoryKey);
781 
782             if (frame == null) {
783                 // it's the first time we need this frame, build it and store it
784                 final Frame itrf = getITRF(ITRFVersion.ITRF_2008, convention, simpleEOP);
785                 final HelmertTransformation pz90Raw = new HelmertTransformation(new AbsoluteDate(2010, 1, 1, 12, 0, 0, getTimeScales().getTT()),
786                         +3.0, +1.0, -0.0, +0.019, -0.042, +0.002, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
787                 frame = new FactoryManagedFrame(itrf, pz90Raw, false, factoryKey);
788                 frames.put(factoryKey, frame);
789             }
790 
791             return frame;
792 
793         }
794     }
795 
796     /**
797      * Get the time scales.
798      *
799      * @return time scales used to define these frames.
800      */
801     protected TimeScales getTimeScales() {
802         return timeScales;
803     }
804 
805     /** Local class for different ITRF versions keys.
806      * @since 9.2
807      */
808     private static class ITRFKey implements Serializable {
809 
810         /** Serialized UID. */
811         private static final long serialVersionUID = 20180412L;
812 
813         /** ITRF version. */
814         private final ITRFVersion version;
815 
816         /** IERS conventions to apply. */
817         private final IERSConventions conventions;
818 
819         /** Tidal effects flag. */
820         private final boolean simpleEOP;
821 
822         /** Simple constructor.
823          * @param version ITRF version
824          * @param conventions IERS conventions to apply
825          * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
826          */
827         ITRFKey(final ITRFVersion version, final IERSConventions conventions, final boolean simpleEOP) {
828             this.version     = version;
829             this.conventions = conventions;
830             this.simpleEOP   = simpleEOP;
831         }
832 
833         /** {@inheritDoc} */
834         @Override
835         public int hashCode() {
836             return (version.ordinal()     << 5) +
837                     (conventions.ordinal() << 1) +
838                     (simpleEOP ? 0 : 1);
839         }
840 
841         /** {@inheritDoc} */
842         @Override
843         public boolean equals(final Object other) {
844 
845             if (this == other) {
846                 return true;
847             }
848 
849             if (other instanceof ITRFKey) {
850                 final ITRFKey key = (ITRFKey) other;
851                 return version     == key.version     &&
852                        conventions == key.conventions &&
853                        simpleEOP   == key.simpleEOP;
854             }
855 
856             return false;
857         }
858 
859     }
860 }