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 2014. */
124     ITRF2014 {
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_2014, conventions, simpleEOP);
135         }
136 
137     },
138 
139     /** International Terrestrial Reference Frame 2008. */
140     ITRF2008 {
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_2008, conventions, simpleEOP);
151         }
152 
153     },
154 
155     /** International Terrestrial Reference Frame 2005. */
156     ITRF2005 {
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_2005, conventions, simpleEOP);
167         }
168 
169     },
170 
171     /** International Terrestrial Reference Frame 2000. */
172     ITRF2000 {
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_2000, conventions, simpleEOP);
183         }
184 
185     },
186 
187     /** International Terrestrial Reference Frame 1997. */
188     ITRF1997 {
189 
190         /** {@inheritDoc} */
191         @Override
192         public String getName() {
193             return "ITRF-97";
194         }
195 
196         /** {@inheritDoc} */
197         @Override
198         public Frame getFrame(final IERSConventions conventions,
199                               final boolean simpleEOP,
200                               final DataContext dataContext) {
201             if (conventions == null) {
202                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
203             }
204             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1997, conventions, simpleEOP);
205         }
206 
207     },
208 
209     /** International Terrestrial Reference Frame 1996. */
210     ITRF1996 {
211 
212         /** {@inheritDoc} */
213         @Override
214         public String getName() {
215             return "ITRF-96";
216         }
217 
218         /** {@inheritDoc} */
219         @Override
220         public Frame getFrame(final IERSConventions conventions,
221                               final boolean simpleEOP,
222                               final DataContext dataContext) {
223             if (conventions == null) {
224                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
225             }
226             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1996, conventions, simpleEOP);
227         }
228 
229     },
230 
231     /** International Terrestrial Reference Frame 1994. */
232     ITRF1994 {
233 
234         /** {@inheritDoc} */
235         @Override
236         public String getName() {
237             return "ITRF-94";
238         }
239 
240         /** {@inheritDoc} */
241         @Override
242         public Frame getFrame(final IERSConventions conventions,
243                               final boolean simpleEOP,
244                               final DataContext dataContext) {
245             if (conventions == null) {
246                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
247             }
248             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1994, conventions, simpleEOP);
249         }
250 
251     },
252 
253     /** International Terrestrial Reference Frame 1993. */
254     ITRF1993 {
255 
256         /** {@inheritDoc} */
257         @Override
258         public String getName() {
259             return "ITRF-93";
260         }
261 
262         /** {@inheritDoc} */
263         @Override
264         public Frame getFrame(final IERSConventions conventions,
265                               final boolean simpleEOP,
266                               final DataContext dataContext) {
267             if (conventions == null) {
268                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
269             }
270             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1993, conventions, simpleEOP);
271         }
272 
273     },
274 
275     /** International Terrestrial Reference Frame 1992. */
276     ITRF1992 {
277 
278         /** {@inheritDoc} */
279         @Override
280         public String getName() {
281             return "ITRF-92";
282         }
283 
284         /** {@inheritDoc} */
285         @Override
286         public Frame getFrame(final IERSConventions conventions,
287                               final boolean simpleEOP,
288                               final DataContext dataContext) {
289             if (conventions == null) {
290                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
291             }
292             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1992, conventions, simpleEOP);
293         }
294 
295     },
296 
297     /** International Terrestrial Reference Frame 1991. */
298     ITRF1991 {
299 
300         /** {@inheritDoc} */
301         @Override
302         public String getName() {
303             return "ITRF-91";
304         }
305 
306         /** {@inheritDoc} */
307         @Override
308         public Frame getFrame(final IERSConventions conventions,
309                               final boolean simpleEOP,
310                               final DataContext dataContext) {
311             if (conventions == null) {
312                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
313             }
314             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1991, conventions, simpleEOP);
315         }
316 
317     },
318 
319     /** International Terrestrial Reference Frame 1990. */
320     ITRF1990 {
321 
322         /** {@inheritDoc} */
323         @Override
324         public String getName() {
325             return "ITRF-90";
326         }
327 
328         /** {@inheritDoc} */
329         @Override
330         public Frame getFrame(final IERSConventions conventions,
331                               final boolean simpleEOP,
332                               final DataContext dataContext) {
333             if (conventions == null) {
334                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
335             }
336             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1990, conventions, simpleEOP);
337         }
338 
339     },
340 
341     /** International Terrestrial Reference Frame 1989. */
342     ITRF1989 {
343 
344         /** {@inheritDoc} */
345         @Override
346         public String getName() {
347             return "ITRF-89";
348         }
349 
350         /** {@inheritDoc} */
351         @Override
352         public Frame getFrame(final IERSConventions conventions,
353                               final boolean simpleEOP,
354                               final DataContext dataContext) {
355             if (conventions == null) {
356                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
357             }
358             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1989, conventions, simpleEOP);
359         }
360 
361     },
362 
363     /** International Terrestrial Reference Frame 1988. */
364     ITRF1988 {
365 
366         /** {@inheritDoc} */
367         @Override
368         public String getName() {
369             return "ITRF-88";
370         }
371 
372         /** {@inheritDoc} */
373         @Override
374         public Frame getFrame(final IERSConventions conventions,
375                               final boolean simpleEOP,
376                               final DataContext dataContext) {
377             if (conventions == null) {
378                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
379             }
380             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1988, conventions, simpleEOP);
381         }
382 
383     },
384 
385     /** Mars Centered Inertial. */
386     MCI {
387 
388         /** {@inheritDoc} */
389         @Override
390         public Frame getFrame(final IERSConventions conventions,
391                               final boolean simpleEOP,
392                               final DataContext dataContext) {
393             return dataContext.getCelestialBodies().getMars().getInertiallyOrientedFrame();
394         }
395 
396     },
397 
398     /** True of Date, Rotating. */
399     TDR {
400 
401         /** {@inheritDoc} */
402         @Override
403         public Frame getFrame(final IERSConventions conventions,
404                               final boolean simpleEOP,
405                               final DataContext dataContext) {
406             if (conventions == null) {
407                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
408             }
409             return dataContext.getFrames().getGTOD(conventions, simpleEOP);
410         }
411 
412     },
413 
414     /**
415      * True Equator Mean Equinox.
416      * TEME may be used only for OMMs based on NORAD
417      * Two Line Element sets, and in no other circumstances.
418      */
419     TEME {
420 
421         /** {@inheritDoc} */
422         @Override
423         public Frame getFrame(final IERSConventions conventions,
424                               final boolean simpleEOP,
425                               final DataContext dataContext) {
426             return dataContext.getFrames().getTEME();
427         }
428 
429     },
430 
431     /** True of Date. */
432     TOD {
433 
434         /** {@inheritDoc} */
435         @Override
436         public Frame getFrame(final IERSConventions conventions,
437                               final boolean simpleEOP,
438                               final DataContext dataContext) {
439             if (conventions == null) {
440                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
441             }
442             return dataContext.getFrames().getTOD(conventions, simpleEOP);
443         }
444 
445     };
446 
447     /** Pattern for dash. */
448     private static final Pattern DASH = Pattern.compile("-");
449 
450     /** Suffix of the name of the inertial frame attached to a planet. */
451     private static final String INERTIAL_FRAME_SUFFIX = "/inertial";
452 
453     /** Substring common to all ITRF frames. */
454     private static final String ITRF_SUBSTRING = "ITRF";
455 
456     /**
457      * Get the frame corresponding to the CCSDS constant.
458      * @param conventions IERS conventions to use
459      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
460      * @param dataContext to use when creating the frame.
461      * @return frame corresponding to the CCSDS constant
462      * @since 10.1
463      */
464     public abstract Frame getFrame(IERSConventions conventions, boolean simpleEOP, DataContext dataContext);
465 
466     /**
467      * Get the name of celestial body frame.
468      * @return the name of celestial body frame
469      * @since 11.1
470      */
471     public String getName() {
472         return name();
473     }
474 
475     /** Parse a CCSDS frame.
476      * @param frameName name of the frame, as the value of a CCSDS key=value line
477      * @return CCSDS frame corresponding to the name
478      */
479     public static CelestialBodyFrame parse(final String frameName) {
480         String canonicalized = DASH.matcher(frameName).replaceAll("");
481         if (canonicalized.startsWith(ITRF_SUBSTRING) && canonicalized.length() > 4) {
482             final int year = Integer.parseInt(canonicalized.substring(4));
483             if (year > 87 && year < 100) {
484                 // convert two-digits specifications like ITRF97 into ITRF1997
485                 canonicalized = ITRF_SUBSTRING + (year + 1900);
486             }
487         }
488         return CelestialBodyFrame.valueOf(canonicalized);
489     }
490 
491     /**
492      * Map an Orekit frame to a CCSDS frame.
493      *
494      * <p> The goal of this method is to perform the opposite mapping of {@link
495      * #getFrame(IERSConventions, boolean, DataContext)}.
496      *
497      * @param frame a reference frame.
498      * @return the CCSDSFrame corresponding to the Orekit frame
499      */
500     public static CelestialBodyFrame map(final Frame frame) {
501         // Try to determine the CCSDS name from Annex A by examining the Orekit name.
502         final String name = frame.getName();
503         try {
504             // should handle J2000, GCRF, TEME, and some frames created by OEMParser.
505             return CelestialBodyFrame.valueOf(name);
506         } catch (IllegalArgumentException iae) {
507             if (frame instanceof ModifiedFrame) {
508                 return ((ModifiedFrame) frame).getRefFrame();
509             } else if ((CelestialBodyFactory.MARS + INERTIAL_FRAME_SUFFIX).equals(name)) {
510                 return MCI;
511             } else if ((CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER + INERTIAL_FRAME_SUFFIX).equals(name)) {
512                 return ICRF;
513             } else if (name.contains("GTOD")) {
514                 return GTOD;
515             } else if (name.contains("TOD")) { // check after GTOD
516                 return TOD;
517             } else if (name.contains("Equinox") && name.contains(ITRF_SUBSTRING)) {
518                 return GRC;
519             } else if (frame instanceof VersionedITRF) {
520                 try {
521                     final ITRFVersion itrfVersion = ((VersionedITRF) frame).getITRFVersion();
522                     return CelestialBodyFrame.valueOf(itrfVersion.name().replace("_", ""));
523                 } catch (IllegalArgumentException iae2) {
524                     // this should never happen
525                     throw new OrekitInternalError(iae2);
526                 }
527             } else if (name.contains("CIO") && name.contains(ITRF_SUBSTRING)) {
528                 return ITRF2014;
529             }
530             throw new OrekitException(iae, OrekitMessages.CCSDS_INVALID_FRAME, name);
531         }
532     }
533 
534     /**
535      * Guesses names from ODM Table 5-3 and Annex A.
536      *
537      * <p> The goal of this method is to perform the opposite mapping of {@link
538      * #getFrame(IERSConventions, boolean, DataContext)}.
539      *
540      * @param frame a reference frame.
541      * @return the string to use in the OEM file to identify {@code frame}.
542      */
543     public static String guessFrame(final Frame frame) {
544         try {
545             return map(frame).name();
546         } catch (OrekitException oe) {
547             // we were unable to find a match
548             return frame.getName();
549         }
550     }
551 
552 }