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.sp3;
18
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22
23 import org.orekit.errors.OrekitException;
24 import org.orekit.errors.OrekitMessages;
25 import org.orekit.gnss.TimeSystem;
26 import org.orekit.time.AbsoluteDate;
27 import org.orekit.utils.CartesianDerivativesFilter;
28
29 /** Header for SP3 files.
30 * @author Luc Maisonobe
31 * @since 12.0
32 */
33 public class SP3Header {
34
35 /** String representation of the center of ephemeris coordinate system. **/
36 public static final String SP3_FRAME_CENTER_STRING = "EARTH";
37
38 /** Name for pos/vel accuracy base header entry. */
39 private static final String POS_VEL_ACCURACY_BASE = "pos/vel accuracy base";
40
41 /** Name for clock accuracy base header entry. */
42 private static final String CLOCK_ACCURACY_BASE = "clock accuracy base";
43
44 /** Name for comments header entry. */
45 private static final String COMMENTS = "comments";
46
47 /** File version. */
48 private char version;
49
50 /** File type. */
51 private SP3FileType type;
52
53 /** Time system. */
54 private TimeSystem timeSystem;
55
56 /** Epoch of the file. */
57 private AbsoluteDate epoch;
58
59 /** GPS week. */
60 private int gpsWeek;
61
62 /** Seconds of the current GPS week. */
63 private double secondsOfWeek;
64
65 /** Julian day. */
66 private int modifiedJulianDay;
67
68 /** Day fraction. */
69 private double dayFraction;
70
71 /** Time-interval between epochs. */
72 private double epochInterval;
73
74 /** Number of epochs. */
75 private int numberOfEpochs;
76
77 /** Coordinate system. */
78 private String coordinateSystem;
79
80 /** Data used indicator. */
81 private List<DataUsed> dataUsed;
82
83 /** Orbit type. */
84 private SP3OrbitType orbitType;
85
86 /** Key for orbit type. */
87 private String orbitTypeKey;
88
89 /** Agency providing the file. */
90 private String agency;
91
92 /** Indicates if data contains velocity or not. */
93 private CartesianDerivativesFilter filter;
94
95 /** Base for position/velocity accuracy. */
96 private double posVelBase;
97
98 /** Base for clock/clock-rate accuracy. */
99 private double clockBase;
100
101 /** Satellite identifiers. */
102 private List<String> satIds;
103
104 /** Satellite accuracies. */
105 private double[] accuracies;
106
107 /** Comments. */
108 private final List<String> comments;
109
110 /** Create a new SP3 header.
111 */
112 public SP3Header() {
113 this.version = '?';
114 this.satIds = new ArrayList<>();
115 this.accuracies = null;
116 this.comments = new ArrayList<>();
117 }
118
119 /** Check header is valid.
120 * @param parsing if true, we are parsing an existing file, and are more lenient
121 * in order to accept some common errors (like between 86 and 99 satellites
122 * in SP3a, SP3b or SP3c files)
123 * @param hasAccuracy if true, there are accuracy data in the file
124 * @param fileName file name to generate the error message
125 * @exception OrekitException if file is not valid
126 */
127 void validate(final boolean parsing, final boolean hasAccuracy, final String fileName) throws OrekitException {
128
129 // check version
130 if ("abcd".indexOf(getVersion()) < 0) {
131 throw new OrekitException(OrekitMessages.SP3_UNSUPPORTED_VERSION, getVersion());
132 }
133
134 if (getVersion() == 'a') {
135 // in SP3 version a, the base accuracy must be set to 0
136 if (getPosVelBase() != 0.0) {
137 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
138 POS_VEL_ACCURACY_BASE, getPosVelBase(), fileName, getVersion());
139 }
140 if (getClockBase() != 0.0) {
141 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
142 CLOCK_ACCURACY_BASE, getClockBase(), fileName, getVersion());
143 }
144 } else if (hasAccuracy) {
145 // in SP3 versions after version a, the base accuracy must be set if entries specify accuracy
146 if (getPosVelBase() <= 0.0) {
147 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
148 POS_VEL_ACCURACY_BASE, getPosVelBase(), fileName, getVersion());
149 }
150 if (getClockBase() <= 0.0) {
151 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
152 CLOCK_ACCURACY_BASE, getClockBase(), fileName, getVersion());
153 }
154 }
155 if (getVersion() < 'd') {
156 // in SP3 versions a, b, and c, there are exactly 4 comments with max length 57
157 // (60 minus first three characters)
158 if (comments.size() != 4 ||
159 comments.get(0).length() > 57 ||
160 comments.get(1).length() > 57 ||
161 comments.get(2).length() > 57 ||
162 comments.get(3).length() > 57) {
163 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
164 COMMENTS, "/* …", fileName, getVersion());
165 }
166 } else {
167 // starting with SP3 version d, there is an unspecified number of comments with max length 77
168 // (80 minus first three characters)
169 for (final String c : comments) {
170 if (c.length() > 77) {
171 throw new OrekitException(OrekitMessages.SP3_INVALID_HEADER_ENTRY,
172 COMMENTS, c, fileName, getVersion());
173 }
174 }
175 }
176
177 }
178
179 /** Set the file version.
180 * @param version file version
181 */
182 public void setVersion(final char version) {
183 this.version = version;
184 }
185
186 /** Get the file version.
187 * @return file version
188 */
189 public char getVersion() {
190 return version;
191 }
192
193 /** Set the derivatives filter.
194 * @param filter that indicates which derivatives of position are available.
195 */
196 public void setFilter(final CartesianDerivativesFilter filter) {
197 this.filter = filter;
198 }
199
200 /** Get the derivatives filter.
201 * @return filter with available derivatives
202 */
203 public CartesianDerivativesFilter getFilter() {
204 return filter;
205 }
206
207 /** Returns the {@link SP3FileType} associated with this SP3 file.
208 * @return the file type for this SP3 file
209 */
210 public SP3FileType getType() {
211 return type;
212 }
213
214 /** Set the file type for this SP3 file.
215 * @param fileType the file type to be set
216 */
217 public void setType(final SP3FileType fileType) {
218 this.type = fileType;
219 }
220
221 /** Returns the {@link TimeSystem} used to time-stamp position entries.
222 * @return the {@link TimeSystem} of the orbit file
223 */
224 public TimeSystem getTimeSystem() {
225 return timeSystem;
226 }
227
228 /** Set the time system used in this SP3 file.
229 * @param system the time system to be set
230 */
231 public void setTimeSystem(final TimeSystem system) {
232 this.timeSystem = system;
233 }
234
235 /** Returns the data used indicator from the SP3 file.
236 * @return the data used indicator
237 */
238 public List<DataUsed> getDataUsed() {
239 return dataUsed;
240 }
241
242 /** Set the data used indicator for this SP3 file.
243 * @param dataUsed the data used indicator to be set
244 */
245 public void setDataUsed(final List<DataUsed> dataUsed) {
246 this.dataUsed = dataUsed;
247 }
248
249 /** Returns the start epoch of the orbit file.
250 * @return the start epoch
251 */
252 public AbsoluteDate getEpoch() {
253 return epoch;
254 }
255
256 /** Set the epoch of the SP3 file.
257 * @param time the epoch to be set
258 */
259 public void setEpoch(final AbsoluteDate time) {
260 this.epoch = time;
261 }
262
263 /** Returns the GPS week as contained in the SP3 file.
264 * @return the GPS week of the SP3 file
265 */
266 public int getGpsWeek() {
267 return gpsWeek;
268 }
269
270 /** Set the GPS week of the SP3 file.
271 * @param week the GPS week to be set
272 */
273 public void setGpsWeek(final int week) {
274 this.gpsWeek = week;
275 }
276
277 /** Returns the seconds of the GPS week as contained in the SP3 file.
278 * @return the seconds of the GPS week
279 */
280 public double getSecondsOfWeek() {
281 return secondsOfWeek;
282 }
283
284 /** Set the seconds of the GPS week for this SP3 file.
285 * @param seconds the seconds to be set
286 */
287 public void setSecondsOfWeek(final double seconds) {
288 this.secondsOfWeek = seconds;
289 }
290
291 /** Returns the modified julian day for this SP3 file.
292 * @return the modified julian day
293 */
294 public int getModifiedJulianDay() {
295 return modifiedJulianDay;
296 }
297
298 /** Set the modified julian day for this SP3 file.
299 * @param day the modified julian day to be set
300 */
301 public void setModifiedJulianDay(final int day) {
302 this.modifiedJulianDay = day;
303 }
304
305 /** Returns the day fraction for this SP3 file.
306 * @return the day fraction
307 */
308 public double getDayFraction() {
309 return dayFraction;
310 }
311
312 /** Set the day fraction for this SP3 file.
313 * @param fraction the day fraction to be set
314 */
315 public void setDayFraction(final double fraction) {
316 this.dayFraction = fraction;
317 }
318
319 /** Returns the time interval between epochs (in seconds).
320 * @return the time interval between epochs
321 */
322 public double getEpochInterval() {
323 return epochInterval;
324 }
325
326 /** Set the epoch interval for this SP3 file.
327 * @param interval the interval between orbit entries
328 */
329 public void setEpochInterval(final double interval) {
330 this.epochInterval = interval;
331 }
332
333 /** Returns the number of epochs contained in this orbit file.
334 * @return the number of epochs
335 */
336 public int getNumberOfEpochs() {
337 return numberOfEpochs;
338 }
339
340 /** Set the number of epochs as contained in the SP3 file.
341 * @param epochCount the number of epochs to be set
342 */
343 public void setNumberOfEpochs(final int epochCount) {
344 this.numberOfEpochs = epochCount;
345 }
346
347 /** Returns the coordinate system of the entries in this orbit file.
348 * @return the coordinate system
349 */
350 public String getCoordinateSystem() {
351 return coordinateSystem;
352 }
353
354 /** Set the coordinate system used for the orbit entries.
355 * @param system the coordinate system to be set
356 */
357 public void setCoordinateSystem(final String system) {
358 this.coordinateSystem = system;
359 }
360
361 /** Returns the {@link SP3OrbitType} for this SP3 file.
362 * @return the orbit type
363 */
364 public SP3OrbitType getOrbitType() {
365 return orbitType;
366 }
367
368 /** Returns the orbit type key for this SP3 file.
369 * @return the orbit type key
370 */
371 public String getOrbitTypeKey() {
372 return orbitTypeKey;
373 }
374
375 /** Set the orbit type key for this SP3 file.
376 * @param oTypeKey the orbit type key to be set
377 */
378 public void setOrbitTypeKey(final String oTypeKey) {
379 this.orbitTypeKey = oTypeKey;
380 this.orbitType = SP3OrbitType.parseType(oTypeKey);
381 }
382
383 /** Returns the agency that prepared this SP3 file.
384 * @return the agency
385 */
386 public String getAgency() {
387 return agency;
388 }
389
390 /** Set the agency string for this SP3 file.
391 * @param agencyStr the agency string to be set
392 */
393 public void setAgency(final String agencyStr) {
394 this.agency = agencyStr;
395 }
396
397 /** Set the base for position/velocity accuracy.
398 * @param posVelBase base for position/velocity accuracy
399 */
400 public void setPosVelBase(final double posVelBase) {
401 this.posVelBase = posVelBase;
402 }
403
404 /** Get the base for position/velocity accuracy.
405 * @return base for position/velocity accuracy
406 */
407 public double getPosVelBase() {
408 return posVelBase;
409 }
410
411 /** Set the base for clock/clock-rate accuracy.
412 * @param clockBase base for clock/clock-rate accuracy
413 */
414 public void setClockBase(final double clockBase) {
415 this.clockBase = clockBase;
416 }
417
418 /** Get the base for clock/clock-rate accuracy.
419 * @return base for clock/clock-rate accuracy
420 */
421 public double getClockBase() {
422 return clockBase;
423 }
424
425 /** Add a satellite identifier.
426 * @param satId satellite identifier
427 */
428 public void addSatId(final String satId) {
429 satIds.add(satId);
430 }
431
432 /** Get the satellite identifiers.
433 * @return satellites identifiers
434 */
435 public List<String> getSatIds() {
436 return Collections.unmodifiableList(satIds);
437 }
438
439 /** Set the accuracy.
440 * @param index satellite index in {@link #getSatIds()}
441 * @param accuracy in m
442 */
443 public void setAccuracy(final int index, final double accuracy) {
444 if (accuracies == null) {
445 // lazy allocation of the array
446 accuracies = new double[satIds.size()];
447 }
448 accuracies[index] = accuracy;
449 }
450
451 /** Get the formal accuracy.
452 * <p>
453 * The accuracy is limited by the SP3 standard to be a power of 2 in mm.
454 * The value returned here is in meters.
455 * </p>
456 * @param satId satellite identifier
457 * @return magnitude of one standard deviation, in m.
458 */
459 public double getAccuracy(final String satId) {
460 for (int i = 0; i < satIds.size(); ++i) {
461 if (satIds.get(i).equals(satId)) {
462 return accuracies[i];
463 }
464 }
465 return Double.NaN;
466 }
467
468 /** Get the comments.
469 * @return an unmodifiable view of comments
470 */
471 public List<String> getComments() {
472 return Collections.unmodifiableList(comments);
473 }
474
475 /** Add a comment.
476 * @param comment comment to add
477 */
478 public void addComment(final String comment) {
479 comments.add(comment);
480 }
481
482 }