1 /* Copyright 2002-2021 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 inRunBy = false;
274 boolean inMarkerName = false;
275 boolean inObserver = false;
276 boolean inRecType = false;
277 boolean inAntType = false;
278 boolean inAproxPos = false;
279 boolean inAntDelta = false;
280 boolean inTypesObs = false;
281 boolean inFirstObs = false;
282 boolean inPhaseShift = false;
283 boolean inGlonassSlot = false;
284 boolean inGlonassCOD = false;
285 RinexObservationHeader rinexHeader = null;
286 int scaleFactor = 1;
287 int nbObsScaleFactor = 0;
288 final List<ScaleFactorCorrection> scaleFactorCorrections = new ArrayList<>();
289 final Map<SatelliteSystem, List<ObservationType>> listTypeObs = new HashMap<>();
290
291 //First line must always contain Rinex Version, File Type and Satellite Systems Observed
292 readLine(reader, true);
293 if (line.length() < LABEL_START || !RINEX_VERSION_TYPE.equals(line.substring(LABEL_START).trim())) {
294 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
295 }
296 formatVersion = parseDouble(0, 9);
297 final int format100 = (int) FastMath.rint(100 * formatVersion);
298
299 if (format100 != 200 && format100 != 210 && format100 != 211 &&
300 format100 != 212 && format100 != 220 && format100 != 300 &&
301 format100 != 301 && format100 != 302 && format100 != 303 &&
302 format100 != 304) {
303 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
304 }
305
306 //File Type must be Observation_Data
307 if (!(parseString(20, 1)).equals(FILE_TYPE)) {
308 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
309 }
310 satelliteSystem = SatelliteSystem.parseSatelliteSystem(parseString(40, 1));
311 inRinexVersion = true;
312
313 switch (format100 / 100) {
314 case 2: {
315
316 final int MAX_OBS_TYPES_PER_LINE_RNX2 = 9;
317 final int MAX_N_SAT_OBSERVATION = 12;
318 final int MAX_N_TYPES_OBSERVATION = 5;
319 final int MAX_OBS_TYPES_SCALE_FACTOR = 8;
320 final List<ObservationType> typesObs = new ArrayList<>();
321
322 while (readLine(reader, false)) {
323
324 if (rinexHeader == null) {
325 switch(line.substring(LABEL_START).trim()) {
326 case COMMENT :
327 // nothing to do
328 break;
329 case PGM_RUN_BY_DATE :
330 inRunBy = true;
331 break;
332 case MARKER_NAME :
333 markerName = parseString(0, 60);
334 inMarkerName = true;
335 break;
336 case MARKER_NUMBER :
337 markerNumber = parseString(0, 20);
338 break;
339 case MARKER_TYPE :
340 markerType = parseString(0, 20);
341 break;
342 case OBSERVER_AGENCY :
343 observerName = parseString(0, 20);
344 agencyName = parseString(20, 40);
345 inObserver = true;
346 break;
347 case REC_NB_TYPE_VERS :
348 receiverNumber = parseString(0, 20);
349 receiverType = parseString(20, 20);
350 receiverVersion = parseString(40, 20);
351 inRecType = true;
352 break;
353 case ANT_NB_TYPE :
354 antennaNumber = parseString(0, 20);
355 antennaType = parseString(20, 20);
356 inAntType = true;
357 break;
358 case APPROX_POSITION_XYZ :
359 approxPos = new Vector3D(parseDouble(0, 14), parseDouble(14, 14),
360 parseDouble(28, 14));
361 inAproxPos = true;
362 break;
363 case ANTENNA_DELTA_H_E_N :
364 antHeight = parseDouble(0, 14);
365 eccentricities = new Vector2D(parseDouble(14, 14), parseDouble(28, 14));
366 inAntDelta = true;
367 break;
368 case ANTENNA_DELTA_X_Y_Z :
369 antRefPoint = new Vector3D(parseDouble(0, 14),
370 parseDouble(14, 14),
371 parseDouble(28, 14));
372 break;
373 case ANTENNA_B_SIGHT_XYZ :
374 antBSight = new Vector3D(parseDouble(0, 14),
375 parseDouble(14, 14),
376 parseDouble(28, 14));
377 break;
378 case CENTER_OF_MASS_XYZ :
379 centerMass = new Vector3D(parseDouble(0, 14),
380 parseDouble(14, 14),
381 parseDouble(28, 14));
382 break;
383 case NB_OF_SATELLITES :
384 nbSat = parseInt(0, 6);
385 break;
386 case WAVELENGTH_FACT_L1_2 :
387 //Optional line in header
388 //Not stored for now
389 break;
390 case RCV_CLOCK_OFFS_APPL :
391 clkOffset = parseInt(0, 6);
392 break;
393 case INTERVAL :
394 interval = parseDouble(0, 10);
395 break;
396 case TIME_OF_FIRST_OBS :
397 switch (satelliteSystem) {
398 case GPS:
399 timeScale = timeScales.getGPS();
400 break;
401 case GALILEO:
402 timeScale = timeScales.getGST();
403 break;
404 case GLONASS:
405 timeScale = timeScales.getGLONASS();
406 break;
407 case MIXED:
408 //in Case of Mixed data, Timescale must be specified in the Time of First line
409 timeScaleStr = parseString(48, 3);
410
411 if (timeScaleStr.equals(GPS)) {
412 timeScale = timeScales.getGPS();
413 } else if (timeScaleStr.equals(GAL)) {
414 timeScale = timeScales.getGST();
415 } else if (timeScaleStr.equals(GLO)) {
416 timeScale = timeScales.getGLONASS();
417 } else {
418 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
419 }
420 break;
421 default :
422 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
423 lineNumber, name, line);
424 }
425
426 tFirstObs = new AbsoluteDate(parseInt(0, 6),
427 parseInt(6, 6),
428 parseInt(12, 6),
429 parseInt(18, 6),
430 parseInt(24, 6),
431 parseDouble(30, 13), timeScale);
432 inFirstObs = true;
433 break;
434 case TIME_OF_LAST_OBS :
435 tLastObs = new AbsoluteDate(parseInt(0, 6),
436 parseInt(6, 6),
437 parseInt(12, 6),
438 parseInt(18, 6),
439 parseInt(24, 6),
440 parseDouble(30, 13), timeScale);
441 break;
442 case LEAP_SECONDS :
443 leapSeconds = parseInt(0, 6);
444 break;
445 case PRN_NB_OF_OBS :
446 //Optional line in header, indicates number of Observations par Satellite
447 //Not stored for now
448 break;
449 case NB_TYPES_OF_OBSERV :
450 nbTypes = parseInt(0, 6);
451 final int nbLinesTypesObs = (nbTypes + MAX_OBS_TYPES_PER_LINE_RNX2 - 1 ) / MAX_OBS_TYPES_PER_LINE_RNX2;
452
453 for (int j = 0; j < nbLinesTypesObs; j++) {
454 if (j > 0) {
455 readLine(reader, true);
456 }
457 final int iMax = FastMath.min(MAX_OBS_TYPES_PER_LINE_RNX2, nbTypes - typesObs.size());
458 for (int i = 0; i < iMax; i++) {
459 try {
460 typesObs.add(ObservationType.valueOf(parseString(10 + (6 * i), 2)));
461 } catch (IllegalArgumentException iae) {
462 throw new OrekitException(iae, OrekitMessages.UNKNOWN_RINEX_FREQUENCY,
463 parseString(10 + (6 * i), 2), name, lineNumber);
464 }
465 }
466 }
467 inTypesObs = true;
468 break;
469 case OBS_SCALE_FACTOR :
470 scaleFactor = FastMath.max(1, parseInt(0, 6));
471 nbObsScaleFactor = parseInt(6, 6);
472 if (nbObsScaleFactor > MAX_OBS_TYPES_SCALE_FACTOR) {
473 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
474 lineNumber, name, line);
475 }
476 final List<ObservationType> typesObsScaleFactor = new ArrayList<>(nbObsScaleFactor);
477 for (int i = 0; i < nbObsScaleFactor; i++) {
478 typesObsScaleFactor.add(ObservationType.valueOf(parseString(16 + (6 * i), 2)));
479 }
480 scaleFactorCorrections.add(new ScaleFactorCorrection(satelliteSystem,
481 scaleFactor, typesObsScaleFactor));
482 break;
483 case END_OF_HEADER :
484 //We make sure that we have read all the mandatory fields inside the header of the Rinex
485 if (!inRinexVersion || !inRunBy || !inMarkerName ||
486 !inObserver || !inRecType || !inAntType ||
487 formatVersion < 2.20 && !inAproxPos ||
488 formatVersion < 2.20 && !inAntDelta ||
489 !inTypesObs || !inFirstObs) {
490 throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, name);
491 }
492
493 //Header information gathered
494 rinexHeader = new RinexObservationHeader(formatVersion, satelliteSystem,
495 markerName, markerNumber, markerType, observerName,
496 agencyName, receiverNumber, receiverType,
497 receiverVersion, antennaNumber, antennaType,
498 approxPos, antHeight, eccentricities,
499 antRefPoint, antBSight, centerMass, interval,
500 tFirstObs, tLastObs, clkOffset, leapSeconds);
501 break;
502 default :
503 if (rinexHeader == null) {
504 //There must be an error due to an unknown Label inside the Header
505 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
506 lineNumber, name, line);
507 }
508 }
509 } else {
510
511 //Start of a new Observation
512 rcvrClkOffset = 0;
513 nbLinesSat = -1;
514 eventFlag = -1;
515 nbSatObs = -1;
516 satsObsList = null;
517 tObs = null;
518
519 eventFlag = parseInt(28, 1);
520 //If eventFlag>1, we skip the corresponding lines to the next observation
521 if (eventFlag > 1) {
522 if (eventFlag == 6) {
523 nbSatObs = parseInt(29, 3);
524 nbLinesSat = (nbSatObs + 12 - 1) / 12;
525 final int nbLinesObs = (nbTypes + 5 - 1) / 5;
526 final int nbLinesSkip = (nbLinesSat - 1) + nbSatObs * nbLinesObs;
527 for (int i = 0; i < nbLinesSkip; i++) {
528 readLine(reader, true);
529 }
530 } else {
531 final int nbLinesSkip = parseInt(29, 3);
532 for (int i = 0; i < nbLinesSkip; i++) {
533 readLine(reader, true);
534 }
535 }
536 } else {
537
538 int y = parseInt(0, 3);
539 if (79 < y && y <= 99) {
540 y += 1900;
541 } else if (0 <= y && y <= 79) {
542 y += 2000;
543 }
544 tObs = new AbsoluteDate(y,
545 parseInt(3, 3),
546 parseInt(6, 3),
547 parseInt(9, 3),
548 parseInt(12, 3),
549 parseDouble(15, 11), timeScale);
550
551 nbSatObs = parseInt(29, 3);
552 satsObsList = new String[nbSatObs];
553 //If the total number of satellites was indicated in the Header
554 if (nbSat != -1 && nbSatObs > nbSat) {
555 //we check that the number of Sat in the observation is consistent
556 throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS,
557 lineNumber, name, nbSatObs, nbSat);
558 }
559
560 nbLinesSat = (nbSatObs + MAX_N_SAT_OBSERVATION - 1) / MAX_N_SAT_OBSERVATION;
561 for (int j = 0; j < nbLinesSat; j++) {
562 if (j > 0) {
563 readLine(reader, true);
564 }
565 final int iMax = FastMath.min(MAX_N_SAT_OBSERVATION, nbSatObs - j * MAX_N_SAT_OBSERVATION);
566 for (int i = 0; i < iMax; i++) {
567 satsObsList[i + MAX_N_SAT_OBSERVATION * j] = parseString(32 + 3 * i, 3);
568 }
569
570 //Read the Receiver Clock offset, if present
571 rcvrClkOffset = parseDouble(68, 12);
572 if (Double.isNaN(rcvrClkOffset)) {
573 rcvrClkOffset = 0.0;
574 }
575
576 }
577
578 //For each one of the Satellites in this observation
579 final int nbLinesObs = (nbTypes + MAX_N_TYPES_OBSERVATION - 1) / MAX_N_TYPES_OBSERVATION;
580 for (int k = 0; k < nbSatObs; k++) {
581
582
583 //Once the Date and Satellites list is read:
584 // - to read the Data for each satellite
585 // - 5 Observations per line
586 final List<ObservationData> observationData = new ArrayList<>(nbSatObs);
587 for (int j = 0; j < nbLinesObs; j++) {
588 readLine(reader, true);
589 final int iMax = FastMath.min(MAX_N_TYPES_OBSERVATION, nbTypes - observationData.size());
590 for (int i = 0; i < iMax; i++) {
591 final ObservationType type = typesObs.get(observationData.size());
592 double value = parseDouble(16 * i, 14);
593 boolean scaleFactorFound = false;
594 //We look for the lines of ScaledFactorCorrections
595 for (int l = 0; l < scaleFactorCorrections.size() && !scaleFactorFound; ++l) {
596 //We check if the next Observation Type to read needs to be scaled
597 if (scaleFactorCorrections.get(l).getTypesObsScaled().contains(type)) {
598 value /= scaleFactorCorrections.get(l).getCorrection();
599 scaleFactorFound = true;
600 }
601 }
602 observationData.add(new ObservationData(type,
603 value,
604 parseInt(14 + 16 * i, 1),
605 parseInt(15 + 16 * i, 1)));
606 }
607 }
608
609 //We check that the Satellite type is consistent with Satellite System in the top of the file
610 final SatelliteSystem satelliteSystemSat;
611 final int id;
612 if (satsObsList[k].length() < 3) {
613 // missing satellite system, we use the global one
614 satelliteSystemSat = satelliteSystem;
615 id = Integer.parseInt(satsObsList[k]);
616 } else {
617 satelliteSystemSat = SatelliteSystem.parseSatelliteSystem(satsObsList[k]);
618 id = Integer.parseInt(satsObsList[k].substring(1, 3).trim());
619 }
620 if (!satelliteSystem.equals(SatelliteSystem.MIXED)) {
621 if (!satelliteSystemSat.equals(satelliteSystem)) {
622 throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM,
623 lineNumber, name, satelliteSystem, satelliteSystemSat);
624 }
625 }
626
627 final int prnNumber;
628 switch (satelliteSystemSat) {
629 case GPS:
630 case GLONASS:
631 case GALILEO:
632 prnNumber = id;
633 break;
634 case SBAS:
635 prnNumber = id + 100;
636 break;
637 default:
638 // MIXED satellite system is not allowed here
639 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
640 lineNumber, name, line);
641 }
642
643 observationDataSets.add(new ObservationDataSet(rinexHeader, satelliteSystemSat, prnNumber,
644 tObs, rcvrClkOffset, observationData));
645
646 }
647 }
648 }
649 }
650 break;
651 }
652 case 3: {
653
654 final int MAX_OBS_TYPES_PER_LINE_RNX3 = 13;
655 final int MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE = 12;
656 final int MAX_N_SAT_PHSHIFT_PER_LINE = 10;
657
658 final List<ObservationType> typeObs = new ArrayList<>();
659 String sigStrengthUnit = null;
660 int leapSecondsFuture = 0;
661 int leapSecondsWeekNum = 0;
662 int leapSecondsDayNum = 0;
663 final List<AppliedDCBS> listAppliedDCBs = new ArrayList<>();
664 final List<AppliedPCVS> listAppliedPCVS = new ArrayList<>();
665 SatelliteSystem satSystemScaleFactor = null;
666 String[] satsPhaseShift = null;
667 int nbSatPhaseShift = 0;
668 SatelliteSystem satSystemPhaseShift = null;
669 double corrPhaseShift = 0.0;
670 final List<PhaseShiftCorrection> phaseShiftCorrections = new ArrayList<>();
671 ObservationType phaseShiftTypeObs = null;
672
673
674 while (readLine(reader, false)) {
675 if (rinexHeader == null) {
676 switch(line.substring(LABEL_START).trim()) {
677 case COMMENT :
678 // nothing to do
679 break;
680 case PGM_RUN_BY_DATE :
681 inRunBy = true;
682 break;
683 case MARKER_NAME :
684 markerName = parseString(0, 60);
685 inMarkerName = true;
686 break;
687 case MARKER_NUMBER :
688 markerNumber = parseString(0, 20);
689 break;
690 case MARKER_TYPE :
691 markerType = parseString(0, 20);
692 //Could be done with an Enumeration
693 break;
694 case OBSERVER_AGENCY :
695 observerName = parseString(0, 20);
696 agencyName = parseString(20, 40);
697 inObserver = true;
698 break;
699 case REC_NB_TYPE_VERS :
700 receiverNumber = parseString(0, 20);
701 receiverType = parseString(20, 20);
702 receiverVersion = parseString(40, 20);
703 inRecType = true;
704 break;
705 case ANT_NB_TYPE :
706 antennaNumber = parseString(0, 20);
707 antennaType = parseString(20, 20);
708 inAntType = true;
709 break;
710 case APPROX_POSITION_XYZ :
711 approxPos = new Vector3D(parseDouble(0, 14),
712 parseDouble(14, 14),
713 parseDouble(28, 14));
714 inAproxPos = true;
715 break;
716 case ANTENNA_DELTA_H_E_N :
717 antHeight = parseDouble(0, 14);
718 eccentricities = new Vector2D(parseDouble(14, 14),
719 parseDouble(28, 14));
720 inAntDelta = true;
721 break;
722 case ANTENNA_DELTA_X_Y_Z :
723 antRefPoint = new Vector3D(parseDouble(0, 14),
724 parseDouble(14, 14),
725 parseDouble(28, 14));
726 break;
727 case ANTENNA_PHASECENTER :
728 obsCode = parseString(2, 3);
729 antPhaseCenter = new Vector3D(parseDouble(5, 9),
730 parseDouble(14, 14),
731 parseDouble(28, 14));
732 break;
733 case ANTENNA_B_SIGHT_XYZ :
734 antBSight = new Vector3D(parseDouble(0, 14),
735 parseDouble(14, 14),
736 parseDouble(28, 14));
737 break;
738 case ANTENNA_ZERODIR_AZI :
739 antAzi = parseDouble(0, 14);
740 break;
741 case ANTENNA_ZERODIR_XYZ :
742 antZeroDir = new Vector3D(parseDouble(0, 14),
743 parseDouble(14, 14),
744 parseDouble(28, 14));
745 break;
746 case CENTER_OF_MASS_XYZ :
747 centerMass = new Vector3D(parseDouble(0, 14),
748 parseDouble(14, 14),
749 parseDouble(28, 14));
750 break;
751 case NB_OF_SATELLITES :
752 nbSat = parseInt(0, 6);
753 break;
754 case RCV_CLOCK_OFFS_APPL :
755 clkOffset = parseInt(0, 6);
756 break;
757 case INTERVAL :
758 interval = parseDouble(0, 10);
759 break;
760 case TIME_OF_FIRST_OBS :
761 switch(satelliteSystem) {
762 case GPS:
763 timeScale = timeScales.getGPS();
764 break;
765 case GALILEO:
766 timeScale = timeScales.getGST();
767 break;
768 case GLONASS:
769 timeScale = timeScales.getGLONASS();
770 break;
771 case QZSS:
772 timeScale = timeScales.getQZSS();
773 break;
774 case BEIDOU:
775 timeScale = timeScales.getBDT();
776 break;
777 case IRNSS:
778 timeScale = timeScales.getIRNSS();
779 break;
780 case MIXED:
781 //in Case of Mixed data, Timescale must be specified in the Time of First line
782 timeScaleStr = parseString(48, 3);
783
784 if (timeScaleStr.equals(GPS)) {
785 timeScale = timeScales.getGPS();
786 } else if (timeScaleStr.equals(GAL)) {
787 timeScale = timeScales.getGST();
788 } else if (timeScaleStr.equals(GLO)) {
789 timeScale = timeScales.getGLONASS();
790 } else if (timeScaleStr.equals(QZS)) {
791 timeScale = timeScales.getQZSS();
792 } else if (timeScaleStr.equals(BDT)) {
793 timeScale = timeScales.getBDT();
794 } else if (timeScaleStr.equals(IRN)) {
795 timeScale = timeScales.getIRNSS();
796 } else {
797 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
798 }
799 break;
800 default :
801 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
802 lineNumber, name, line);
803 }
804
805 tFirstObs = new AbsoluteDate(parseInt(0, 6),
806 parseInt(6, 6),
807 parseInt(12, 6),
808 parseInt(18, 6),
809 parseInt(24, 6),
810 parseDouble(30, 13), timeScale);
811 inFirstObs = true;
812 break;
813 case TIME_OF_LAST_OBS :
814 tLastObs = new AbsoluteDate(parseInt(0, 6),
815 parseInt(6, 6),
816 parseInt(12, 6),
817 parseInt(18, 6),
818 parseInt(24, 6),
819 parseDouble(30, 13), timeScale);
820 break;
821 case LEAP_SECONDS :
822 leapSeconds = parseInt(0, 6);
823 leapSecondsFuture = parseInt(6, 6);
824 leapSecondsWeekNum = parseInt(12, 6);
825 leapSecondsDayNum = parseInt(18, 6);
826 //Time System Identifier must be added, last A3 String
827 break;
828 case PRN_NB_OF_OBS :
829 //Optional line in header, indicates number of Observations par Satellite
830 //Not stored for now
831 break;
832 case SYS_NB_OBS_TYPES :
833 obsTypesSystem = null;
834 typeObs.clear();
835
836 obsTypesSystem = SatelliteSystem.parseSatelliteSystem(parseString(0, 1));
837 nbTypes = parseInt(3, 3);
838
839 final int nbLinesTypesObs = (nbTypes + MAX_OBS_TYPES_PER_LINE_RNX3 - 1) / MAX_OBS_TYPES_PER_LINE_RNX3;
840 for (int j = 0; j < nbLinesTypesObs; j++) {
841 if (j > 0) {
842 readLine(reader, true);
843 }
844 final int iMax = FastMath.min(MAX_OBS_TYPES_PER_LINE_RNX3, nbTypes - typeObs.size());
845 for (int i = 0; i < iMax; i++) {
846 try {
847 typeObs.add(ObservationType.valueOf(parseString(7 + (4 * i), 3)));
848 } catch (IllegalArgumentException iae) {
849 throw new OrekitException(iae, OrekitMessages.UNKNOWN_RINEX_FREQUENCY,
850 parseString(7 + (4 * i), 3), name, lineNumber);
851 }
852 }
853 }
854 listTypeObs.put(obsTypesSystem, new ArrayList<>(typeObs));
855 inTypesObs = true;
856 break;
857 case SIGNAL_STRENGTH_UNIT :
858 sigStrengthUnit = parseString(0, 20);
859 break;
860 case SYS_DCBS_APPLIED :
861
862 listAppliedDCBs.add(new AppliedDCBS(SatelliteSystem.parseSatelliteSystem(parseString(0, 1)),
863 parseString(2, 17), parseString(20, 40)));
864 break;
865 case SYS_PCVS_APPLIED :
866
867 listAppliedPCVS.add(new AppliedPCVS(SatelliteSystem.parseSatelliteSystem(parseString(0, 1)),
868 parseString(2, 17), parseString(20, 40)));
869 break;
870 case SYS_SCALE_FACTOR :
871 satSystemScaleFactor = null;
872 scaleFactor = 1;
873 nbObsScaleFactor = 0;
874
875 satSystemScaleFactor = SatelliteSystem.parseSatelliteSystem(parseString(0, 1));
876 scaleFactor = parseInt(2, 4);
877 nbObsScaleFactor = parseInt(8, 2);
878 final List<ObservationType> typesObsScaleFactor = new ArrayList<>(nbObsScaleFactor);
879
880 if (nbObsScaleFactor == 0) {
881 typesObsScaleFactor.addAll(listTypeObs.get(satSystemScaleFactor));
882 } else {
883 final int nbLinesTypesObsScaleFactor = (nbObsScaleFactor + MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE - 1) /
884 MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE;
885 for (int j = 0; j < nbLinesTypesObsScaleFactor; j++) {
886 if ( j > 0) {
887 readLine(reader, true);
888 }
889 final int iMax = FastMath.min(MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE, nbObsScaleFactor - typesObsScaleFactor.size());
890 for (int i = 0; i < iMax; i++) {
891 typesObsScaleFactor.add(ObservationType.valueOf(parseString(11 + (4 * i), 3)));
892 }
893 }
894 }
895
896 scaleFactorCorrections.add(new ScaleFactorCorrection(satSystemScaleFactor,
897 scaleFactor, typesObsScaleFactor));
898 break;
899 case SYS_PHASE_SHIFT :
900 case SYS_PHASE_SHIFTS : {
901
902 nbSatPhaseShift = 0;
903 satsPhaseShift = null;
904 corrPhaseShift = 0.0;
905 phaseShiftTypeObs = null;
906 satSystemPhaseShift = null;
907
908 satSystemPhaseShift = SatelliteSystem.parseSatelliteSystem(parseString(0, 1));
909 final String to = parseString(2, 3);
910 phaseShiftTypeObs = to.isEmpty() ?
911 null :
912 ObservationType.valueOf(to.length() < 3 ? "L" + to : to);
913 nbSatPhaseShift = parseInt(16, 2);
914 corrPhaseShift = parseDouble(6, 8);
915
916 if (nbSatPhaseShift == 0) {
917 //If nbSat with Phase Shift is not indicated: all the satellites are affected for this Obs Type
918 } else {
919 satsPhaseShift = new String[nbSatPhaseShift];
920 final int nbLinesSatPhaseShift = (nbSatPhaseShift + MAX_N_SAT_PHSHIFT_PER_LINE - 1) / MAX_N_SAT_PHSHIFT_PER_LINE;
921 for (int j = 0; j < nbLinesSatPhaseShift; j++) {
922 if (j > 0) {
923 readLine(reader, true);
924 }
925 final int iMax = FastMath.min(MAX_N_SAT_PHSHIFT_PER_LINE, nbSatPhaseShift - j * MAX_N_SAT_PHSHIFT_PER_LINE);
926 for (int i = 0; i < iMax; i++) {
927 satsPhaseShift[i + 10 * j] = parseString(19 + 4 * i, 3);
928 }
929 }
930 }
931 phaseShiftCorrections.add(new PhaseShiftCorrection(satSystemPhaseShift,
932 phaseShiftTypeObs,
933 corrPhaseShift,
934 satsPhaseShift));
935 inPhaseShift = true;
936 break;
937 }
938 case GLONASS_SLOT_FRQ_NB :
939 //Not defined yet
940 inGlonassSlot = true;
941 break;
942 case GLONASS_COD_PHS_BIS :
943 //Not defined yet
944 inGlonassCOD = true;
945 break;
946 case END_OF_HEADER :
947 //We make sure that we have read all the mandatory fields inside the header of the Rinex
948 if (!inRinexVersion || !inRunBy || !inMarkerName ||
949 !inObserver || !inRecType || !inAntType ||
950 !inAntDelta || !inTypesObs || !inFirstObs ||
951 formatVersion >= 3.01 && !inPhaseShift ||
952 formatVersion >= 3.03 && (!inGlonassSlot || !inGlonassCOD)) {
953 throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, name);
954 }
955
956 //Header information gathered
957 rinexHeader = new RinexObservationHeader(formatVersion, satelliteSystem,
958 markerName, markerNumber, markerType,
959 observerName, agencyName, receiverNumber,
960 receiverType, receiverVersion, antennaNumber,
961 antennaType, approxPos, antHeight, eccentricities,
962 antRefPoint, obsCode, antPhaseCenter, antBSight,
963 antAzi, antZeroDir, centerMass, sigStrengthUnit,
964 interval, tFirstObs, tLastObs, clkOffset, listAppliedDCBs,
965 listAppliedPCVS, phaseShiftCorrections, leapSeconds,
966 leapSecondsFuture, leapSecondsWeekNum, leapSecondsDayNum);
967 break;
968 default :
969 if (rinexHeader == null) {
970 //There must be an error due to an unknown Label inside the Header
971 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
972 lineNumber, name, line);
973 }
974 }
975 } else {
976 //If End of Header
977
978 //Start of a new Observation
979 rcvrClkOffset = 0;
980 eventFlag = -1;
981 nbSatObs = -1;
982 tObs = null;
983
984 //A line that starts with ">" correspond to a new observation epoch
985 if (parseString(0, 1).equals(">")) {
986
987 eventFlag = parseInt(31, 1);
988 //If eventFlag>1, we skip the corresponding lines to the next observation
989 if (eventFlag != 0) {
990 final int nbLinesSkip = parseInt(32, 3);
991 for (int i = 0; i < nbLinesSkip; i++) {
992 readLine(reader, true);
993 }
994 } else {
995
996 tObs = new AbsoluteDate(parseInt(2, 4),
997 parseInt(6, 3),
998 parseInt(9, 3),
999 parseInt(12, 3),
1000 parseInt(15, 3),
1001 parseDouble(18, 11), timeScale);
1002
1003 nbSatObs = parseInt(32, 3);
1004 //If the total number of satellites was indicated in the Header
1005 if (nbSat != -1 && nbSatObs > nbSat) {
1006 //we check that the number of Sat in the observation is consistent
1007 throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS,
1008 lineNumber, name, nbSatObs, nbSat);
1009 }
1010 //Read the Receiver Clock offset, if present
1011 rcvrClkOffset = parseDouble(41, 15);
1012 if (Double.isNaN(rcvrClkOffset)) {
1013 rcvrClkOffset = 0.0;
1014 }
1015
1016 //For each one of the Satellites in this Observation
1017 for (int i = 0; i < nbSatObs; i++) {
1018
1019 readLine(reader, true);
1020
1021 //We check that the Satellite type is consistent with Satellite System in the top of the file
1022 final SatelliteSystem satelliteSystemSat = SatelliteSystem.parseSatelliteSystem(parseString(0, 1));
1023 if (!satelliteSystem.equals(SatelliteSystem.MIXED)) {
1024 if (!satelliteSystemSat.equals(satelliteSystem)) {
1025 throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM,
1026 lineNumber, name, satelliteSystem, satelliteSystemSat);
1027 }
1028 }
1029
1030 final int prn = parseInt(1, 2);
1031 final int prnNumber;
1032 switch (satelliteSystemSat) {
1033 case GPS:
1034 case GLONASS:
1035 case GALILEO:
1036 case BEIDOU:
1037 case IRNSS:
1038 prnNumber = prn;
1039 break;
1040 case QZSS:
1041 prnNumber = prn + 192;
1042 break;
1043 case SBAS:
1044 prnNumber = prn + 100;
1045 break;
1046 default:
1047 // MIXED satellite system is not allowed here
1048 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
1049 lineNumber, name, line);
1050 }
1051 final List<ObservationData> observationData = new ArrayList<>(nbSatObs);
1052 for (int j = 0; j < listTypeObs.get(satelliteSystemSat).size(); j++) {
1053 final ObservationType rf = listTypeObs.get(satelliteSystemSat).get(j);
1054 boolean scaleFactorFound = false;
1055 //We look for the lines of ScaledFactorCorrections that correspond to this SatSystem
1056 int k = 0;
1057 double value = parseDouble(3 + j * 16, 14);
1058 while (k < scaleFactorCorrections.size() && !scaleFactorFound) {
1059 if (scaleFactorCorrections.get(k).getSatelliteSystem().equals(satelliteSystemSat)) {
1060 //We check if the next Observation Type to read needs to be scaled
1061 if (scaleFactorCorrections.get(k).getTypesObsScaled().contains(rf)) {
1062 value /= scaleFactorCorrections.get(k).getCorrection();
1063 scaleFactorFound = true;
1064 }
1065 }
1066 k++;
1067 }
1068 observationData.add(new ObservationData(rf,
1069 value,
1070 parseInt(17 + j * 16, 1),
1071 parseInt(18 + j * 16, 1)));
1072 }
1073 observationDataSets.add(new ObservationDataSet(rinexHeader, satelliteSystemSat, prnNumber,
1074 tObs, rcvrClkOffset, observationData));
1075
1076 }
1077 }
1078 }
1079 }
1080 }
1081 break;
1082 }
1083 default:
1084 //If RINEX Version is neither 2 nor 3
1085 throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
1086 }
1087 }
1088 }
1089
1090 /** Read a new line.
1091 * @param reader reader from where to read line
1092 * @param complainIfEnd if true an exception should be thrown if end of file is encountered
1093 * @return true if a line has been read
1094 * @exception IOException if a read error occurs
1095 */
1096 private boolean readLine(final BufferedReader reader, final boolean complainIfEnd)
1097 throws IOException {
1098 line = reader.readLine();
1099 if (line == null && complainIfEnd) {
1100 throw new OrekitException(OrekitMessages.UNEXPECTED_END_OF_FILE, name);
1101 }
1102 lineNumber++;
1103 return line != null;
1104 }
1105
1106 /** Extract a string from a line.
1107 * @param start start index of the string
1108 * @param length length of the string
1109 * @return parsed string
1110 */
1111 private String parseString(final int start, final int length) {
1112 if (line.length() > start) {
1113 return line.substring(start, FastMath.min(line.length(), start + length)).trim();
1114 } else {
1115 return null;
1116 }
1117 }
1118
1119 /** Extract an integer from a line.
1120 * @param start start index of the integer
1121 * @param length length of the integer
1122 * @return parsed integer
1123 */
1124 private int parseInt(final int start, final int length) {
1125 if (line.length() > start && !parseString(start, length).isEmpty()) {
1126 return Integer.parseInt(parseString(start, length));
1127 } else {
1128 return 0;
1129 }
1130 }
1131
1132 /** Extract a double from a line.
1133 * @param start start index of the real
1134 * @param length length of the real
1135 * @return parsed real, or {@code Double.NaN} if field was empty
1136 */
1137 private double parseDouble(final int start, final int length) {
1138 if (line.length() > start && !parseString(start, length).isEmpty()) {
1139 return Double.parseDouble(parseString(start, length));
1140 } else {
1141 return Double.NaN;
1142 }
1143 }
1144
1145 /** Phase Shift corrections.
1146 * Contains the phase shift corrections used to
1147 * generate phases consistent with respect to cycle shifts.
1148 */
1149 public class PhaseShiftCorrection {
1150
1151 /** Satellite System. */
1152 private final SatelliteSystem satSystemPhaseShift;
1153 /** Carrier Phase Observation Code (may be null). */
1154 private final ObservationType typeObsPhaseShift;
1155 /** Phase Shift Corrections (cycles). */
1156 private final double phaseShiftCorrection;
1157 /** List of satellites involved. */
1158 private final String[] satsPhaseShift;
1159
1160 /** Simple constructor.
1161 * @param satSystemPhaseShift Satellite System
1162 * @param typeObsPhaseShift Carrier Phase Observation Code (may be null)
1163 * @param phaseShiftCorrection Phase Shift Corrections (cycles)
1164 * @param satsPhaseShift List of satellites involved
1165 */
1166 private PhaseShiftCorrection(final SatelliteSystem satSystemPhaseShift,
1167 final ObservationType typeObsPhaseShift,
1168 final double phaseShiftCorrection, final String[] satsPhaseShift) {
1169 this.satSystemPhaseShift = satSystemPhaseShift;
1170 this.typeObsPhaseShift = typeObsPhaseShift;
1171 this.phaseShiftCorrection = phaseShiftCorrection;
1172 this.satsPhaseShift = satsPhaseShift;
1173 }
1174
1175 /** Get the Satellite System.
1176 * @return Satellite System.
1177 */
1178 public SatelliteSystem getSatelliteSystem() {
1179 return satSystemPhaseShift;
1180 }
1181 /** Get the Carrier Phase Observation Code.
1182 * <p>
1183 * The observation code may be null for the uncorrected reference
1184 * signal group
1185 * </p>
1186 * @return Carrier Phase Observation Code.
1187 */
1188 public ObservationType getTypeObs() {
1189 return typeObsPhaseShift;
1190 }
1191 /** Get the Phase Shift Corrections.
1192 * @return Phase Shift Corrections (cycles)
1193 */
1194 public double getCorrection() {
1195 return phaseShiftCorrection;
1196 }
1197 /** Get the list of satellites involved.
1198 * @return List of satellites involved (if null, all the sats are involved)
1199 */
1200 public String[] getSatsCorrected() {
1201 //If empty, all the satellites of this constellation are affected for this Observation type
1202 return satsPhaseShift == null ? null : satsPhaseShift.clone();
1203 }
1204 }
1205
1206 /** Scale Factor to be applied.
1207 * Contains the scale factors of 10 applied to the data before
1208 * being stored into the RINEX file.
1209 */
1210 public class ScaleFactorCorrection {
1211
1212 /** Satellite System. */
1213 private final SatelliteSystem satSystemScaleFactor;
1214 /** List of Observations types that have been scaled. */
1215 private final List<ObservationType> typesObsScaleFactor;
1216 /** Factor to divide stored observations with before use. */
1217 private final double scaleFactor;
1218
1219 /** Simple constructor.
1220 * @param satSystemScaleFactor Satellite System
1221 * @param scaleFactor Factor to divide stored observations (1,10,100,1000)
1222 * @param typesObsScaleFactor List of Observations types that have been scaled
1223 */
1224 private ScaleFactorCorrection(final SatelliteSystem satSystemScaleFactor,
1225 final double scaleFactor,
1226 final List<ObservationType> typesObsScaleFactor) {
1227 this.satSystemScaleFactor = satSystemScaleFactor;
1228 this.scaleFactor = scaleFactor;
1229 this.typesObsScaleFactor = typesObsScaleFactor;
1230 }
1231 /** Get the Satellite System.
1232 * @return Satellite System
1233 */
1234 public SatelliteSystem getSatelliteSystem() {
1235 return satSystemScaleFactor;
1236 }
1237 /** Get the Scale Factor.
1238 * @return Scale Factor
1239 */
1240 public double getCorrection() {
1241 return scaleFactor;
1242 }
1243 /** Get the list of Observation Types scaled.
1244 * @return List of Observation types scaled
1245 */
1246 public List<ObservationType> getTypesObsScaled() {
1247 return typesObsScaleFactor;
1248 }
1249 }
1250
1251 }
1252
1253 }