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