1   /* Copyright 2002-2022 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.files.ccsds.definitions;
18  
19  import java.util.regex.Pattern;
20  
21  import org.orekit.bodies.CelestialBodyFactory;
22  import org.orekit.data.DataContext;
23  import org.orekit.errors.OrekitException;
24  import org.orekit.errors.OrekitInternalError;
25  import org.orekit.errors.OrekitMessages;
26  import org.orekit.frames.Frame;
27  import org.orekit.frames.ITRFVersion;
28  import org.orekit.frames.VersionedITRF;
29  import org.orekit.utils.IERSConventions;
30  
31  /** Frames used in CCSDS Orbit Data Messages.
32   * @author Steven Ports
33   * @since 6.1
34   */
35  public enum CelestialBodyFrame {
36  
37      /** Earth Mean Equator and Equinox of J2000. */
38      EME2000 {
39  
40          /** {@inheritDoc} */
41          @Override
42          public Frame getFrame(final IERSConventions conventions,
43                                final boolean simpleEOP,
44                                final DataContext dataContext) {
45              return dataContext.getFrames().getEME2000();
46          }
47  
48      },
49  
50      /** Earth Mean Equator and Equinox of J2000. */
51      J2000 {
52  
53          /** {@inheritDoc} */
54          @Override
55          public Frame getFrame(final IERSConventions conventions,
56                                final boolean simpleEOP,
57                                final DataContext dataContext) {
58              return dataContext.getFrames().getEME2000();
59          }
60  
61      },
62  
63      /** Geocentric Celestial Reference Frame. */
64      GCRF {
65  
66          /** {@inheritDoc} */
67          @Override
68          public Frame getFrame(final IERSConventions conventions,
69                                final boolean simpleEOP,
70                                final DataContext dataContext) {
71              return dataContext.getFrames().getGCRF();
72          }
73  
74      },
75  
76      /** Greenwich Rotating Coordinates. */
77      GRC {
78  
79          /** {@inheritDoc} */
80          @Override
81          public Frame getFrame(final IERSConventions conventions,
82                                final boolean simpleEOP,
83                                final DataContext dataContext) {
84              if (conventions == null) {
85                  throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
86              }
87              return dataContext.getFrames().getITRFEquinox(conventions, simpleEOP);
88          }
89  
90      },
91  
92      /** Greenwich True Of Date.
93       * @since 11.0
94       */
95      GTOD {
96  
97          /** {@inheritDoc} */
98          @Override
99          public Frame getFrame(final IERSConventions conventions,
100                               final boolean simpleEOP,
101                               final DataContext dataContext) {
102             if (conventions == null) {
103                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
104             }
105             return dataContext.getFrames().getGTOD(conventions, simpleEOP);
106         }
107 
108     },
109 
110     /** International Celestial Reference Frame. */
111     ICRF {
112 
113         /** {@inheritDoc} */
114         @Override
115         public Frame getFrame(final IERSConventions conventions,
116                               final boolean simpleEOP,
117                               final DataContext dataContext) {
118             return dataContext.getFrames().getICRF();
119         }
120 
121     },
122 
123     /** International Terrestrial Reference Frame 2020. */
124     ITRF2020 {
125 
126         /** {@inheritDoc} */
127         @Override
128         public Frame getFrame(final IERSConventions conventions,
129                               final boolean simpleEOP,
130                               final DataContext dataContext) {
131             if (conventions == null) {
132                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
133             }
134             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_2020, conventions, simpleEOP);
135         }
136 
137     },
138 
139     /** International Terrestrial Reference Frame 2014. */
140     ITRF2014 {
141 
142         /** {@inheritDoc} */
143         @Override
144         public Frame getFrame(final IERSConventions conventions,
145                               final boolean simpleEOP,
146                               final DataContext dataContext) {
147             if (conventions == null) {
148                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
149             }
150             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_2014, conventions, simpleEOP);
151         }
152 
153     },
154 
155     /** International Terrestrial Reference Frame 2008. */
156     ITRF2008 {
157 
158         /** {@inheritDoc} */
159         @Override
160         public Frame getFrame(final IERSConventions conventions,
161                               final boolean simpleEOP,
162                               final DataContext dataContext) {
163             if (conventions == null) {
164                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
165             }
166             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_2008, conventions, simpleEOP);
167         }
168 
169     },
170 
171     /** International Terrestrial Reference Frame 2005. */
172     ITRF2005 {
173 
174         /** {@inheritDoc} */
175         @Override
176         public Frame getFrame(final IERSConventions conventions,
177                               final boolean simpleEOP,
178                               final DataContext dataContext) {
179             if (conventions == null) {
180                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
181             }
182             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_2005, conventions, simpleEOP);
183         }
184 
185     },
186 
187     /** International Terrestrial Reference Frame 2000. */
188     ITRF2000 {
189 
190         /** {@inheritDoc} */
191         @Override
192         public Frame getFrame(final IERSConventions conventions,
193                               final boolean simpleEOP,
194                               final DataContext dataContext) {
195             if (conventions == null) {
196                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
197             }
198             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_2000, conventions, simpleEOP);
199         }
200 
201     },
202 
203     /** International Terrestrial Reference Frame 1997. */
204     ITRF1997 {
205 
206         /** {@inheritDoc} */
207         @Override
208         public String getName() {
209             return "ITRF-97";
210         }
211 
212         /** {@inheritDoc} */
213         @Override
214         public Frame getFrame(final IERSConventions conventions,
215                               final boolean simpleEOP,
216                               final DataContext dataContext) {
217             if (conventions == null) {
218                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
219             }
220             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1997, conventions, simpleEOP);
221         }
222 
223     },
224 
225     /** International Terrestrial Reference Frame 1996. */
226     ITRF1996 {
227 
228         /** {@inheritDoc} */
229         @Override
230         public String getName() {
231             return "ITRF-96";
232         }
233 
234         /** {@inheritDoc} */
235         @Override
236         public Frame getFrame(final IERSConventions conventions,
237                               final boolean simpleEOP,
238                               final DataContext dataContext) {
239             if (conventions == null) {
240                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
241             }
242             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1996, conventions, simpleEOP);
243         }
244 
245     },
246 
247     /** International Terrestrial Reference Frame 1994. */
248     ITRF1994 {
249 
250         /** {@inheritDoc} */
251         @Override
252         public String getName() {
253             return "ITRF-94";
254         }
255 
256         /** {@inheritDoc} */
257         @Override
258         public Frame getFrame(final IERSConventions conventions,
259                               final boolean simpleEOP,
260                               final DataContext dataContext) {
261             if (conventions == null) {
262                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
263             }
264             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1994, conventions, simpleEOP);
265         }
266 
267     },
268 
269     /** International Terrestrial Reference Frame 1993. */
270     ITRF1993 {
271 
272         /** {@inheritDoc} */
273         @Override
274         public String getName() {
275             return "ITRF-93";
276         }
277 
278         /** {@inheritDoc} */
279         @Override
280         public Frame getFrame(final IERSConventions conventions,
281                               final boolean simpleEOP,
282                               final DataContext dataContext) {
283             if (conventions == null) {
284                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
285             }
286             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1993, conventions, simpleEOP);
287         }
288 
289     },
290 
291     /** International Terrestrial Reference Frame 1992. */
292     ITRF1992 {
293 
294         /** {@inheritDoc} */
295         @Override
296         public String getName() {
297             return "ITRF-92";
298         }
299 
300         /** {@inheritDoc} */
301         @Override
302         public Frame getFrame(final IERSConventions conventions,
303                               final boolean simpleEOP,
304                               final DataContext dataContext) {
305             if (conventions == null) {
306                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
307             }
308             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1992, conventions, simpleEOP);
309         }
310 
311     },
312 
313     /** International Terrestrial Reference Frame 1991. */
314     ITRF1991 {
315 
316         /** {@inheritDoc} */
317         @Override
318         public String getName() {
319             return "ITRF-91";
320         }
321 
322         /** {@inheritDoc} */
323         @Override
324         public Frame getFrame(final IERSConventions conventions,
325                               final boolean simpleEOP,
326                               final DataContext dataContext) {
327             if (conventions == null) {
328                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
329             }
330             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1991, conventions, simpleEOP);
331         }
332 
333     },
334 
335     /** International Terrestrial Reference Frame 1990. */
336     ITRF1990 {
337 
338         /** {@inheritDoc} */
339         @Override
340         public String getName() {
341             return "ITRF-90";
342         }
343 
344         /** {@inheritDoc} */
345         @Override
346         public Frame getFrame(final IERSConventions conventions,
347                               final boolean simpleEOP,
348                               final DataContext dataContext) {
349             if (conventions == null) {
350                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
351             }
352             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1990, conventions, simpleEOP);
353         }
354 
355     },
356 
357     /** International Terrestrial Reference Frame 1989. */
358     ITRF1989 {
359 
360         /** {@inheritDoc} */
361         @Override
362         public String getName() {
363             return "ITRF-89";
364         }
365 
366         /** {@inheritDoc} */
367         @Override
368         public Frame getFrame(final IERSConventions conventions,
369                               final boolean simpleEOP,
370                               final DataContext dataContext) {
371             if (conventions == null) {
372                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
373             }
374             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1989, conventions, simpleEOP);
375         }
376 
377     },
378 
379     /** International Terrestrial Reference Frame 1988. */
380     ITRF1988 {
381 
382         /** {@inheritDoc} */
383         @Override
384         public String getName() {
385             return "ITRF-88";
386         }
387 
388         /** {@inheritDoc} */
389         @Override
390         public Frame getFrame(final IERSConventions conventions,
391                               final boolean simpleEOP,
392                               final DataContext dataContext) {
393             if (conventions == null) {
394                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
395             }
396             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1988, conventions, simpleEOP);
397         }
398 
399     },
400 
401     /** Mars Centered Inertial. */
402     MCI {
403 
404         /** {@inheritDoc} */
405         @Override
406         public Frame getFrame(final IERSConventions conventions,
407                               final boolean simpleEOP,
408                               final DataContext dataContext) {
409             return dataContext.getCelestialBodies().getMars().getInertiallyOrientedFrame();
410         }
411 
412     },
413 
414     /** True of Date, Rotating. */
415     TDR {
416 
417         /** {@inheritDoc} */
418         @Override
419         public Frame getFrame(final IERSConventions conventions,
420                               final boolean simpleEOP,
421                               final DataContext dataContext) {
422             if (conventions == null) {
423                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
424             }
425             return dataContext.getFrames().getGTOD(conventions, simpleEOP);
426         }
427 
428     },
429 
430     /**
431      * True Equator Mean Equinox.
432      * TEME may be used only for OMMs based on NORAD
433      * Two Line Element sets, and in no other circumstances.
434      */
435     TEME {
436 
437         /** {@inheritDoc} */
438         @Override
439         public Frame getFrame(final IERSConventions conventions,
440                               final boolean simpleEOP,
441                               final DataContext dataContext) {
442             return dataContext.getFrames().getTEME();
443         }
444 
445     },
446 
447     /** True of Date. */
448     TOD {
449 
450         /** {@inheritDoc} */
451         @Override
452         public Frame getFrame(final IERSConventions conventions,
453                               final boolean simpleEOP,
454                               final DataContext dataContext) {
455             if (conventions == null) {
456                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
457             }
458             return dataContext.getFrames().getTOD(conventions, simpleEOP);
459         }
460 
461     };
462 
463     /** Pattern for dash. */
464     private static final Pattern DASH = Pattern.compile("-");
465 
466     /** Suffix of the name of the inertial frame attached to a planet. */
467     private static final String INERTIAL_FRAME_SUFFIX = "/inertial";
468 
469     /** Substring common to all ITRF frames. */
470     private static final String ITRF_SUBSTRING = "ITRF";
471 
472     /**
473      * Get the frame corresponding to the CCSDS constant.
474      * @param conventions IERS conventions to use
475      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
476      * @param dataContext to use when creating the frame.
477      * @return frame corresponding to the CCSDS constant
478      * @since 10.1
479      */
480     public abstract Frame getFrame(IERSConventions conventions, boolean simpleEOP, DataContext dataContext);
481 
482     /**
483      * Get the name of celestial body frame.
484      * @return the name of celestial body frame
485      * @since 11.1
486      */
487     public String getName() {
488         return name();
489     }
490 
491     /** Parse a CCSDS frame.
492      * @param frameName name of the frame, as the value of a CCSDS key=value line
493      * @return CCSDS frame corresponding to the name
494      */
495     public static CelestialBodyFrame parse(final String frameName) {
496         String canonicalized = DASH.matcher(frameName).replaceAll("");
497         if (canonicalized.startsWith(ITRF_SUBSTRING) && canonicalized.length() > 4) {
498             final int year = Integer.parseInt(canonicalized.substring(4));
499             if (year > 87 && year < 100) {
500                 // convert two-digits specifications like ITRF97 into ITRF1997
501                 canonicalized = ITRF_SUBSTRING + (year + 1900);
502             }
503         }
504         return CelestialBodyFrame.valueOf(canonicalized);
505     }
506 
507     /**
508      * Map an Orekit frame to a CCSDS frame.
509      *
510      * <p> The goal of this method is to perform the opposite mapping of {@link
511      * #getFrame(IERSConventions, boolean, DataContext)}.
512      *
513      * @param frame a reference frame.
514      * @return the CCSDSFrame corresponding to the Orekit frame
515      */
516     public static CelestialBodyFrame map(final Frame frame) {
517         // Try to determine the CCSDS name from Annex A by examining the Orekit name.
518         final String name = frame.getName();
519         try {
520             // should handle J2000, GCRF, TEME, and some frames created by OEMParser.
521             return CelestialBodyFrame.valueOf(name);
522         } catch (IllegalArgumentException iae) {
523             if (frame instanceof ModifiedFrame) {
524                 return ((ModifiedFrame) frame).getRefFrame();
525             } else if ((CelestialBodyFactory.MARS + INERTIAL_FRAME_SUFFIX).equals(name)) {
526                 return MCI;
527             } else if ((CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER + INERTIAL_FRAME_SUFFIX).equals(name)) {
528                 return ICRF;
529             } else if (name.contains("GTOD")) {
530                 return GTOD;
531             } else if (name.contains("TOD")) { // check after GTOD
532                 return TOD;
533             } else if (name.contains("Equinox") && name.contains(ITRF_SUBSTRING)) {
534                 return GRC;
535             } else if (frame instanceof VersionedITRF) {
536                 try {
537                     final ITRFVersion itrfVersion = ((VersionedITRF) frame).getITRFVersion();
538                     return CelestialBodyFrame.valueOf(itrfVersion.name().replace("_", ""));
539                 } catch (IllegalArgumentException iae2) {
540                     // this should never happen
541                     throw new OrekitInternalError(iae2);
542                 }
543             } else if (name.contains("CIO") && name.contains(ITRF_SUBSTRING)) {
544                 return ITRF2014;
545             }
546             throw new OrekitException(iae, OrekitMessages.CCSDS_INVALID_FRAME, name);
547         }
548     }
549 
550     /**
551      * Guesses names from ODM Table 5-3 and Annex A.
552      *
553      * <p> The goal of this method is to perform the opposite mapping of {@link
554      * #getFrame(IERSConventions, boolean, DataContext)}.
555      *
556      * @param frame a reference frame.
557      * @return the string to use in the OEM file to identify {@code frame}.
558      */
559     public static String guessFrame(final Frame frame) {
560         try {
561             return map(frame).name();
562         } catch (OrekitException oe) {
563             // we were unable to find a match
564             return frame.getName();
565         }
566     }
567 
568 }