1 /* Copyright 2002-2018 CS Systèmes d'Information
2 * Licensed to CS Systèmes d'Information (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 import java.io.BufferedReader;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.InputStreamReader;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
27
28 import org.hipparchus.exception.DummyLocalizable;
29 import org.hipparchus.geometry.euclidean.threed.Vector3D;
30 import org.hipparchus.geometry.euclidean.twod.Vector2D;
31 import org.hipparchus.util.FastMath;
32 import org.orekit.data.DataLoader;
33 import org.orekit.data.DataProvidersManager;
34 import org.orekit.errors.OrekitException;
35 import org.orekit.errors.OrekitMessages;
36 import org.orekit.time.AbsoluteDate;
37 import org.orekit.time.TimeScale;
38 import org.orekit.time.TimeScalesFactory;
39
40 public class RinexLoader {
41
42 /** Default supported files name pattern for rinex 2 observation files. */
43 public static final String DEFAULT_RINEX_2_SUPPORTED_NAMES = "^\\w{4}\\d{3}[0a-x](?:\\d{2})?\\.\\d{2}[oO]$";
44
45 /** Default supported files name pattern for rinex 3 observation files. */
46 public static final String DEFAULT_RINEX_3_SUPPORTED_NAMES = "^\\w{9}_\\w{1}_\\d{11}_\\d{2}\\w_\\d{2}\\w{1}_\\w{2}\\.rnx$";
47
48 // CHECKSTYLE: stop JavadocVariable check
49 private static final String RINEX_VERSION_TYPE = "RINEX VERSION / TYPE";
50 private static final String COMMENT = "COMMENT";
51 private static final String PGM_RUN_BY_DATE = "PGM / RUN BY / DATE";
52 private static final String MARKER_NAME = "MARKER NAME";
53 private static final String MARKER_NUMBER = "MARKER NUMBER";
54 private static final String MARKER_TYPE = "MARKER TYPE";
55 private static final String OBSERVER_AGENCY = "OBSERVER / AGENCY";
56 private static final String REC_NB_TYPE_VERS = "REC # / TYPE / VERS";
57 private static final String ANT_NB_TYPE = "ANT # / TYPE";
58 private static final String APPROX_POSITION_XYZ = "APPROX POSITION XYZ";
59 private static final String ANTENNA_DELTA_H_E_N = "ANTENNA: DELTA H/E/N";
60 private static final String ANTENNA_DELTA_X_Y_Z = "ANTENNA: DELTA X/Y/Z";
61 private static final String ANTENNA_PHASECENTER = "ANTENNA: PHASECENTER";
62 private static final String ANTENNA_B_SIGHT_XYZ = "ANTENNA: B.SIGHT XYZ";
63 private static final String ANTENNA_ZERODIR_AZI = "ANTENNA: ZERODIR AZI";
64 private static final String ANTENNA_ZERODIR_XYZ = "ANTENNA: ZERODIR XYZ";
65 private static final String NB_OF_SATELLITES = "# OF SATELLITES";
66 private static final String WAVELENGTH_FACT_L1_2 = "WAVELENGTH FACT L1/2";
67 private static final String RCV_CLOCK_OFFS_APPL = "RCV CLOCK OFFS APPL";
68 private static final String INTERVAL = "INTERVAL";
69 private static final String TIME_OF_FIRST_OBS = "TIME OF FIRST OBS";
70 private static final String TIME_OF_LAST_OBS = "TIME OF LAST OBS";
71 private static final String LEAP_SECONDS = "LEAP SECONDS";
72 private static final String PRN_NB_OF_OBS = "PRN / # OF OBS";
73 private static final String NB_TYPES_OF_OBSERV = "# / TYPES OF OBSERV";
74 private static final String END_OF_HEADER = "END OF HEADER";
75 private static final String CENTER_OF_MASS_XYZ = "CENTER OF MASS: XYZ";
76 private static final String SIGNAL_STRENGTH_UNIT = "SIGNAL STRENGTH UNIT";
77 private static final String SYS_NB_OBS_TYPES = "SYS / # / OBS TYPES";
78 private static final String SYS_DCBS_APPLIED = "SYS / DCBS APPLIED";
79 private static final String SYS_PCVS_APPLIED = "SYS / PCVS APPLIED";
80 private static final String SYS_SCALE_FACTOR = "SYS / SCALE FACTOR";
81 private static final String SYS_PHASE_SHIFT = "SYS / PHASE SHIFT";
82 private static final String GLONASS_SLOT_FRQ_NB = "GLONASS SLOT / FRQ #";
83 private static final String GLONASS_COD_PHS_BIS = "GLONASS COD/PHS/BIS";
84
85 private static final String GPS = "GPS";
86 private static final String GAL = "GAL";
87 private static final String GLO = "GLO";
88 private static final String QZS = "QZS";
89 private static final String BDT = "BDT";
90 private static final String IRN = "IRN";
91 // CHECKSTYLE: resume JavadocVariable check
92
93 /** Rinex Observations, grouped by file. */
94 private final Map<RinexHeader, List<ObservationDataSet>> observations;
95
96 /** Simple constructor.
97 * <p>
98 * This constructor is used when the rinex files are managed by the
99 * global {@link DataProvidersManager DataProvidersManager}.
100 * </p>
101 * @param supportedNames regular expression for supported files names
102 * @exception OrekitException if no rinex file can be read
103 */
104 public RinexLoader(final String supportedNames)
105 throws OrekitException {
106 observations = new HashMap<>();
107 DataProvidersManager.getInstance().feed(supportedNames, new Parser());
108 }
109
110 /** Simple constructor.
111 * @param input data input stream
112 * @param name name of the file (or zip entry)
113 * @exception OrekitException if no rinex file can be read
114 */
115 public RinexLoader(final InputStream input, final String name)
116 throws OrekitException {
117 try {
118 observations = new HashMap<>();
119 new Parser().loadData(input, name);
120 } catch (IOException ioe) {
121 throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage()));
122 }
123 }
124
125 /** Add a Rinex header.
126 * @param header rinex header to add
127 * @return the list into which observations should be added
128 */
129 private List<ObservationDataSet> addHeader(final RinexHeader header) {
130 final List<ObservationDataSet> list = new ArrayList<>();
131 observations.put(header, list);
132 return list;
133 }
134
135 /** Get parsed rinex observations.
136 * @return unmodifiable view of parsed rinex observations
137 */
138 public Map<RinexHeader, List<ObservationDataSet>> getObservations() {
139 return Collections.unmodifiableMap(observations);
140 }
141
142 /** Parser for rinex files.
143 */
144 public class Parser implements DataLoader {
145
146 /** Index of label in data lines. */
147 private static final int LABEL_START = 60;
148
149 /** File type Accepted (only Observation Data). */
150 private static final String FILE_TYPE = "O"; //Only Observation Data files
151
152 /** {@inheritDoc} */
153 @Override
154 public boolean stillAcceptsData() {
155 // we load all rinex files we can find
156 return true;
157 }
158
159 /** {@inheritDoc} */
160 @Override
161 public void loadData(final InputStream input, final String name)
162 throws IOException, OrekitException {
163
164 try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"))) {
165
166 // placeholders for parsed data
167 SatelliteSystem satelliteSystem = null;
168 double formatVersion = Double.NaN;
169 boolean inRinexVersion = false;
170 int lineNumber = 0;
171 SatelliteSystem obsTypesSystem = null;
172 String markerName = null;
173 String markerNumber = null;
174 String markerType = null;
175 String observerName = null;
176 String agencyName = null;
177 String receiverNumber = null;
178 String receiverType = null;
179 String receiverVersion = null;
180 String antennaNumber = null;
181 String antennaType = null;
182 Vector3D approxPos = null;
183 Vector3D antRefPoint = null;
184 String obsCode = null;
185 Vector3D antPhaseCenter = null;
186 Vector3D antBSight = null;
187 double antAzi = Double.NaN;
188 Vector3D antZeroDir = null;
189 Vector3D centerMass = null;
190 double antHeight = Double.NaN;
191 Vector2D eccentricities = Vector2D.ZERO;
192 int clkOffset = -1;
193 int nbTypes = -1;
194 int nbSat = -1;
195 double interval = Double.NaN;
196 AbsoluteDate tFirstObs = AbsoluteDate.PAST_INFINITY;
197 AbsoluteDate tLastObs = AbsoluteDate.FUTURE_INFINITY;
198 TimeScale timeScale = null;
199 String timeScaleStr = null;
200 int leapSeconds = 0;
201 AbsoluteDate tObs = AbsoluteDate.PAST_INFINITY;
202 String[] satsObsList = null;
203 String strYear = null;
204 int eventFlag = -1;
205 int nbSatObs = -1;
206 int nbLinesSat = -1;
207 double rcvrClkOffset = 0;
208 boolean inRunBy = false;
209 boolean inMarkerName = false;
210 boolean inMarkerType = false;
211 boolean inObserver = false;
212 boolean inRecType = false;
213 boolean inAntType = false;
214 boolean inAproxPos = false;
215 boolean inAntDelta = false;
216 boolean inTypesObs = false;
217 boolean inFirstObs = false;
218 boolean inPhaseShift = false;
219 boolean inGlonassSlot = false;
220 boolean inGlonassCOD = false;
221 List<ObservationDataSet> observationsList = null;
222
223 //First line must always contain Rinex Version, File Type and Satellite Systems Observed
224 String line = reader.readLine();
225 lineNumber++;
226 formatVersion = parseDouble(line, 0, 9);
227 int format100 = (int) FastMath.rint(100 * formatVersion);
228
229 if ((format100 != 200) && (format100 != 210) && (format100 != 211) &&
230 (format100 != 300) && (format100 != 301) && (format100 != 302) && (format100 != 303)) {
231 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
232 }
233
234 //File Type must be Observation_Data
235 if (!(parseString(line, 20, 1)).equals(FILE_TYPE)) {
236 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
237 }
238 satelliteSystem = SatelliteSystem.parseSatelliteSystem(parseString(line, 40, 1));
239 inRinexVersion = true;
240
241 switch (format100 / 100) {
242 case 2:
243
244 final int MAX_OBS_TYPES_PER_LINE_RNX2 = 9;
245 final int MAX_N_SAT_OBSERVATION = 12;
246 final int MAX_N_TYPES_OBSERVATION = 5;
247 final List<ObservationType> typesObs = new ArrayList<>();
248
249 for (line = reader.readLine(); line != null; line = reader.readLine()) {
250 ++lineNumber;
251
252 if (observationsList == null) {
253 switch(line.substring(LABEL_START).trim()) {
254 case RINEX_VERSION_TYPE :
255
256 formatVersion = parseDouble(line, 0, 9);
257 //File Type must be Observation_Data
258 if (!(parseString(line, 20, 1)).equals(FILE_TYPE)) {
259 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
260 }
261 satelliteSystem = SatelliteSystem.parseSatelliteSystem(parseString(line, 40, 1));
262 inRinexVersion = true;
263 break;
264 case COMMENT :
265 // nothing to do
266 break;
267 case PGM_RUN_BY_DATE :
268 inRunBy = true;
269 break;
270 case MARKER_NAME :
271 markerName = parseString(line, 0, 60);
272 inMarkerName = true;
273 break;
274 case MARKER_NUMBER :
275 markerNumber = parseString(line, 0, 20);
276 break;
277 case OBSERVER_AGENCY :
278 observerName = parseString(line, 0, 20);
279 agencyName = parseString(line, 20, 40);
280 inObserver = true;
281 break;
282 case REC_NB_TYPE_VERS :
283 receiverNumber = parseString(line, 0, 20);
284 receiverType = parseString(line, 20, 20);
285 receiverVersion = parseString(line, 40, 20);
286 inRecType = true;
287 break;
288 case ANT_NB_TYPE :
289 antennaNumber = parseString(line, 0, 20);
290 antennaType = parseString(line, 20, 20);
291 inAntType = true;
292 break;
293 case APPROX_POSITION_XYZ :
294 approxPos = new Vector3D(parseDouble(line, 0, 14), parseDouble(line, 14, 14),
295 parseDouble(line, 28, 14));
296 inAproxPos = true;
297 break;
298 case ANTENNA_DELTA_H_E_N :
299 antHeight = parseDouble(line, 0, 14);
300 eccentricities = new Vector2D(parseDouble(line, 14, 14), parseDouble(line, 28, 14));
301 inAntDelta = true;
302 break;
303 case NB_OF_SATELLITES :
304 nbSat = parseInt(line, 0, 6);
305 break;
306 case WAVELENGTH_FACT_L1_2 :
307 //Optional line in header
308 //Not stored for now
309 break;
310 case RCV_CLOCK_OFFS_APPL :
311 clkOffset = parseInt(line, 0, 6);
312 break;
313 case INTERVAL :
314 interval = parseDouble(line, 0, 10);
315 break;
316 case TIME_OF_FIRST_OBS :
317 switch (satelliteSystem) {
318 case GPS:
319 timeScale = TimeScalesFactory.getGPS();
320 break;
321 case GALILEO:
322 timeScale = TimeScalesFactory.getGST();
323 break;
324 case GLONASS:
325 timeScale = TimeScalesFactory.getGLONASS();
326 break;
327 case MIXED:
328 //in Case of Mixed data, Timescale must be specified in the Time of First line
329 timeScaleStr = parseString(line, 48, 3);
330
331 if (timeScaleStr.equals(GPS)) {
332 timeScale = TimeScalesFactory.getGPS();
333 } else if (timeScaleStr.equals(GAL)) {
334 timeScale = TimeScalesFactory.getGST();
335 } else if (timeScaleStr.equals(GLO)) {
336 timeScale = TimeScalesFactory.getGLONASS();
337 } else {
338 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
339 }
340 break;
341 default :
342 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
343 lineNumber, name, line);
344 }
345
346 tFirstObs = new AbsoluteDate(parseInt(line, 0, 6),
347 parseInt(line, 6, 6),
348 parseInt(line, 12, 6),
349 parseInt(line, 18, 6),
350 parseInt(line, 24, 6),
351 parseDouble(line, 30, 13), timeScale);
352 inFirstObs = true;
353 break;
354 case TIME_OF_LAST_OBS :
355 tLastObs = new AbsoluteDate(parseInt(line, 0, 6),
356 parseInt(line, 6, 6),
357 parseInt(line, 12, 6),
358 parseInt(line, 18, 6),
359 parseInt(line, 24, 6),
360 parseDouble(line, 30, 13), timeScale);
361 break;
362 case LEAP_SECONDS :
363 leapSeconds = parseInt(line, 0, 6);
364 break;
365 case PRN_NB_OF_OBS :
366 //Optional line in header, indicates number of Observations par Satellite
367 //Not stored for now
368 break;
369 case NB_TYPES_OF_OBSERV :
370 nbTypes = parseInt(line, 0, 6);
371 final int nbLinesTypesObs = (nbTypes + MAX_OBS_TYPES_PER_LINE_RNX2 - 1 ) / MAX_OBS_TYPES_PER_LINE_RNX2;
372
373 for (int j = 0; j < nbLinesTypesObs; j++) {
374 if (j > 0) {
375 line = reader.readLine(); //Next line
376 lineNumber++;
377 }
378 final int iMax = FastMath.min(MAX_OBS_TYPES_PER_LINE_RNX2, nbTypes - typesObs.size());
379 for (int i = 0; i < iMax; i++) {
380 try {
381 typesObs.add(ObservationType.valueOf(parseString(line, 10 + (6 * i), 2)));
382 } catch (IllegalArgumentException iae) {
383 throw new OrekitException(OrekitMessages.UNKNOWN_RINEX_FREQUENCY,
384 parseString(line, 10 + (6 * i), 2), name, lineNumber);
385 }
386 }
387 }
388 inTypesObs = true;
389 break;
390 case END_OF_HEADER :
391 //We make sure that we have read all the mandatory fields inside the header of the Rinex
392 if (!inRinexVersion || !inRunBy || !inMarkerName ||
393 !inObserver || !inRecType || !inAntType ||
394 !inAproxPos || !inAntDelta || !inTypesObs || !inFirstObs) {
395 throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, name);
396 }
397
398 //Header information gathered
399 observationsList = addHeader(new RinexHeader(formatVersion, satelliteSystem,
400 markerName, markerNumber, observerName,
401 agencyName, receiverNumber, receiverType,
402 receiverVersion, antennaNumber, antennaType,
403 approxPos, antHeight, eccentricities, interval,
404 tFirstObs, tLastObs, clkOffset, leapSeconds));
405 break;
406 default :
407 if (observationsList == null) {
408 //There must be an error due to an unknown Label inside the Header
409 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
410 lineNumber, name, line);
411 }
412 }
413 } else {
414
415 //Start of a new Observation
416 rcvrClkOffset = 0;
417 nbLinesSat = -1;
418 eventFlag = -1;
419 nbSatObs = -1;
420 satsObsList = null;
421 tObs = null;
422 strYear = null;
423
424 eventFlag = parseInt(line, 28, 1);
425 //If eventFlag>1, we skip the corresponding lines to the next observation
426 if (eventFlag != 0) {
427 if (eventFlag == 6) {
428 nbSatObs = parseInt(line, 29, 3);
429 nbLinesSat = (nbSatObs + 12 - 1) / 12;
430 final int nbLinesObs = (nbTypes + 5 - 1) / 5;
431 final int nbLinesSkip = (nbLinesSat - 1) + nbSatObs * nbLinesObs;
432 for (int i = 0; i < nbLinesSkip; i++) {
433 line = reader.readLine(); //Next line
434 lineNumber++;
435 }
436 } else {
437 final int nbLinesSkip = parseInt(line, 29, 3);
438 for (int i = 0; i < nbLinesSkip; i++) {
439 line = reader.readLine(); //Next line
440 lineNumber++;
441 }
442 }
443 } else {
444
445 final int y = Integer.parseInt(parseString(line, 0, 3));
446 if (79 < y && y <= 99) {
447 strYear = "19" + y;
448 } else if (0 <= y && y <= 79) {
449 strYear = "20" + parseString(line, 0, 3);
450 }
451 tObs = new AbsoluteDate(Integer.parseInt(strYear),
452 parseInt(line, 3, 3),
453 parseInt(line, 6, 3),
454 parseInt(line, 9, 3),
455 parseInt(line, 12, 3),
456 parseDouble(line, 15, 11), timeScale);
457
458 nbSatObs = parseInt(line, 29, 3);
459 satsObsList = new String[nbSatObs];
460 //If the total number of satellites was indicated in the Header
461 if (nbSat != -1 && nbSatObs > nbSat) {
462 //we check that the number of Sat in the observation is consistent
463 throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS,
464 lineNumber, name, nbSatObs, nbSat);
465 }
466
467 nbLinesSat = (nbSatObs + MAX_N_SAT_OBSERVATION - 1) / MAX_N_SAT_OBSERVATION;
468 for (int j = 0; j < nbLinesSat; j++) {
469 if (j > 0) {
470 line = reader.readLine(); //Next line
471 lineNumber++;
472 }
473 final int iMax = FastMath.min(MAX_N_SAT_OBSERVATION, nbSatObs - j * MAX_N_SAT_OBSERVATION);
474 for (int i = 0; i < iMax; i++) {
475 satsObsList[i + MAX_N_SAT_OBSERVATION * j] = parseString(line, 32 + 3 * i, 3);
476 }
477
478 //Read the Receiver Clock offset, if present
479 rcvrClkOffset = parseDouble(line, 68, 12);
480 if (Double.isNaN(rcvrClkOffset)) {
481 rcvrClkOffset = 0.0;
482 }
483
484 }
485
486 //For each one of the Satellites in this observation
487 final int nbLinesObs = (nbTypes + MAX_N_TYPES_OBSERVATION - 1) / MAX_N_TYPES_OBSERVATION;
488 for (int k = 0; k < nbSatObs; k++) {
489
490
491 //Once the Date and Satellites list is read:
492 // - to read the Data for each satellite
493 // - 5 Observations per line
494 final List<ObservationData> observationData = new ArrayList<>(nbSatObs);
495 for (int j = 0; j < nbLinesObs; j++) {
496 line = reader.readLine(); //Next line
497 lineNumber++;
498 final int iMax = FastMath.min(MAX_N_TYPES_OBSERVATION, nbTypes - observationData.size());
499 for (int i = 0; i < iMax; i++) {
500 observationData.add(new ObservationData(typesObs.get(observationData.size()),
501 parseDouble(line, 16 * i, 14),
502 parseInt(line, 14 + 16 * i, 1),
503 parseInt(line, 15 + 16 * i, 1)));
504 }
505 }
506
507 //We check that the Satellite type is consistent with Satellite System in the top of the file
508 final SatelliteSystem satelliteSystemSat = SatelliteSystem.parseSatelliteSystem(satsObsList[k]);
509 if (!satelliteSystem.equals(SatelliteSystem.MIXED)) {
510 if (!satelliteSystemSat.equals(satelliteSystem)) {
511 throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM,
512 lineNumber, name, satelliteSystem, satelliteSystemSat);
513 }
514 }
515
516 final int prnNumber;
517 switch (satelliteSystemSat) {
518 case GPS:
519 case GLONASS:
520 case GALILEO:
521 prnNumber = Integer.parseInt(satsObsList[k].substring(1, 3).trim());
522 break;
523 case SBAS:
524 prnNumber = Integer.parseInt(satsObsList[k].substring(1, 3).trim()) + 100;
525 break;
526 default:
527 // MIXED satellite system is not allowed here
528 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
529 lineNumber, name, line);
530 }
531
532 observationsList.add(new ObservationDataSet(satelliteSystemSat, prnNumber, tObs, rcvrClkOffset,
533 observationData));
534
535 }
536 }
537 }
538 }
539 break;
540 case 3:
541
542 final int MAX_OBS_TYPES_PER_LINE_RNX3 = 13;
543 final int MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE = 12;
544 final int MAX_N_SAT_PHSHIFT_PER_LINE = 10;
545
546 final Map<SatelliteSystem, List<ObservationType>> listTypeObs = new HashMap<>();
547 final List<ObservationType> typeObs = new ArrayList<>();
548 String sigStrengthUnit = null;
549 int leapSecondsFuture = 0;
550 int leapSecondsWeekNum = 0;
551 int leapSecondsDayNum = 0;
552 final List<AppliedDCBS> listAppliedDCBs = new ArrayList<>();
553 final List<AppliedPCVS> listAppliedPCVS = new ArrayList<>();
554 SatelliteSystem satSystemScaleFactor = null;
555 int scaleFactor = 1;
556 int nbObsScaleFactor = 0;
557 final List<ObservationType> typesObsScaleFactor = new ArrayList<>();
558 final List<ScaleFactorCorrection> scaleFactorCorrections = new ArrayList<>();
559 String[] satsPhaseShift = null;
560 int nbSatPhaseShift = 0;
561 SatelliteSystem satSystemPhaseShift = null;
562 double corrPhaseShift = 0.0;
563 final List<PhaseShiftCorrection> phaseShiftCorrections = new ArrayList<>();
564 ObservationType phaseShiftTypeObs = null;
565
566
567 for (line = reader.readLine(); line != null; line = reader.readLine()) {
568 ++lineNumber;
569 if (observationsList == null) {
570 switch(line.substring(LABEL_START).trim()) {
571 case RINEX_VERSION_TYPE : {
572 formatVersion = parseDouble(line, 0, 9);
573 format100 = (int) FastMath.rint(100 * formatVersion);
574 if ((format100 != 300) && (format100 != 301) && (format100 != 302) && (format100 != 303)) {
575 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
576 }
577 //File Type must be Observation_Data
578 if (!(parseString(line, 20, 1)).equals(FILE_TYPE)) {
579 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
580 }
581 satelliteSystem = SatelliteSystem.parseSatelliteSystem(parseString(line, 40, 1));
582 inRinexVersion = true;
583 }
584 break;
585 case COMMENT :
586 // nothing to do
587 break;
588 case PGM_RUN_BY_DATE :
589 inRunBy = true;
590 break;
591 case MARKER_NAME :
592 markerName = parseString(line, 0, 60);
593 inMarkerName = true;
594 break;
595 case MARKER_NUMBER :
596 markerNumber = parseString(line, 0, 20);
597 break;
598 case MARKER_TYPE :
599 markerType = parseString(line, 0, 20);
600 inMarkerType = true;
601 //Could be done with an Enumeration
602 break;
603 case OBSERVER_AGENCY :
604 observerName = parseString(line, 0, 20);
605 agencyName = parseString(line, 20, 40);
606 inObserver = true;
607 break;
608 case REC_NB_TYPE_VERS :
609 receiverNumber = parseString(line, 0, 20);
610 receiverType = parseString(line, 20, 20);
611 receiverVersion = parseString(line, 40, 20);
612 inRecType = true;
613 break;
614 case ANT_NB_TYPE :
615 antennaNumber = parseString(line, 0, 20);
616 antennaType = parseString(line, 20, 20);
617 inAntType = true;
618 break;
619 case APPROX_POSITION_XYZ :
620 approxPos = new Vector3D(parseDouble(line, 0, 14),
621 parseDouble(line, 14, 14),
622 parseDouble(line, 28, 14));
623 inAproxPos = true;
624 break;
625 case ANTENNA_DELTA_H_E_N :
626 antHeight = parseDouble(line, 0, 14);
627 eccentricities = new Vector2D(parseDouble(line, 14, 14),
628 parseDouble(line, 28, 14));
629 inAntDelta = true;
630 break;
631 case ANTENNA_DELTA_X_Y_Z :
632 antRefPoint = new Vector3D(parseDouble(line, 0, 14),
633 parseDouble(line, 14, 14),
634 parseDouble(line, 28, 14));
635 break;
636 case ANTENNA_PHASECENTER :
637 obsCode = parseString(line, 2, 3);
638 antPhaseCenter = new Vector3D(parseDouble(line, 5, 9),
639 parseDouble(line, 14, 14),
640 parseDouble(line, 28, 14));
641 break;
642 case ANTENNA_B_SIGHT_XYZ :
643 antBSight = new Vector3D(parseDouble(line, 0, 14),
644 parseDouble(line, 14, 14),
645 parseDouble(line, 28, 14));
646 break;
647 case ANTENNA_ZERODIR_AZI :
648 antAzi = parseDouble(line, 0, 14);
649 break;
650 case ANTENNA_ZERODIR_XYZ :
651 antZeroDir = new Vector3D(parseDouble(line, 0, 14),
652 parseDouble(line, 14, 14),
653 parseDouble(line, 28, 14));
654 break;
655 case CENTER_OF_MASS_XYZ :
656 centerMass = new Vector3D(parseDouble(line, 0, 14),
657 parseDouble(line, 14, 14),
658 parseDouble(line, 28, 14));
659 break;
660 case NB_OF_SATELLITES :
661 nbSat = parseInt(line, 0, 6);
662 break;
663 case RCV_CLOCK_OFFS_APPL :
664 clkOffset = parseInt(line, 0, 6);
665 break;
666 case INTERVAL :
667 interval = parseDouble(line, 0, 10);
668 break;
669 case TIME_OF_FIRST_OBS :
670 switch(satelliteSystem) {
671 case GPS:
672 timeScale = TimeScalesFactory.getGPS();
673 break;
674 case GALILEO:
675 timeScale = TimeScalesFactory.getGST();
676 break;
677 case GLONASS:
678 timeScale = TimeScalesFactory.getGLONASS();
679 break;
680 case QZSS:
681 timeScale = TimeScalesFactory.getQZSS();
682 break;
683 case BEIDOU:
684 timeScale = TimeScalesFactory.getBDT();
685 break;
686 case IRNSS:
687 timeScale = TimeScalesFactory.getIRNSS();
688 break;
689 case MIXED:
690 //in Case of Mixed data, Timescale must be specified in the Time of First line
691 timeScaleStr = parseString(line, 48, 3);
692
693 if (timeScaleStr.equals(GPS)) {
694 timeScale = TimeScalesFactory.getGPS();
695 } else if (timeScaleStr.equals(GAL)) {
696 timeScale = TimeScalesFactory.getGST();
697 } else if (timeScaleStr.equals(GLO)) {
698 timeScale = TimeScalesFactory.getGLONASS();
699 } else if (timeScaleStr.equals(QZS)) {
700 timeScale = TimeScalesFactory.getQZSS();
701 } else if (timeScaleStr.equals(BDT)) {
702 timeScale = TimeScalesFactory.getBDT();
703 } else if (timeScaleStr.equals(IRN)) {
704 timeScale = TimeScalesFactory.getIRNSS();
705 } else {
706 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
707 }
708 break;
709 default :
710 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
711 lineNumber, name, line);
712 }
713
714 tFirstObs = new AbsoluteDate(parseInt(line, 0, 6),
715 parseInt(line, 6, 6),
716 parseInt(line, 12, 6),
717 parseInt(line, 18, 6),
718 parseInt(line, 24, 6),
719 parseDouble(line, 30, 13), timeScale);
720 inFirstObs = true;
721 break;
722 case TIME_OF_LAST_OBS :
723 tLastObs = new AbsoluteDate(parseInt(line, 0, 6),
724 parseInt(line, 6, 6),
725 parseInt(line, 12, 6),
726 parseInt(line, 18, 6),
727 parseInt(line, 24, 6),
728 parseDouble(line, 30, 13), timeScale);
729 break;
730 case LEAP_SECONDS :
731 leapSeconds = parseInt(line, 0, 6);
732 leapSecondsFuture = parseInt(line, 6, 6);
733 leapSecondsWeekNum = parseInt(line, 12, 6);
734 leapSecondsDayNum = parseInt(line, 18, 6);
735 //Time System Identifier must be added, last A3 String
736 break;
737 case PRN_NB_OF_OBS :
738 //Optional line in header, indicates number of Observations par Satellite
739 //Not stored for now
740 break;
741 case SYS_NB_OBS_TYPES :
742 obsTypesSystem = null;
743 typeObs.clear();
744
745 obsTypesSystem = SatelliteSystem.parseSatelliteSystem(parseString(line, 0, 1));
746 nbTypes = parseInt(line, 3, 3);
747
748 final int nbLinesTypesObs = (nbTypes + MAX_OBS_TYPES_PER_LINE_RNX3 - 1) / MAX_OBS_TYPES_PER_LINE_RNX3;
749 for (int j = 0; j < nbLinesTypesObs; j++) {
750 if (j > 0) {
751 line = reader.readLine(); //Next line
752 lineNumber++;
753 }
754 final int iMax = FastMath.min(MAX_OBS_TYPES_PER_LINE_RNX3, nbTypes - typeObs.size());
755 for (int i = 0; i < iMax; i++) {
756 try {
757 typeObs.add(ObservationType.valueOf(parseString(line, 7 + (4 * i), 3)));
758 } catch (IllegalArgumentException iae) {
759 throw new OrekitException(OrekitMessages.UNKNOWN_RINEX_FREQUENCY,
760 parseString(line, 7 + (4 * i), 3), name, lineNumber);
761 }
762 }
763 }
764 listTypeObs.put(obsTypesSystem, new ArrayList<>(typeObs));
765 inTypesObs = true;
766 break;
767 case SIGNAL_STRENGTH_UNIT :
768 sigStrengthUnit = parseString(line, 0, 20);
769 break;
770 case SYS_DCBS_APPLIED :
771
772 listAppliedDCBs.add(new AppliedDCBS(SatelliteSystem.parseSatelliteSystem(parseString(line, 0, 1)),
773 parseString(line, 2, 17), parseString(line, 20, 40)));
774 break;
775 case SYS_PCVS_APPLIED :
776
777 listAppliedPCVS.add(new AppliedPCVS(SatelliteSystem.parseSatelliteSystem(parseString(line, 0, 1)),
778 parseString(line, 2, 17), parseString(line, 20, 40)));
779 break;
780 case SYS_SCALE_FACTOR :
781 satSystemScaleFactor = null;
782 scaleFactor = 1;
783 nbObsScaleFactor = 0;
784
785 satSystemScaleFactor = SatelliteSystem.parseSatelliteSystem(parseString(line, 0, 1));
786 scaleFactor = parseInt(line, 2, 4);
787 nbObsScaleFactor = parseInt(line, 8, 2);
788
789 if (nbObsScaleFactor == 0) {
790 typesObsScaleFactor.addAll(listTypeObs.get(satSystemScaleFactor));
791 } else {
792 final int nbLinesTypesObsScaleFactor = (nbObsScaleFactor + MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE - 1) /
793 MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE;
794 for (int j = 0; j < nbLinesTypesObsScaleFactor; j++) {
795 if ( j > 0) {
796 line = reader.readLine(); //Next line
797 lineNumber++;
798 }
799 final int iMax = FastMath.min(MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE, nbObsScaleFactor - typesObsScaleFactor.size());
800 for (int i = 0; i < iMax; i++) {
801 typesObsScaleFactor.add(ObservationType.valueOf(parseString(line, 11 + (4 * i), 3)));
802 }
803 }
804 }
805
806 scaleFactorCorrections.add(new ScaleFactorCorrection(satSystemScaleFactor,
807 scaleFactor, typesObsScaleFactor));
808 break;
809 case SYS_PHASE_SHIFT :
810
811 nbSatPhaseShift = 0;
812 satsPhaseShift = null;
813 corrPhaseShift = 0.0;
814 phaseShiftTypeObs = null;
815 satSystemPhaseShift = null;
816
817 satSystemPhaseShift = SatelliteSystem.parseSatelliteSystem(parseString(line, 0, 1));
818 phaseShiftTypeObs = ObservationType.valueOf(parseString(line, 2, 3));
819 nbSatPhaseShift = parseInt(line, 16, 2);
820 corrPhaseShift = parseDouble(line, 6, 8);
821
822 if (nbSatPhaseShift == 0) {
823 //If nbSat with Phase Shift is not indicated: all the satellites are affected for this Obs Type
824 } else {
825 satsPhaseShift = new String[nbSatPhaseShift];
826 final int nbLinesSatPhaseShift = (nbSatPhaseShift + MAX_N_SAT_PHSHIFT_PER_LINE - 1) / MAX_N_SAT_PHSHIFT_PER_LINE;
827 for (int j = 0; j < nbLinesSatPhaseShift; j++) {
828 if (j > 0) {
829 line = reader.readLine(); //Next line
830 lineNumber++;
831 }
832 final int iMax = FastMath.min(MAX_N_SAT_PHSHIFT_PER_LINE, nbSatPhaseShift - j * MAX_N_SAT_PHSHIFT_PER_LINE);
833 for (int i = 0; i < iMax; i++) {
834 satsPhaseShift[i + 10 * j] = parseString(line, 19 + 4 * i, 3);
835 }
836 }
837 }
838 phaseShiftCorrections.add(new PhaseShiftCorrection(satSystemPhaseShift,
839 phaseShiftTypeObs,
840 corrPhaseShift,
841 satsPhaseShift));
842 inPhaseShift = true;
843 break;
844 case GLONASS_SLOT_FRQ_NB :
845 //Not defined yet
846 inGlonassSlot = true;
847 break;
848 case GLONASS_COD_PHS_BIS :
849 //Not defined yet
850 inGlonassCOD = true;
851 break;
852 case END_OF_HEADER :
853 //We make sure that we have read all the mandatory fields inside the header of the Rinex
854 if (!inRinexVersion || !inRunBy || !inMarkerName ||
855 !inMarkerType || !inObserver || !inRecType || !inAntType ||
856 !inAproxPos || !inAntDelta || !inTypesObs || !inFirstObs ||
857 (formatVersion >= 3.01 && !inPhaseShift) ||
858 (formatVersion >= 3.03 && (!inGlonassSlot || !inGlonassCOD))) {
859 throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, name);
860 }
861
862 //Header information gathered
863 observationsList = addHeader(new RinexHeader(formatVersion, satelliteSystem,
864 markerName, markerNumber, markerType,
865 observerName, agencyName, receiverNumber,
866 receiverType, receiverVersion, antennaNumber,
867 antennaType, approxPos, antHeight, eccentricities,
868 antRefPoint, obsCode, antPhaseCenter, antBSight,
869 antAzi, antZeroDir, centerMass, sigStrengthUnit,
870 interval, tFirstObs, tLastObs, clkOffset, listAppliedDCBs,
871 listAppliedPCVS, phaseShiftCorrections, leapSeconds,
872 leapSecondsFuture, leapSecondsWeekNum, leapSecondsDayNum));
873 break;
874 default :
875 if (observationsList == null) {
876 //There must be an error due to an unknown Label inside the Header
877 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
878 lineNumber, name, line);
879 }
880 }
881 } else {
882 //If End of Header
883
884 //Start of a new Observation
885 rcvrClkOffset = 0;
886 eventFlag = -1;
887 nbSatObs = -1;
888 tObs = null;
889
890 //A line that starts with ">" correspond to a new observation epoch
891 if (parseString(line, 0, 1).equals(">")) {
892
893 eventFlag = parseInt(line, 31, 1);
894 //If eventFlag>1, we skip the corresponding lines to the next observation
895 if (eventFlag != 0) {
896 final int nbLinesSkip = parseInt(line, 32, 3);
897 for (int i = 0; i < nbLinesSkip; i++) {
898 line = reader.readLine();
899 lineNumber++;
900 }
901 } else {
902
903 tObs = new AbsoluteDate(parseInt(line, 2, 4),
904 parseInt(line, 6, 3),
905 parseInt(line, 9, 3),
906 parseInt(line, 12, 3),
907 parseInt(line, 15, 3),
908 parseDouble(line, 18, 11), timeScale);
909
910 nbSatObs = parseInt(line, 32, 3);
911 //If the total number of satellites was indicated in the Header
912 if (nbSat != -1 && nbSatObs > nbSat) {
913 //we check that the number of Sat in the observation is consistent
914 throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS,
915 lineNumber, name, nbSatObs, nbSat);
916 }
917 //Read the Receiver Clock offset, if present
918 rcvrClkOffset = parseDouble(line, 41, 15);
919 if (Double.isNaN(rcvrClkOffset)) {
920 rcvrClkOffset = 0.0;
921 }
922
923 //For each one of the Satellites in this Observation
924 for (int i = 0; i < nbSatObs; i++) {
925
926 line = reader.readLine();
927 lineNumber++;
928
929 //We check that the Satellite type is consistent with Satellite System in the top of the file
930 final SatelliteSystem satelliteSystemSat = SatelliteSystem.parseSatelliteSystem(parseString(line, 0, 1));
931 if (!satelliteSystem.equals(SatelliteSystem.MIXED)) {
932 if (!satelliteSystemSat.equals(satelliteSystem)) {
933 throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM,
934 lineNumber, name, satelliteSystem, satelliteSystemSat);
935 }
936 }
937
938 final int prn = parseInt(line, 1, 2);
939 final int prnNumber;
940 switch (satelliteSystemSat) {
941 case GPS:
942 case GLONASS:
943 case GALILEO:
944 case BEIDOU:
945 case IRNSS:
946 prnNumber = prn;
947 break;
948 case QZSS:
949 prnNumber = prn + 192;
950 break;
951 case SBAS:
952 prnNumber = prn + 100;
953 break;
954 default:
955 // MIXED satellite system is not allowed here
956 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
957 lineNumber, name, line);
958 }
959 final List<ObservationData> observationData = new ArrayList<>(nbSatObs);
960 for (int j = 0; j < listTypeObs.get(satelliteSystemSat).size(); j++) {
961 final ObservationType rf = listTypeObs.get(satelliteSystemSat).get(j);
962 boolean scaleFactorFound = false;
963 //We look for the lines of ScaledFactorCorrections that correspond to this SatSystem
964 int k = 0;
965 double value = parseDouble(line, 3 + j * 16, 14);
966 while (k < scaleFactorCorrections.size() && !scaleFactorFound) {
967 if (scaleFactorCorrections.get(k).getSatelliteSystem().equals(satelliteSystemSat)) {
968 //We check if the next Observation Type to read needs to be scaled
969 if (scaleFactorCorrections.get(k).getTypesObsScaled().contains(rf)) {
970 value /= scaleFactorCorrections.get(k).getCorrection();
971 scaleFactorFound = true;
972 }
973 }
974 k++;
975 }
976 observationData.add(new ObservationData(rf,
977 value,
978 parseInt(line, 17 + j * 16, 1),
979 parseInt(line, 18 + j * 16, 1)));
980 }
981 observationsList.add(new ObservationDataSet(satelliteSystemSat, prnNumber, tObs, rcvrClkOffset,
982 observationData));
983
984 }
985 }
986 }
987 }
988 }
989 break;
990 default:
991 //If RINEX Version is neither 2 nor 3
992 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
993 }
994 }
995 }
996
997
998 /** Extract a string from a line.
999 * @param line to parse
1000 * @param start start index of the string
1001 * @param length length of the string
1002 * @return parsed string
1003 */
1004 private String parseString(final String line, final int start, final int length) {
1005 if (line.length() > start) {
1006 return line.substring(start, FastMath.min(line.length(), start + length)).trim();
1007 } else {
1008 return null;
1009 }
1010 }
1011
1012 /** Extract an integer from a line.
1013 * @param line to parse
1014 * @param start start index of the integer
1015 * @param length length of the integer
1016 * @return parsed integer
1017 */
1018 private int parseInt(final String line, final int start, final int length) {
1019 if (line.length() > start && !parseString(line, start, length).isEmpty()) {
1020 return Integer.parseInt(parseString(line, start, length));
1021 } else {
1022 return 0;
1023 }
1024 }
1025
1026 /** Extract a double from a line.
1027 * @param line to parse
1028 * @param start start index of the real
1029 * @param length length of the real
1030 * @return parsed real, or {@code Double.NaN} if field was empty
1031 */
1032 private double parseDouble(final String line, final int start, final int length) {
1033 if (line.length() > start && !parseString(line, start, length).isEmpty()) {
1034 return Double.parseDouble(parseString(line, start, length));
1035 } else {
1036 return Double.NaN;
1037 }
1038 }
1039
1040 /** Phase Shift corrections.
1041 * Contains the phase shift corrections used to
1042 * generate phases consistent with respect to cycle shifts.
1043 */
1044 public class PhaseShiftCorrection {
1045
1046 /** Satellite System. */
1047 private final SatelliteSystem satSystemPhaseShift;
1048 /** Carrier Phase Observation Code. */
1049 private final ObservationType typeObsPhaseShift;
1050 /** Phase Shift Corrections (cycles). */
1051 private final double phaseShiftCorrection;
1052 /** List of satellites involved. */
1053 private final String[] satsPhaseShift;
1054
1055 /** Simple constructor.
1056 * @param satSystemPhaseShift Satellite System
1057 * @param typeObsPhaseShift Carrier Phase Observation Code
1058 * @param phaseShiftCorrection Phase Shift Corrections (cycles)
1059 * @param satsPhaseShift List of satellites involved
1060 */
1061 private PhaseShiftCorrection(final SatelliteSystem satSystemPhaseShift,
1062 final ObservationType typeObsPhaseShift,
1063 final double phaseShiftCorrection, final String[] satsPhaseShift) {
1064 this.satSystemPhaseShift = satSystemPhaseShift;
1065 this.typeObsPhaseShift = typeObsPhaseShift;
1066 this.phaseShiftCorrection = phaseShiftCorrection;
1067 this.satsPhaseShift = satsPhaseShift;
1068 }
1069
1070 /** Get the Satellite System.
1071 * @return Satellite System.
1072 */
1073 public SatelliteSystem getSatelliteSystem() {
1074 return satSystemPhaseShift;
1075 }
1076 /** Get the Carrier Phase Observation Code.
1077 * @return Carrier Phase Observation Code.
1078 */
1079 public ObservationType getTypeObs() {
1080 return typeObsPhaseShift;
1081 }
1082 /** Get the Phase Shift Corrections.
1083 * @return Phase Shift Corrections (cycles)
1084 */
1085 public double getCorrection() {
1086 return phaseShiftCorrection;
1087 }
1088 /** Get the list of satellites involved.
1089 * @return List of satellites involved (if null, all the sats are involved)
1090 */
1091 public String[] getSatsCorrected() {
1092 //If empty, all the satellites of this constellation are affected for this Observation type
1093 return satsPhaseShift;
1094 }
1095 }
1096
1097 /** Scale Factor to be applied.
1098 * Contains the scale factors of 10 applied to the data before
1099 * being stored into the RINEX file.
1100 */
1101 public class ScaleFactorCorrection {
1102
1103 /** Satellite System. */
1104 private final SatelliteSystem satSystemScaleFactor;
1105 /** List of Observations types that have been scaled. */
1106 private final List<ObservationType> typesObsScaleFactor;
1107 /** Factor to divide stored observations with before use. */
1108 private final double scaleFactor;
1109
1110 /** Simple constructor.
1111 * @param satSystemScaleFactor Satellite System
1112 * @param scaleFactor Factor to divide stored observations (1,10,100,1000)
1113 * @param typesObsScaleFactor List of Observations types that have been scaled
1114 */
1115 private ScaleFactorCorrection(final SatelliteSystem satSystemScaleFactor,
1116 final double scaleFactor,
1117 final List<ObservationType> typesObsScaleFactor) {
1118 this.satSystemScaleFactor = satSystemScaleFactor;
1119 this.scaleFactor = scaleFactor;
1120 this.typesObsScaleFactor = typesObsScaleFactor;
1121 }
1122 /** Get the Satellite System.
1123 * @return Satellite System
1124 */
1125 public SatelliteSystem getSatelliteSystem() {
1126 return satSystemScaleFactor;
1127 }
1128 /** Get the Scale Factor.
1129 * @return Scale Factor
1130 */
1131 public double getCorrection() {
1132 return scaleFactor;
1133 }
1134 /** Get the list of Observation Types scaled.
1135 * @return List of Observation types scaled
1136 */
1137 public List<ObservationType> getTypesObsScaled() {
1138 return typesObsScaleFactor;
1139 }
1140 }
1141
1142 /** Corrections of Differential Code Biases (DCBs) applied.
1143 * Contains information on the programs used to correct the observations
1144 * in RINEX files for differential code biases.
1145 */
1146 public class AppliedDCBS {
1147
1148 /** Satellite system. */
1149 private final SatelliteSystem satelliteSystem;
1150
1151 /** Program name used to apply differential code bias corrections. */
1152 private final String progDCBS;
1153
1154 /** Source of corrections (URL). */
1155 private final String sourceDCBS;
1156
1157 /** Simple constructor.
1158 * @param satelliteSystem satellite system
1159 * @param progDCBS Program name used to apply DCBs
1160 * @param sourceDCBS Source of corrections (URL)
1161 */
1162 private AppliedDCBS(final SatelliteSystem satelliteSystem,
1163 final String progDCBS, final String sourceDCBS) {
1164 this.satelliteSystem = satelliteSystem;
1165 this.progDCBS = progDCBS;
1166 this.sourceDCBS = sourceDCBS;
1167 }
1168
1169 /** Get the satellite system.
1170 * @return satellite system
1171 */
1172 public SatelliteSystem getSatelliteSystem() {
1173 return satelliteSystem;
1174 }
1175
1176 /** Get the program name used to apply DCBs.
1177 * @return Program name used to apply DCBs
1178 */
1179 public String getProgDCBS() {
1180 return progDCBS;
1181 }
1182
1183 /** Get the source of corrections.
1184 * @return Source of corrections (URL)
1185 */
1186 public String getSourceDCBS() {
1187 return sourceDCBS;
1188 }
1189
1190 }
1191
1192 /** Corrections of antenna phase center variations (PCVs) applied.
1193 * Contains information on the programs used to correct the observations
1194 * in RINEX files for antenna phase center variations.
1195 */
1196 public class AppliedPCVS {
1197
1198 /** Satellite system. */
1199 private final SatelliteSystem satelliteSystem;
1200
1201 /** Program name used to antenna center variation corrections. */
1202 private final String progPCVS;
1203
1204 /** Source of corrections (URL). */
1205 private final String sourcePCVS;
1206
1207 /** Simple constructor.
1208 * @param satelliteSystem satellite system
1209 * @param progPCVS Program name used for PCVs
1210 * @param sourcePCVS Source of corrections (URL)
1211 */
1212 private AppliedPCVS(final SatelliteSystem satelliteSystem,
1213 final String progPCVS, final String sourcePCVS) {
1214 this.satelliteSystem = satelliteSystem;
1215 this.progPCVS = progPCVS;
1216 this.sourcePCVS = sourcePCVS;
1217 }
1218
1219 /** Get the satellite system.
1220 * @return satellite system
1221 */
1222 public SatelliteSystem getSatelliteSystem() {
1223 return satelliteSystem;
1224 }
1225
1226 /** Get the program name used to apply PCVs.
1227 * @return Program name used to apply PCVs
1228 */
1229 public String getProgPCVS() {
1230 return progPCVS;
1231 }
1232
1233 /** Get the source of corrections.
1234 * @return Source of corrections (URL)
1235 */
1236 public String getSourcePCVS() {
1237 return sourcePCVS;
1238 }
1239
1240 }
1241 }
1242
1243 }