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