1 /* Copyright 2022-2025 Luc Maisonobe
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.sinex;
18
19 import org.hipparchus.util.FastMath;
20 import org.orekit.time.AbsoluteDate;
21 import org.orekit.time.DateComponents;
22 import org.orekit.time.TimeScale;
23 import org.orekit.time.TimeScales;
24 import org.orekit.utils.Constants;
25 import org.orekit.utils.units.Unit;
26
27 import java.util.regex.Pattern;
28
29 /** Transient data used for parsing a SINEX file.
30 * @param <T> type of the SINEX files
31 * @author Luc Maisonobe
32 * @since 13.0
33 */
34 public abstract class ParseInfo<T extends AbstractSinex> {
35
36 /** 00:000:00000 epoch. */
37 private static final String DEFAULT_EPOCH_TWO_DIGITS = "00:000:00000";
38
39 /** 0000:000:00000 epoch. */
40 private static final String DEFAULT_EPOCH_FOUR_DIGITS = "0000:000:00000";
41
42 /** Pattern for delimiting regular expressions. */
43 private static final Pattern SEPARATOR = Pattern.compile(":");
44
45 /** Time scales. */
46 private final TimeScales timeScales;
47
48 /** Name of the data source. */
49 private String name;
50
51 /** Current line. */
52 private String line;
53
54 /** Current line number of the navigation message. */
55 private int lineNumber;
56
57 /** SINEX file creation date as extracted for the first line. */
58 private AbsoluteDate creationDate;
59
60 /** SINEX file creation date as extracted for the first line. */
61 private String creationDateString;
62
63 /** Start time of the data used in the Sinex solution. */
64 private AbsoluteDate startDate;
65
66 /** Start time of the data used in the Sinex solution. */
67 private String startDateString;
68
69 /** End time of the data used in the Sinex solution. */
70 private AbsoluteDate endDate;
71
72 /** End time of the data used in the Sinex solution. */
73 private String endDateString;
74
75 /** Time scale. */
76 private TimeScale timeScale;
77
78 /** Simple constructor.
79 * @param timeScales time scales
80 */
81 protected ParseInfo(final TimeScales timeScales) {
82 this.timeScales = timeScales;
83 }
84
85 /** Start parsing of a new data source.
86 * @param newName name of the new data source
87 */
88 void newSource(final String newName) {
89 // initialize parsing
90 this.name = newName;
91 this.line = null;
92 this.lineNumber = 0;
93 }
94
95 /** Get name of data source.
96 * @return name of data source
97 */
98 protected String getName() {
99 return name;
100 }
101
102 /** Set current line.
103 * @param line current line
104 */
105 void setLine(final String line)
106 {
107 this.line = line;
108 }
109
110 /** Get current line.
111 * @return current line
112 */
113 String getLine() {
114 return line;
115 }
116
117 /** Increment line number.
118 */
119 void incrementLineNumber() {
120 ++lineNumber;
121 }
122
123 /** Get current line number.
124 * @return current line number
125 */
126 int getLineNumber() {
127 return lineNumber;
128 }
129
130 /** Set creation date.
131 * @param dateString creation date
132 */
133 protected void setCreationDate(final String dateString) {
134 this.creationDateString = dateString;
135 this.creationDate = stringEpochToAbsoluteDate(creationDateString, false);
136 }
137
138 /** Get creation date.
139 * @return creation date
140 */
141 protected AbsoluteDate getCreationDate() {
142 return creationDate;
143 }
144
145 /** Set start date if earlier than previous setting.
146 * @param candidateStartDateString candidate start date
147 */
148 protected void setStartDateIfEarlier(final String candidateStartDateString) {
149 final AbsoluteDate candidateStart = stringEpochToAbsoluteDate(candidateStartDateString, true);
150 if (startDate == null || candidateStart.isBefore(startDate)) {
151 // this is either the first setting
152 // or we are parsing a new data source referring to an earlier date than previous ones
153 this.startDateString = candidateStartDateString;
154 this.startDate = candidateStart;
155 }
156 }
157
158 /** Get start date.
159 * @return start date
160 */
161 protected AbsoluteDate getStartDate() {
162 return startDate;
163 }
164
165 /** Set end date if later than previous setting.
166 * @param candidateEndDateString end date
167 */
168 protected void setEndDateIfLater(final String candidateEndDateString) {
169 final AbsoluteDate candidateEnd = stringEpochToAbsoluteDate(candidateEndDateString, true);
170 if (endDate == null || candidateEnd.isAfter(endDate)) {
171 // this is either the first setting
172 // or we are parsing a new data source referring to a later date than previous ones
173 this.endDateString = candidateEndDateString;
174 this.endDate = candidateEnd;
175 }
176 }
177
178 /** Get end date.
179 * @return end date
180 */
181 protected AbsoluteDate getEndDate() {
182 return endDate;
183 }
184
185 /** Set time scale.
186 * @param timeScale time scale
187 */
188 protected void setTimeScale(final TimeScale timeScale) {
189
190 this.timeScale = timeScale;
191
192 // A time scale has been parsed, update start, end, and creation dates
193 // to take into account the time scale
194 if (startDateString != null) {
195 startDate = stringEpochToAbsoluteDate(startDateString, true);
196 }
197 if (endDateString != null) {
198 endDate = stringEpochToAbsoluteDate(endDateString, false);
199 }
200 if (creationDateString != null) {
201 creationDate = stringEpochToAbsoluteDate(creationDateString, false);
202 }
203
204 }
205
206 /** Get time scales.
207 * @return time scales
208 */
209 TimeScales getTimeScales() {
210 return timeScales;
211 }
212
213 /** Build the parsed file.
214 * @return built parsed file
215 */
216 protected abstract T build();
217
218 /** Extract a string from current line.
219 * @param start start index of the string
220 * @param length length of the string
221 * @return parsed string
222 */
223 protected String parseString(final int start, final int length) {
224 return line.substring(start, FastMath.min(line.length(), start + length)).trim();
225 }
226
227 /** Extract a double from current line.
228 * @param start start index of the real
229 * @param length length of the real
230 * @return parsed real
231 */
232 protected double parseDouble(final int start, final int length) {
233 return Double.parseDouble(parseString(start, length));
234 }
235
236 /** Extract an integer from current line.
237 * @param start start index of the real
238 * @param length length of the real
239 * @return parsed integer
240 */
241 protected int parseInt(final int start, final int length) {
242 return Integer.parseInt(parseString(start, length));
243 }
244
245 /** Extract a double from current line and convert in SI unit.
246 * @param startUnit start index of the unit
247 * @param lengthUnit length of the unit
248 * @param startDouble start index of the real
249 * @param lengthDouble length of the real
250 * @return parsed double in SI unit
251 */
252 protected double parseDoubleWithUnit(final int startUnit, final int lengthUnit,
253 final int startDouble, final int lengthDouble) {
254 final Unit unit = Unit.parse(parseString(startUnit, lengthUnit));
255 return unit.toSI(parseDouble(startDouble, lengthDouble));
256 }
257
258 /** Transform a String epoch to an AbsoluteDate.
259 * @param stringDate string epoch
260 * @param isStart true if epoch is a start validity epoch
261 * @return the corresponding AbsoluteDate
262 */
263 protected AbsoluteDate stringEpochToAbsoluteDate(final String stringDate, final boolean isStart) {
264
265 // Deal with 00:000:00000 epochs
266 if (DEFAULT_EPOCH_TWO_DIGITS.equals(stringDate) || DEFAULT_EPOCH_FOUR_DIGITS.equals(stringDate)) {
267 // If it's a start validity epoch, the file start date shall be used.
268 // For end validity epoch, future infinity is acceptable.
269 return isStart ? startDate : AbsoluteDate.FUTURE_INFINITY;
270 }
271
272 // Date components
273 final String[] fields = SEPARATOR.split(stringDate);
274
275 // Read fields
276 final int digitsYear = Integer.parseInt(fields[0]);
277 final int day = Integer.parseInt(fields[1]);
278 final int secInDay = Integer.parseInt(fields[2]);
279
280 // Data year
281 final int year;
282 if (digitsYear > 50 && digitsYear < 100) {
283 year = 1900 + digitsYear;
284 } else if (digitsYear < 100) {
285 year = 2000 + digitsYear;
286 } else {
287 year = digitsYear;
288 }
289
290 // Return an absolute date.
291 // Initialize to 1st January of the given year because
292 // sometimes day is equal to 0 in the file.
293 return new AbsoluteDate(new DateComponents(year, 1, 1), timeScale).
294 shiftedBy(Constants.JULIAN_DAY * (day - 1)).
295 shiftedBy(secInDay);
296
297 }
298
299 }