1   /* Copyright 2002-2021 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 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_1997, conventions, simpleEOP);
199         }
200 
201     },
202 
203     /** International Terrestrial Reference Frame 1996. */
204     ITRF1996 {
205 
206         /** {@inheritDoc} */
207         @Override
208         public Frame getFrame(final IERSConventions conventions,
209                               final boolean simpleEOP,
210                               final DataContext dataContext) {
211             if (conventions == null) {
212                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
213             }
214             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1996, conventions, simpleEOP);
215         }
216 
217     },
218 
219     /** International Terrestrial Reference Frame 1994. */
220     ITRF1994 {
221 
222         /** {@inheritDoc} */
223         @Override
224         public Frame getFrame(final IERSConventions conventions,
225                               final boolean simpleEOP,
226                               final DataContext dataContext) {
227             if (conventions == null) {
228                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
229             }
230             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1994, conventions, simpleEOP);
231         }
232 
233     },
234 
235     /** International Terrestrial Reference Frame 1993. */
236     ITRF1993 {
237 
238         /** {@inheritDoc} */
239         @Override
240         public Frame getFrame(final IERSConventions conventions,
241                               final boolean simpleEOP,
242                               final DataContext dataContext) {
243             if (conventions == null) {
244                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
245             }
246             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1993, conventions, simpleEOP);
247         }
248 
249     },
250 
251     /** International Terrestrial Reference Frame 1992. */
252     ITRF1992 {
253 
254         /** {@inheritDoc} */
255         @Override
256         public Frame getFrame(final IERSConventions conventions,
257                               final boolean simpleEOP,
258                               final DataContext dataContext) {
259             if (conventions == null) {
260                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
261             }
262             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1992, conventions, simpleEOP);
263         }
264 
265     },
266 
267     /** International Terrestrial Reference Frame 1991. */
268     ITRF1991 {
269 
270         /** {@inheritDoc} */
271         @Override
272         public Frame getFrame(final IERSConventions conventions,
273                               final boolean simpleEOP,
274                               final DataContext dataContext) {
275             if (conventions == null) {
276                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
277             }
278             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1991, conventions, simpleEOP);
279         }
280 
281     },
282 
283     /** International Terrestrial Reference Frame 1990. */
284     ITRF1990 {
285 
286         /** {@inheritDoc} */
287         @Override
288         public Frame getFrame(final IERSConventions conventions,
289                               final boolean simpleEOP,
290                               final DataContext dataContext) {
291             if (conventions == null) {
292                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
293             }
294             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1990, conventions, simpleEOP);
295         }
296 
297     },
298 
299     /** International Terrestrial Reference Frame 1989. */
300     ITRF1989 {
301 
302         /** {@inheritDoc} */
303         @Override
304         public Frame getFrame(final IERSConventions conventions,
305                               final boolean simpleEOP,
306                               final DataContext dataContext) {
307             if (conventions == null) {
308                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
309             }
310             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1989, conventions, simpleEOP);
311         }
312 
313     },
314 
315     /** International Terrestrial Reference Frame 1988. */
316     ITRF1988 {
317 
318         /** {@inheritDoc} */
319         @Override
320         public Frame getFrame(final IERSConventions conventions,
321                               final boolean simpleEOP,
322                               final DataContext dataContext) {
323             if (conventions == null) {
324                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
325             }
326             return dataContext.getFrames().getITRF(ITRFVersion.ITRF_1988, conventions, simpleEOP);
327         }
328 
329     },
330 
331     /** Mars Centered Inertial. */
332     MCI {
333 
334         /** {@inheritDoc} */
335         @Override
336         public Frame getFrame(final IERSConventions conventions,
337                               final boolean simpleEOP,
338                               final DataContext dataContext) {
339             return dataContext.getCelestialBodies().getMars().getInertiallyOrientedFrame();
340         }
341 
342     },
343 
344     /** True of Date, Rotating. */
345     TDR {
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().getGTOD(conventions, simpleEOP);
356         }
357 
358     },
359 
360     /**
361      * True Equator Mean Equinox.
362      * TEME may be used only for OMMs based on NORAD
363      * Two Line Element sets, and in no other circumstances.
364      */
365     TEME {
366 
367         /** {@inheritDoc} */
368         @Override
369         public Frame getFrame(final IERSConventions conventions,
370                               final boolean simpleEOP,
371                               final DataContext dataContext) {
372             return dataContext.getFrames().getTEME();
373         }
374 
375     },
376 
377     /** True of Date. */
378     TOD {
379 
380         /** {@inheritDoc} */
381         @Override
382         public Frame getFrame(final IERSConventions conventions,
383                               final boolean simpleEOP,
384                               final DataContext dataContext) {
385             if (conventions == null) {
386                 throw new OrekitException(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS);
387             }
388             return dataContext.getFrames().getTOD(conventions, simpleEOP);
389         }
390 
391     };
392 
393     /** Pattern for dash. */
394     private static final Pattern DASH = Pattern.compile("-");
395 
396     /** Suffix of the name of the inertial frame attached to a planet. */
397     private static final String INERTIAL_FRAME_SUFFIX = "/inertial";
398 
399     /** Substring common to all ITRF frames. */
400     private static final String ITRF_SUBSTRING = "ITRF";
401 
402     /**
403      * Get the frame corresponding to the CCSDS constant.
404      * @param conventions IERS conventions to use
405      * @param simpleEOP if true, tidal effects are ignored when interpolating EOP
406      * @param dataContext to use when creating the frame.
407      * @return frame corresponding to the CCSDS constant
408      * @since 10.1
409      */
410     public abstract Frame getFrame(IERSConventions conventions, boolean simpleEOP, DataContext dataContext);
411 
412     /** Parse a CCSDS frame.
413      * @param frameName name of the frame, as the value of a CCSDS key=value line
414      * @return CCSDS frame corresponding to the name
415      */
416     public static CelestialBodyFrame parse(final String frameName) {
417         String canonicalized = DASH.matcher(frameName).replaceAll("");
418         if (canonicalized.startsWith(ITRF_SUBSTRING) && canonicalized.length() > 4) {
419             final int year = Integer.parseInt(canonicalized.substring(4));
420             if (year > 87 && year < 100) {
421                 // convert two-digits specifications like ITRF97 into ITRF1997
422                 canonicalized = ITRF_SUBSTRING + (year + 1900);
423             }
424         }
425         return CelestialBodyFrame.valueOf(canonicalized);
426     }
427 
428     /**
429      * Map an Orekit frame to a CCSDS frame.
430      *
431      * <p> The goal of this method is to perform the opposite mapping of {@link
432      * #getFrame(IERSConventions, boolean, DataContext)}.
433      *
434      * @param frame a reference frame.
435      * @return the CCSDSFrame corresponding to the Orekit frame
436      */
437     public static CelestialBodyFrame map(final Frame frame) {
438         // Try to determine the CCSDS name from Annex A by examining the Orekit name.
439         final String name = frame.getName();
440         try {
441             // should handle J2000, GCRF, TEME, and some frames created by OEMParser.
442             return CelestialBodyFrame.valueOf(name);
443         } catch (IllegalArgumentException iae) {
444             if (frame instanceof ModifiedFrame) {
445                 return ((ModifiedFrame) frame).getRefFrame();
446             } else if ((CelestialBodyFactory.MARS + INERTIAL_FRAME_SUFFIX).equals(name)) {
447                 return MCI;
448             } else if ((CelestialBodyFactory.SOLAR_SYSTEM_BARYCENTER + INERTIAL_FRAME_SUFFIX).equals(name)) {
449                 return ICRF;
450             } else if (name.contains("GTOD")) {
451                 return GTOD;
452             } else if (name.contains("TOD")) { // check after GTOD
453                 return TOD;
454             } else if (name.contains("Equinox") && name.contains(ITRF_SUBSTRING)) {
455                 return GRC;
456             } else if (frame instanceof VersionedITRF) {
457                 try {
458                     final ITRFVersion itrfVersion = ((VersionedITRF) frame).getITRFVersion();
459                     return CelestialBodyFrame.valueOf(itrfVersion.name().replace("_", ""));
460                 } catch (IllegalArgumentException iae2) {
461                     // this should never happen
462                     throw new OrekitInternalError(iae2);
463                 }
464             } else if (name.contains("CIO") && name.contains(ITRF_SUBSTRING)) {
465                 return ITRF2014;
466             }
467             throw new OrekitException(iae, OrekitMessages.CCSDS_INVALID_FRAME, name);
468         }
469     }
470 
471     /**
472      * Guesses names from ODM Table 5-3 and Annex A.
473      *
474      * <p> The goal of this method is to perform the opposite mapping of {@link
475      * #getFrame(IERSConventions, boolean, DataContext)}.
476      *
477      * @param frame a reference frame.
478      * @return the string to use in the OEM file to identify {@code frame}.
479      */
480     public static String guessFrame(final Frame frame) {
481         try {
482             return map(frame).name();
483         } catch (OrekitException oe) {
484             // we were unable to find a match
485             return frame.getName();
486         }
487     }
488 
489 }