1   /* Copyright 2022-2025 Thales Alenia Space
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.gnss;
18  
19  import org.orekit.annotation.DefaultDataContext;
20  import org.orekit.data.DataContext;
21  import org.orekit.errors.OrekitException;
22  import org.orekit.errors.OrekitMessages;
23  import org.orekit.frames.Frame;
24  import org.orekit.frames.Frames;
25  import org.orekit.frames.ITRFVersion;
26  import org.orekit.frames.Predefined;
27  import org.orekit.frames.VersionedITRF;
28  import org.orekit.utils.IERSConventions;
29  
30  import java.util.Locale;
31  import java.util.regex.Matcher;
32  import java.util.regex.Pattern;
33  
34  /** Utility for IGS files.
35   * @author Luc Maisonobe
36   * @since 12.1
37   *
38   */
39  public class IGSUtils {
40  
41      /** Pattern for frame names with year.
42       * @since 12.1
43       */
44      private static final Pattern EARTH_FRAME_WITH_YEAR = Pattern.compile("(?:IER|ITR|ITRF|IGS|IGb|SLR)([0-9]{2})");
45  
46      /** Pattern for GCRF inertial frame.
47       * @since 12.1
48       */
49      private static final Pattern GCRF_FRAME = Pattern.compile(" *GCRF *");
50  
51      /** Pattern for EME2000 inertial frame.
52       * @since 12.1
53       */
54      private static final Pattern EME2000_FRAME = Pattern.compile("EME(?:00|2K)");
55  
56      /** Private constructor for a utility class.
57       */
58      private IGSUtils() {
59          // nothing to do
60      }
61  
62      /** Default string to {@link Frame} conversion for {@link org.orekit.files.sp3.SP3Parser}
63       * or {@link org.orekit.files.rinex.clock.RinexClockParser}.
64       *
65       * <p>
66       * This method uses the {@link DataContext#getDefault() default data context}.
67       * </p>
68       * <p>
69       * Various frame names are supported:
70       * </p>
71       * <ul>
72       *     <li>IER##, ITR##, ITRF##, IGS##, IGb##, or SLR##, where ## is a two digits number,
73       *         the number will be used to build the appropriate {@link ITRFVersion}</li>
74       *     <li>GCRF (left or right justified) for GCRF inertial frame</li>
75       *     <li>EME00 or EME2K for EME2000 inertial frame</li>
76       *     <li>for all other names (for example if name is UNDEF or WGS84),
77       *     then a default {@link org.orekit.frames.Frames#getITRF(IERSConventions, boolean) ITRF}
78       *     frame will be selected</li>
79       * </ul>
80       * <p>
81       * Note that using inertial frames in classical products like SP3 files is non-standard,
82       * it is supported by Orekit, but may not be supported by other programs, so they should
83       * be used with caution when writing files.
84       * </p>
85       *
86       * @param name of the frame.
87       * @return ITRF based on 2010 conventions,
88       * with tidal effects considered during EOP interpolation
89       * @since 12.1
90       */
91      @DefaultDataContext
92      public static Frame guessFrame(final String name) {
93          return guessFrame(DataContext.getDefault().getFrames(), name);
94      }
95  
96      /** Default string to {@link Frame} conversion for {@link org.orekit.files.sp3.SP3Parser}
97       * or {@link org.orekit.files.rinex.clock.RinexClockParser}.
98       *
99       * <p>
100      * Various frame names are supported:
101      * </p>
102      * <ul>
103      *     <li>IER##, ITR##, ITRF##, IGS##, IGb##, or SLR##, where ## is a two digits number,
104      *         the number will be used to build the appropriate {@link ITRFVersion}</li>
105      *     <li>GCRF (left or right justified) for GCRF inertial frame</li>
106      *     <li>EME00 or EME2K for EME2000 inertial frame</li>
107      *     <li>for all other names (for example if name is UNDEF or WGS84),
108      *     then a default {@link org.orekit.frames.Frames#getITRF(IERSConventions, boolean) ITRF}
109      *     frame will be selected</li>
110      * </ul>
111      * <p>
112      * Note that using inertial frames in classical products like SP3 files is non-standard,
113      * it is supported by Orekit, but may not be supported by other programs, so they should
114      * be used with caution when writing files.
115      * </p>
116      *
117      * @param frames frames factory
118      * @param name of the frame.
119      * @return guessed frame
120      * @since 12.1
121      */
122     public static Frame guessFrame(final Frames frames, final String name) {
123         final Matcher earthMatcher = EARTH_FRAME_WITH_YEAR.matcher(name);
124         if (earthMatcher.matches()) {
125             // this is a frame of the form IGS14, or ITR20, or SLR08, or similar
126             final int yy = Integer.parseInt(earthMatcher.group(1));
127             final ITRFVersion itrfVersion = ITRFVersion.getITRFVersion(yy);
128             final IERSConventions conventions =
129                 itrfVersion.getYear() < 2003 ?
130                 IERSConventions.IERS_1996 :
131                 (itrfVersion.getYear() < 2010 ? IERSConventions.IERS_2003 : IERSConventions.IERS_2010);
132             return frames.getITRF(itrfVersion, conventions, false);
133         } else {
134             final Matcher gcrfMatcher = GCRF_FRAME.matcher(name);
135             if (gcrfMatcher.matches()) {
136                 // inertial GCRF frame
137                 return frames.getGCRF();
138             } else {
139                 final Matcher eme2000Matcher = EME2000_FRAME.matcher(name);
140                 if (eme2000Matcher.matches()) {
141                     // inertial EME2000 frame
142                     return frames.getEME2000();
143                 } else {
144                     // unknown frame 'maybe UNDEF or WGS84
145                     // we use a default ITRF
146                     return frames.getITRF(IERSConventions.IERS_2010, false);
147                 }
148             }
149         }
150     }
151 
152     /** Guess a frame name.
153      * <p>
154      * If the frame is not compatible with {@link #guessFrame(Frames, String)},
155      * an exception will be triggered
156      * </p>
157      * @param frame frame from which we want the name
158      * @return name compatible with {@link #guessFrame(Frames, String)}
159      * @since 12.1
160      */
161     public static String frameName(final Frame frame) {
162         if (frame instanceof VersionedITRF) {
163             final int yy = ((VersionedITRF) frame).getITRFVersion().getYear() % 100;
164             return String.format(Locale.US, "IGS%02d", yy);
165         } else if (Predefined.GCRF.getName().equals(frame.getName())) {
166             return "GCRF";
167         } else if (Predefined.EME2000.getName().equals(frame.getName())) {
168             return "EME2K";
169         } else {
170             throw new OrekitException(OrekitMessages.FRAME_NOT_ALLOWED, frame.getName());
171         }
172     }
173 
174 }