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