RinexObservationLoader.java

  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. import org.hipparchus.exception.DummyLocalizable;
  30. import org.hipparchus.geometry.euclidean.threed.Vector3D;
  31. import org.hipparchus.geometry.euclidean.twod.Vector2D;
  32. import org.hipparchus.util.FastMath;
  33. import org.orekit.annotation.DefaultDataContext;
  34. import org.orekit.data.DataContext;
  35. import org.orekit.data.DataLoader;
  36. import org.orekit.data.DataProvidersManager;
  37. import org.orekit.data.DataSource;
  38. import org.orekit.errors.OrekitException;
  39. import org.orekit.errors.OrekitMessages;
  40. import org.orekit.time.AbsoluteDate;
  41. import org.orekit.time.TimeScale;
  42. import org.orekit.time.TimeScales;

  43. /** Loader for Rinex measurements files.
  44.  * <p>
  45.  * Supported versions are: 2.00, 2.10, 2.11, 2.12 (unofficial), 2.20 (unofficial),
  46.  * 3.00, 3.01, 3.02, 3.03, and 3.04.
  47.  * </p>
  48.  * @see <a href="ftp://igs.org/pub/data/format/rinex2.txt">rinex 2.0</a>
  49.  * @see <a href="ftp://igs.org/pub/data/format/rinex210.txt">rinex 2.10</a>
  50.  * @see <a href="ftp://igs.org/pub/data/format/rinex211.txt">rinex 2.11</a>
  51.  * @see <a href="http://www.aiub.unibe.ch/download/rinex/rinex212.txt">unofficial rinex 2.12</a>
  52.  * @see <a href="http://www.aiub.unibe.ch/download/rinex/rnx_leo.txt">unofficial rinex 2.20</a>
  53.  * @see <a href="ftp://igs.org/pub/data/format/rinex300.pdf">rinex 3.00</a>
  54.  * @see <a href="ftp://igs.org/pub/data/format/rinex301.pdf">rinex 3.01</a>
  55.  * @see <a href="ftp://igs.org/pub/data/format/rinex302.pdf">rinex 3.02</a>
  56.  * @see <a href="ftp://igs.org/pub/data/format/rinex303.pdf">rinex 3.03</a>
  57.  * @see <a href="ftp://igs.org/pub/data/format/rinex304.pdf">rinex 3.04</a>
  58.  * @since 9.2
  59.  */
  60. public class RinexObservationLoader {

  61.     /** Default supported files name pattern for rinex 2 observation files. */
  62.     public static final String DEFAULT_RINEX_2_SUPPORTED_NAMES = "^\\w{4}\\d{3}[0a-x](?:\\d{2})?\\.\\d{2}[oO]$";

  63.     /** Default supported files name pattern for rinex 3 observation files. */
  64.     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$";

  65.     // CHECKSTYLE: stop JavadocVariable check
  66.     private static final String RINEX_VERSION_TYPE   = "RINEX VERSION / TYPE";
  67.     private static final String COMMENT              = "COMMENT";
  68.     private static final String PGM_RUN_BY_DATE      = "PGM / RUN BY / DATE";
  69.     private static final String MARKER_NAME          = "MARKER NAME";
  70.     private static final String MARKER_NUMBER        = "MARKER NUMBER";
  71.     private static final String MARKER_TYPE          = "MARKER TYPE";
  72.     private static final String OBSERVER_AGENCY      = "OBSERVER / AGENCY";
  73.     private static final String REC_NB_TYPE_VERS     = "REC # / TYPE / VERS";
  74.     private static final String ANT_NB_TYPE          = "ANT # / TYPE";
  75.     private static final String APPROX_POSITION_XYZ  = "APPROX POSITION XYZ";
  76.     private static final String ANTENNA_DELTA_H_E_N  = "ANTENNA: DELTA H/E/N";
  77.     private static final String ANTENNA_DELTA_X_Y_Z  = "ANTENNA: DELTA X/Y/Z";
  78.     private static final String ANTENNA_PHASECENTER  = "ANTENNA: PHASECENTER";
  79.     private static final String ANTENNA_B_SIGHT_XYZ  = "ANTENNA: B.SIGHT XYZ";
  80.     private static final String ANTENNA_ZERODIR_AZI  = "ANTENNA: ZERODIR AZI";
  81.     private static final String ANTENNA_ZERODIR_XYZ  = "ANTENNA: ZERODIR XYZ";
  82.     private static final String NB_OF_SATELLITES     = "# OF SATELLITES";
  83.     private static final String WAVELENGTH_FACT_L1_2 = "WAVELENGTH FACT L1/2";
  84.     private static final String RCV_CLOCK_OFFS_APPL  = "RCV CLOCK OFFS APPL";
  85.     private static final String INTERVAL             = "INTERVAL";
  86.     private static final String TIME_OF_FIRST_OBS    = "TIME OF FIRST OBS";
  87.     private static final String TIME_OF_LAST_OBS     = "TIME OF LAST OBS";
  88.     private static final String LEAP_SECONDS         = "LEAP SECONDS";
  89.     private static final String PRN_NB_OF_OBS        = "PRN / # OF OBS";
  90.     private static final String NB_TYPES_OF_OBSERV   = "# / TYPES OF OBSERV";
  91.     private static final String END_OF_HEADER        = "END OF HEADER";
  92.     private static final String CENTER_OF_MASS_XYZ   = "CENTER OF MASS: XYZ";
  93.     private static final String SIGNAL_STRENGTH_UNIT = "SIGNAL STRENGTH UNIT";
  94.     private static final String SYS_NB_OBS_TYPES     = "SYS / # / OBS TYPES";
  95.     private static final String SYS_DCBS_APPLIED     = "SYS / DCBS APPLIED";
  96.     private static final String SYS_PCVS_APPLIED     = "SYS / PCVS APPLIED";
  97.     private static final String SYS_SCALE_FACTOR     = "SYS / SCALE FACTOR";
  98.     private static final String SYS_PHASE_SHIFT      = "SYS / PHASE SHIFT";
  99.     private static final String SYS_PHASE_SHIFTS     = "SYS / PHASE SHIFTS";
  100.     private static final String GLONASS_SLOT_FRQ_NB  = "GLONASS SLOT / FRQ #";
  101.     private static final String GLONASS_COD_PHS_BIS  = "GLONASS COD/PHS/BIS";
  102.     private static final String OBS_SCALE_FACTOR     = "OBS SCALE FACTOR";

  103.     private static final String GPS                  = "GPS";
  104.     private static final String GAL                  = "GAL";
  105.     private static final String GLO                  = "GLO";
  106.     private static final String QZS                  = "QZS";
  107.     private static final String BDT                  = "BDT";
  108.     private static final String IRN                  = "IRN";
  109.     // CHECKSTYLE: resume JavadocVariable check

  110.     /** Rinex Observations. */
  111.     private final List<ObservationDataSet> observationDataSets;

  112.     /** Set of time scales. */
  113.     private final TimeScales timeScales;

  114.     /** Simple constructor.
  115.      * <p>
  116.      * This constructor is used when the rinex files are managed by the
  117.      * global {@link DataContext#getDefault() default data context}.
  118.      * </p>
  119.      * @param supportedNames regular expression for supported files names
  120.      * @see #RinexObservationLoader(String, DataProvidersManager, TimeScales)
  121.      */
  122.     @DefaultDataContext
  123.     public RinexObservationLoader(final String supportedNames) {
  124.         this(supportedNames, DataContext.getDefault().getDataProvidersManager(),
  125.                 DataContext.getDefault().getTimeScales());
  126.     }

  127.     /**
  128.      * Create a RINEX loader/parser with the given source of RINEX auxiliary data files.
  129.      *
  130.      * <p>
  131.      * This constructor is used when the rinex files are managed by the given
  132.      * {@code dataProvidersManager}.
  133.      * </p>
  134.      * @param supportedNames regular expression for supported files names
  135.      * @param dataProvidersManager provides access to auxiliary data.
  136.      * @param timeScales the set of time scales to use when parsing dates.
  137.      * @since 10.1
  138.      */
  139.     public RinexObservationLoader(final String supportedNames,
  140.                        final DataProvidersManager dataProvidersManager,
  141.                        final TimeScales timeScales) {
  142.         observationDataSets = new ArrayList<>();
  143.         this.timeScales = timeScales;
  144.         dataProvidersManager.feed(supportedNames, new Parser());
  145.     }

  146.     /** Simple constructor. This constructor uses the {@link DataContext#getDefault()
  147.      * default data context}.
  148.      *
  149.      * @param source source for the RINEX data
  150.      * @see #RinexObservationLoader(DataSource, TimeScales)
  151.      */
  152.     @DefaultDataContext
  153.     public RinexObservationLoader(final DataSource source) {
  154.         this(source, DataContext.getDefault().getTimeScales());
  155.     }

  156.     /**
  157.      * Loads RINEX from the given input stream using the specified auxiliary data.
  158.      *
  159.      * @param source source for the RINEX data
  160.      * @param timeScales the set of time scales to use when parsing dates.
  161.      * @since 10.1
  162.      */
  163.     public RinexObservationLoader(final DataSource source, final TimeScales timeScales) {
  164.         try {
  165.             this.timeScales = timeScales;
  166.             observationDataSets = new ArrayList<>();
  167.             try (InputStream         is  = source.getOpener().openStreamOnce();
  168.                  BufferedInputStream bis = new BufferedInputStream(is)) {
  169.                 new Parser().loadData(bis, source.getName());
  170.             }
  171.         } catch (IOException ioe) {
  172.             throw new OrekitException(ioe, new DummyLocalizable(ioe.getMessage()));
  173.         }
  174.     }

  175.     /** Get parsed rinex observations data sets.
  176.      * @return unmodifiable view of parsed rinex observations
  177.      * @since 9.3
  178.      */
  179.     public List<ObservationDataSet> getObservationDataSets() {
  180.         return Collections.unmodifiableList(observationDataSets);
  181.     }

  182.     /** Parser for rinex files.
  183.      */
  184.     public class Parser implements DataLoader {

  185.         /** Index of label in data lines. */
  186.         private static final int LABEL_START = 60;

  187.         /** File type accepted (only Observation Data). */
  188.         private static final String FILE_TYPE = "O"; //Only Observation Data files

  189.         /** Name of the file. */
  190.         private String name;

  191.         /** Current line. */
  192.         private String line;

  193.         /** current line number. */
  194.         private int lineNumber;

  195.         /** {@inheritDoc} */
  196.         @Override
  197.         public boolean stillAcceptsData() {
  198.             // we load all rinex files we can find
  199.             return true;
  200.         }

  201.         /** {@inheritDoc} */
  202.         @Override
  203.         public void loadData(final InputStream input, final String fileName)
  204.             throws IOException, OrekitException {

  205.             try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {

  206.                 this.name       = fileName;
  207.                 this.line       = null;
  208.                 this.lineNumber = 0;

  209.                 // placeholders for parsed data
  210.                 SatelliteSystem                  satelliteSystem        = null;
  211.                 double                           formatVersion          = Double.NaN;
  212.                 boolean                          inRinexVersion         = false;
  213.                 SatelliteSystem                  obsTypesSystem         = null;
  214.                 String                           markerName             = null;
  215.                 String                           markerNumber           = null;
  216.                 String                           markerType             = null;
  217.                 String                           observerName           = null;
  218.                 String                           agencyName             = null;
  219.                 String                           receiverNumber         = null;
  220.                 String                           receiverType           = null;
  221.                 String                           receiverVersion        = null;
  222.                 String                           antennaNumber          = null;
  223.                 String                           antennaType            = null;
  224.                 Vector3D                         approxPos              = null;
  225.                 Vector3D                         antRefPoint            = null;
  226.                 String                           obsCode                = null;
  227.                 Vector3D                         antPhaseCenter         = null;
  228.                 Vector3D                         antBSight              = null;
  229.                 double                           antAzi                 = Double.NaN;
  230.                 Vector3D                         antZeroDir             = null;
  231.                 Vector3D                         centerMass             = null;
  232.                 double                           antHeight              = Double.NaN;
  233.                 Vector2D                         eccentricities         = Vector2D.ZERO;
  234.                 int                              clkOffset              = -1;
  235.                 int                              nbTypes                = -1;
  236.                 int                              nbSat                  = -1;
  237.                 double                           interval               = Double.NaN;
  238.                 AbsoluteDate                     tFirstObs              = AbsoluteDate.PAST_INFINITY;
  239.                 AbsoluteDate                     tLastObs               = AbsoluteDate.FUTURE_INFINITY;
  240.                 TimeScale                        timeScale              = null;
  241.                 String                           timeScaleStr           = null;
  242.                 int                              leapSeconds            = 0;
  243.                 AbsoluteDate                     tObs                   = AbsoluteDate.PAST_INFINITY;
  244.                 String[]                         satsObsList            = null;
  245.                 int                              eventFlag              = -1;
  246.                 int                              nbSatObs               = -1;
  247.                 int                              nbLinesSat             = -1;
  248.                 double                           rcvrClkOffset          = 0;
  249.                 boolean                          inMarkerName           = false;
  250.                 boolean                          inObserver             = false;
  251.                 boolean                          inRecType              = false;
  252.                 boolean                          inAntType              = false;
  253.                 boolean                          inAproxPos             = false;
  254.                 boolean                          inAntDelta             = false;
  255.                 boolean                          inTypesObs             = false;
  256.                 boolean                          inFirstObs             = false;
  257.                 boolean                          inPhaseShift           = false;
  258.                 RinexObservationHeader                      rinexHeader            = null;
  259.                 int                               scaleFactor            = 1;
  260.                 int                               nbObsScaleFactor       = 0;
  261.                 final List<ScaleFactorCorrection> scaleFactorCorrections = new ArrayList<>();
  262.                 final Map<SatelliteSystem, List<ObservationType>> listTypeObs = new HashMap<>();

  263.                 //First line must  always contain Rinex Version, File Type and Satellite Systems Observed
  264.                 readLine(reader, true);
  265.                 if (line.length() < LABEL_START || !RINEX_VERSION_TYPE.equals(line.substring(LABEL_START).trim())) {
  266.                     throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
  267.                 }
  268.                 formatVersion = parseDouble(0, 9);
  269.                 final int format100 = (int) FastMath.rint(100 * formatVersion);

  270.                 if (format100 != 200 && format100 != 210 && format100 != 211 &&
  271.                     format100 != 212 && format100 != 220 && format100 != 300 &&
  272.                     format100 != 301 && format100 != 302 && format100 != 303 &&
  273.                     format100 != 304) {
  274.                     throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
  275.                 }

  276.                 //File Type must be Observation_Data
  277.                 if (!(parseString(20, 1)).equals(FILE_TYPE)) {
  278.                     throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
  279.                 }
  280.                 satelliteSystem = SatelliteSystem.parseSatelliteSystem(parseString(40, 1));
  281.                 inRinexVersion = true;

  282.                 switch (format100 / 100) {
  283.                     case 2: {

  284.                         final int                   MAX_OBS_TYPES_PER_LINE_RNX2 = 9;
  285.                         final int                   MAX_N_SAT_OBSERVATION       = 12;
  286.                         final int                   MAX_N_TYPES_OBSERVATION     = 5;
  287.                         final int                   MAX_OBS_TYPES_SCALE_FACTOR  = 8;
  288.                         final List<ObservationType> typesObs = new ArrayList<>();

  289.                         while (readLine(reader, false)) {

  290.                             if (rinexHeader == null) {
  291.                                 switch (line.substring(LABEL_START).trim()) {
  292.                                     case COMMENT :
  293.                                         // nothing to do
  294.                                         break;
  295.                                     case PGM_RUN_BY_DATE :
  296.                                         // nothing to do
  297.                                         break;
  298.                                     case MARKER_NAME :
  299.                                         markerName = parseString(0, 60);
  300.                                         inMarkerName = true;
  301.                                         break;
  302.                                     case MARKER_NUMBER :
  303.                                         markerNumber = parseString(0, 20);
  304.                                         break;
  305.                                     case MARKER_TYPE :
  306.                                         markerType = parseString(0, 20);
  307.                                         break;
  308.                                     case OBSERVER_AGENCY :
  309.                                         observerName = parseString(0, 20);
  310.                                         agencyName   = parseString(20, 40);
  311.                                         inObserver = true;
  312.                                         break;
  313.                                     case REC_NB_TYPE_VERS :
  314.                                         receiverNumber  = parseString(0, 20);
  315.                                         receiverType    = parseString(20, 20);
  316.                                         receiverVersion = parseString(40, 20);
  317.                                         inRecType = true;
  318.                                         break;
  319.                                     case ANT_NB_TYPE :
  320.                                         antennaNumber = parseString(0, 20);
  321.                                         antennaType   = parseString(20, 20);
  322.                                         inAntType = true;
  323.                                         break;
  324.                                     case APPROX_POSITION_XYZ :
  325.                                         approxPos = new Vector3D(parseDouble(0, 14), parseDouble(14, 14),
  326.                                                                  parseDouble(28, 14));
  327.                                         inAproxPos = true;
  328.                                         break;
  329.                                     case ANTENNA_DELTA_H_E_N :
  330.                                         antHeight = parseDouble(0, 14);
  331.                                         eccentricities = new Vector2D(parseDouble(14, 14), parseDouble(28, 14));
  332.                                         inAntDelta = true;
  333.                                         break;
  334.                                     case ANTENNA_DELTA_X_Y_Z :
  335.                                         antRefPoint = new Vector3D(parseDouble(0, 14),
  336.                                                                    parseDouble(14, 14),
  337.                                                                    parseDouble(28, 14));
  338.                                         break;
  339.                                     case ANTENNA_B_SIGHT_XYZ :
  340.                                         antBSight = new Vector3D(parseDouble(0, 14),
  341.                                                                  parseDouble(14, 14),
  342.                                                                  parseDouble(28, 14));
  343.                                         break;
  344.                                     case CENTER_OF_MASS_XYZ :
  345.                                         centerMass = new Vector3D(parseDouble(0, 14),
  346.                                                                   parseDouble(14, 14),
  347.                                                                   parseDouble(28, 14));
  348.                                         break;
  349.                                     case NB_OF_SATELLITES :
  350.                                         nbSat = parseInt(0, 6);
  351.                                         break;
  352.                                     case WAVELENGTH_FACT_L1_2 :
  353.                                         //Optional line in header
  354.                                         //Not stored for now
  355.                                         break;
  356.                                     case RCV_CLOCK_OFFS_APPL :
  357.                                         clkOffset = parseInt(0, 6);
  358.                                         break;
  359.                                     case INTERVAL :
  360.                                         interval = parseDouble(0, 10);
  361.                                         break;
  362.                                     case TIME_OF_FIRST_OBS :
  363.                                         switch (satelliteSystem) {
  364.                                             case GPS:
  365.                                                 timeScale = timeScales.getGPS();
  366.                                                 break;
  367.                                             case GALILEO:
  368.                                                 timeScale = timeScales.getGST();
  369.                                                 break;
  370.                                             case GLONASS:
  371.                                                 timeScale = timeScales.getGLONASS();
  372.                                                 break;
  373.                                             case MIXED:
  374.                                                 //in Case of Mixed data, Timescale must be specified in the Time of First line
  375.                                                 timeScaleStr = parseString(48, 3);

  376.                                                 if (timeScaleStr.equals(GPS)) {
  377.                                                     timeScale = timeScales.getGPS();
  378.                                                 } else if (timeScaleStr.equals(GAL)) {
  379.                                                     timeScale = timeScales.getGST();
  380.                                                 } else if (timeScaleStr.equals(GLO)) {
  381.                                                     timeScale = timeScales.getGLONASS();
  382.                                                 } else {
  383.                                                     throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
  384.                                                 }
  385.                                                 break;
  386.                                             default :
  387.                                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  388.                                                                           lineNumber, name, line);
  389.                                         }

  390.                                         tFirstObs = new AbsoluteDate(parseInt(0, 6),
  391.                                                                      parseInt(6, 6),
  392.                                                                      parseInt(12, 6),
  393.                                                                      parseInt(18, 6),
  394.                                                                      parseInt(24, 6),
  395.                                                                      parseDouble(30, 13), timeScale);
  396.                                         inFirstObs = true;
  397.                                         break;
  398.                                     case TIME_OF_LAST_OBS :
  399.                                         tLastObs = new AbsoluteDate(parseInt(0, 6),
  400.                                                                     parseInt(6, 6),
  401.                                                                     parseInt(12, 6),
  402.                                                                     parseInt(18, 6),
  403.                                                                     parseInt(24, 6),
  404.                                                                     parseDouble(30, 13), timeScale);
  405.                                         break;
  406.                                     case LEAP_SECONDS :
  407.                                         leapSeconds = parseInt(0, 6);
  408.                                         break;
  409.                                     case PRN_NB_OF_OBS :
  410.                                         //Optional line in header, indicates number of Observations par Satellite
  411.                                         //Not stored for now
  412.                                         break;
  413.                                     case NB_TYPES_OF_OBSERV :
  414.                                         nbTypes = parseInt(0, 6);
  415.                                         final int nbLinesTypesObs = (nbTypes + MAX_OBS_TYPES_PER_LINE_RNX2 - 1 ) / MAX_OBS_TYPES_PER_LINE_RNX2;

  416.                                         for (int j = 0; j < nbLinesTypesObs; j++) {
  417.                                             if (j > 0) {
  418.                                                 readLine(reader, true);
  419.                                             }
  420.                                             final int iMax = FastMath.min(MAX_OBS_TYPES_PER_LINE_RNX2, nbTypes - typesObs.size());
  421.                                             for (int i = 0; i < iMax; i++) {
  422.                                                 try {
  423.                                                     typesObs.add(ObservationType.valueOf(parseString(10 + (6 * i), 2)));
  424.                                                 } catch (IllegalArgumentException iae) {
  425.                                                     throw new OrekitException(iae, OrekitMessages.UNKNOWN_RINEX_FREQUENCY,
  426.                                                                               parseString(10 + (6 * i), 2), name, lineNumber);
  427.                                                 }
  428.                                             }
  429.                                         }
  430.                                         inTypesObs = true;
  431.                                         break;
  432.                                     case OBS_SCALE_FACTOR :
  433.                                         scaleFactor      = FastMath.max(1, parseInt(0,  6));
  434.                                         nbObsScaleFactor = parseInt(6, 6);
  435.                                         if (nbObsScaleFactor > MAX_OBS_TYPES_SCALE_FACTOR) {
  436.                                             throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  437.                                                                       lineNumber, name, line);
  438.                                         }
  439.                                         final List<ObservationType> typesObsScaleFactor = new ArrayList<>(nbObsScaleFactor);
  440.                                         for (int i = 0; i < nbObsScaleFactor; i++) {
  441.                                             typesObsScaleFactor.add(ObservationType.valueOf(parseString(16 + (6 * i), 2)));
  442.                                         }
  443.                                         scaleFactorCorrections.add(new ScaleFactorCorrection(satelliteSystem,
  444.                                                                                              scaleFactor, typesObsScaleFactor));
  445.                                         break;
  446.                                     case END_OF_HEADER :
  447.                                         //We make sure that we have read all the mandatory fields inside the header of the Rinex
  448.                                         if (!inRinexVersion || !inMarkerName ||
  449.                                             !inObserver || !inRecType || !inAntType ||
  450.                                             formatVersion < 2.20 && !inAproxPos ||
  451.                                             formatVersion < 2.20 && !inAntDelta ||
  452.                                             !inTypesObs || !inFirstObs) {
  453.                                             throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, name);
  454.                                         }

  455.                                         //Header information gathered
  456.                                         rinexHeader = new RinexObservationHeader(formatVersion, satelliteSystem,
  457.                                                                       markerName, markerNumber, markerType, observerName,
  458.                                                                       agencyName, receiverNumber, receiverType,
  459.                                                                       receiverVersion, antennaNumber, antennaType,
  460.                                                                       approxPos, antHeight, eccentricities,
  461.                                                                       antRefPoint, antBSight, centerMass, interval,
  462.                                                                       tFirstObs, tLastObs, clkOffset, leapSeconds);
  463.                                         break;
  464.                                     default :
  465.                                         if (rinexHeader == null) {
  466.                                             //There must be an error due to an unknown Label inside the Header
  467.                                             throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  468.                                                                       lineNumber, name, line);
  469.                                         }
  470.                                 }
  471.                             } else {

  472.                                 //Start of a new Observation
  473.                                 rcvrClkOffset     =  0;
  474.                                 nbLinesSat        = -1;
  475.                                 eventFlag         = -1;
  476.                                 nbSatObs          = -1;
  477.                                 satsObsList       = null;
  478.                                 tObs              = null;

  479.                                 eventFlag = parseInt(28, 1);
  480.                                 //If eventFlag>1, we skip the corresponding lines to the next observation
  481.                                 if (eventFlag > 1) {
  482.                                     if (eventFlag == 6) {
  483.                                         nbSatObs  = parseInt(29, 3);
  484.                                         nbLinesSat = (nbSatObs + 12 - 1) / 12;
  485.                                         final int nbLinesObs = (nbTypes + 5 - 1) / 5;
  486.                                         final int nbLinesSkip = (nbLinesSat - 1) + nbSatObs * nbLinesObs;
  487.                                         for (int i = 0; i < nbLinesSkip; i++) {
  488.                                             readLine(reader, true);
  489.                                         }
  490.                                     } else {
  491.                                         final int nbLinesSkip = parseInt(29, 3);
  492.                                         for (int i = 0; i < nbLinesSkip; i++) {
  493.                                             readLine(reader, true);
  494.                                         }
  495.                                     }
  496.                                 } else {

  497.                                     int y = parseInt(0, 3);
  498.                                     if (79 < y && y <= 99) {
  499.                                         y += 1900;
  500.                                     } else if (0 <= y && y <= 79) {
  501.                                         y += 2000;
  502.                                     }
  503.                                     tObs = new AbsoluteDate(y,
  504.                                                             parseInt(3, 3),
  505.                                                             parseInt(6, 3),
  506.                                                             parseInt(9, 3),
  507.                                                             parseInt(12, 3),
  508.                                                             parseDouble(15, 11), timeScale);

  509.                                     nbSatObs  = parseInt(29, 3);
  510.                                     satsObsList   = new String[nbSatObs];
  511.                                     //If the total number of satellites was indicated in the Header
  512.                                     if (nbSat != -1 && nbSatObs > nbSat) {
  513.                                         //we check that the number of Sat in the observation is consistent
  514.                                         throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS,
  515.                                                                   lineNumber, name, nbSatObs, nbSat);
  516.                                     }

  517.                                     nbLinesSat = (nbSatObs + MAX_N_SAT_OBSERVATION - 1) / MAX_N_SAT_OBSERVATION;
  518.                                     for (int j = 0; j < nbLinesSat; j++) {
  519.                                         if (j > 0) {
  520.                                             readLine(reader, true);
  521.                                         }
  522.                                         final int iMax = FastMath.min(MAX_N_SAT_OBSERVATION, nbSatObs  - j * MAX_N_SAT_OBSERVATION);
  523.                                         for (int i = 0; i < iMax; i++) {
  524.                                             satsObsList[i + MAX_N_SAT_OBSERVATION * j] = parseString(32 + 3 * i, 3);
  525.                                         }

  526.                                         //Read the Receiver Clock offset, if present
  527.                                         rcvrClkOffset = parseDouble(68, 12);
  528.                                         if (Double.isNaN(rcvrClkOffset)) {
  529.                                             rcvrClkOffset = 0.0;
  530.                                         }

  531.                                     }

  532.                                     //For each one of the Satellites in this observation
  533.                                     final int nbLinesObs = (nbTypes + MAX_N_TYPES_OBSERVATION - 1) / MAX_N_TYPES_OBSERVATION;
  534.                                     for (int k = 0; k < nbSatObs; k++) {


  535.                                         //Once the Date and Satellites list is read:
  536.                                         //  - to read the Data for each satellite
  537.                                         //  - 5 Observations per line
  538.                                         final List<ObservationData> observationData = new ArrayList<>(nbSatObs);
  539.                                         for (int j = 0; j < nbLinesObs; j++) {
  540.                                             readLine(reader, true);
  541.                                             final int iMax = FastMath.min(MAX_N_TYPES_OBSERVATION, nbTypes - observationData.size());
  542.                                             for (int i = 0; i < iMax; i++) {
  543.                                                 final ObservationType type = typesObs.get(observationData.size());
  544.                                                 double value = parseDouble(16 * i, 14);
  545.                                                 boolean scaleFactorFound = false;
  546.                                                 //We look for the lines of ScaledFactorCorrections
  547.                                                 for (int l = 0; l < scaleFactorCorrections.size() && !scaleFactorFound; ++l) {
  548.                                                     //We check if the next Observation Type to read needs to be scaled
  549.                                                     if (scaleFactorCorrections.get(l).getTypesObsScaled().contains(type)) {
  550.                                                         value /= scaleFactorCorrections.get(l).getCorrection();
  551.                                                         scaleFactorFound = true;
  552.                                                     }
  553.                                                 }
  554.                                                 observationData.add(new ObservationData(type,
  555.                                                                                         value,
  556.                                                                                         parseInt(14 + 16 * i, 1),
  557.                                                                                         parseInt(15 + 16 * i, 1)));
  558.                                             }
  559.                                         }

  560.                                         //We check that the Satellite type is consistent with Satellite System in the top of the file
  561.                                         final SatelliteSystem satelliteSystemSat;
  562.                                         final int id;
  563.                                         if (satsObsList[k].length() < 3) {
  564.                                             // missing satellite system, we use the global one
  565.                                             satelliteSystemSat = satelliteSystem;
  566.                                             id                 = Integer.parseInt(satsObsList[k]);
  567.                                         } else {
  568.                                             satelliteSystemSat = SatelliteSystem.parseSatelliteSystem(satsObsList[k]);
  569.                                             id                 = Integer.parseInt(satsObsList[k].substring(1, 3).trim());
  570.                                         }
  571.                                         if (!satelliteSystem.equals(SatelliteSystem.MIXED)) {
  572.                                             if (!satelliteSystemSat.equals(satelliteSystem)) {
  573.                                                 throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM,
  574.                                                                           lineNumber, name, satelliteSystem, satelliteSystemSat);
  575.                                             }
  576.                                         }

  577.                                         final int prnNumber;
  578.                                         switch (satelliteSystemSat) {
  579.                                             case GPS:
  580.                                             case GLONASS:
  581.                                             case GALILEO:
  582.                                                 prnNumber = id;
  583.                                                 break;
  584.                                             case SBAS:
  585.                                                 prnNumber = id + 100;
  586.                                                 break;
  587.                                             default:
  588.                                                 // MIXED satellite system is not allowed here
  589.                                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  590.                                                                           lineNumber, name, line);
  591.                                         }

  592.                                         observationDataSets.add(new ObservationDataSet(rinexHeader, satelliteSystemSat, prnNumber,
  593.                                                                                        tObs, rcvrClkOffset, observationData));

  594.                                     }
  595.                                 }
  596.                             }
  597.                         }
  598.                         break;
  599.                     }
  600.                     case 3: {

  601.                         final int                   MAX_OBS_TYPES_PER_LINE_RNX3 = 13;
  602.                         final int           MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE = 12;
  603.                         final int                    MAX_N_SAT_PHSHIFT_PER_LINE = 10;

  604.                         final List<ObservationType>                       typeObs                = new ArrayList<>();
  605.                         String                                            sigStrengthUnit        = null;
  606.                         int                                               leapSecondsFuture      = 0;
  607.                         int                                               leapSecondsWeekNum     = 0;
  608.                         int                                               leapSecondsDayNum      = 0;
  609.                         final List<AppliedDCBS>                           listAppliedDCBs        = new ArrayList<>();
  610.                         final List<AppliedPCVS>                           listAppliedPCVS        = new ArrayList<>();
  611.                         SatelliteSystem                                   satSystemScaleFactor   = null;
  612.                         String[]                                          satsPhaseShift         = null;
  613.                         int                                               nbSatPhaseShift        = 0;
  614.                         SatelliteSystem                                   satSystemPhaseShift    = null;
  615.                         double                                            corrPhaseShift         = 0.0;
  616.                         final List<PhaseShiftCorrection>                  phaseShiftCorrections  = new ArrayList<>();
  617.                         ObservationType                                   phaseShiftTypeObs      = null;


  618.                         while (readLine(reader, false)) {
  619.                             if (rinexHeader == null) {
  620.                                 switch (line.substring(LABEL_START).trim()) {
  621.                                     case COMMENT :
  622.                                         // nothing to do
  623.                                         break;
  624.                                     case PGM_RUN_BY_DATE :
  625.                                         // nothing to do
  626.                                         break;
  627.                                     case MARKER_NAME :
  628.                                         markerName = parseString(0, 60);
  629.                                         inMarkerName = true;
  630.                                         break;
  631.                                     case MARKER_NUMBER :
  632.                                         markerNumber = parseString(0, 20);
  633.                                         break;
  634.                                     case MARKER_TYPE :
  635.                                         markerType = parseString(0, 20);
  636.                                         //Could be done with an Enumeration
  637.                                         break;
  638.                                     case OBSERVER_AGENCY :
  639.                                         observerName = parseString(0, 20);
  640.                                         agencyName   = parseString(20, 40);
  641.                                         inObserver = true;
  642.                                         break;
  643.                                     case REC_NB_TYPE_VERS :
  644.                                         receiverNumber  = parseString(0, 20);
  645.                                         receiverType    = parseString(20, 20);
  646.                                         receiverVersion = parseString(40, 20);
  647.                                         inRecType = true;
  648.                                         break;
  649.                                     case ANT_NB_TYPE :
  650.                                         antennaNumber = parseString(0, 20);
  651.                                         antennaType   = parseString(20, 20);
  652.                                         inAntType = true;
  653.                                         break;
  654.                                     case APPROX_POSITION_XYZ :
  655.                                         approxPos = new Vector3D(parseDouble(0, 14),
  656.                                                                  parseDouble(14, 14),
  657.                                                                  parseDouble(28, 14));
  658.                                         inAproxPos = true;
  659.                                         break;
  660.                                     case ANTENNA_DELTA_H_E_N :
  661.                                         antHeight = parseDouble(0, 14);
  662.                                         eccentricities = new Vector2D(parseDouble(14, 14),
  663.                                                                       parseDouble(28, 14));
  664.                                         inAntDelta = true;
  665.                                         break;
  666.                                     case ANTENNA_DELTA_X_Y_Z :
  667.                                         antRefPoint = new Vector3D(parseDouble(0, 14),
  668.                                                                    parseDouble(14, 14),
  669.                                                                    parseDouble(28, 14));
  670.                                         break;
  671.                                     case ANTENNA_PHASECENTER :
  672.                                         obsCode = parseString(2, 3);
  673.                                         antPhaseCenter = new Vector3D(parseDouble(5, 9),
  674.                                                                       parseDouble(14, 14),
  675.                                                                       parseDouble(28, 14));
  676.                                         break;
  677.                                     case ANTENNA_B_SIGHT_XYZ :
  678.                                         antBSight = new Vector3D(parseDouble(0, 14),
  679.                                                                  parseDouble(14, 14),
  680.                                                                  parseDouble(28, 14));
  681.                                         break;
  682.                                     case ANTENNA_ZERODIR_AZI :
  683.                                         antAzi = parseDouble(0, 14);
  684.                                         break;
  685.                                     case ANTENNA_ZERODIR_XYZ :
  686.                                         antZeroDir = new Vector3D(parseDouble(0, 14),
  687.                                                                   parseDouble(14, 14),
  688.                                                                   parseDouble(28, 14));
  689.                                         break;
  690.                                     case CENTER_OF_MASS_XYZ :
  691.                                         centerMass = new Vector3D(parseDouble(0, 14),
  692.                                                                   parseDouble(14, 14),
  693.                                                                   parseDouble(28, 14));
  694.                                         break;
  695.                                     case NB_OF_SATELLITES :
  696.                                         nbSat = parseInt(0, 6);
  697.                                         break;
  698.                                     case RCV_CLOCK_OFFS_APPL :
  699.                                         clkOffset = parseInt(0, 6);
  700.                                         break;
  701.                                     case INTERVAL :
  702.                                         interval = parseDouble(0, 10);
  703.                                         break;
  704.                                     case TIME_OF_FIRST_OBS :
  705.                                         switch (satelliteSystem) {
  706.                                             case GPS:
  707.                                                 timeScale = timeScales.getGPS();
  708.                                                 break;
  709.                                             case GALILEO:
  710.                                                 timeScale = timeScales.getGST();
  711.                                                 break;
  712.                                             case GLONASS:
  713.                                                 timeScale = timeScales.getGLONASS();
  714.                                                 break;
  715.                                             case QZSS:
  716.                                                 timeScale = timeScales.getQZSS();
  717.                                                 break;
  718.                                             case BEIDOU:
  719.                                                 timeScale = timeScales.getBDT();
  720.                                                 break;
  721.                                             case IRNSS:
  722.                                                 timeScale = timeScales.getIRNSS();
  723.                                                 break;
  724.                                             case MIXED:
  725.                                                 //in Case of Mixed data, Timescale must be specified in the Time of First line
  726.                                                 timeScaleStr = parseString(48, 3);

  727.                                                 if (timeScaleStr.equals(GPS)) {
  728.                                                     timeScale = timeScales.getGPS();
  729.                                                 } else if (timeScaleStr.equals(GAL)) {
  730.                                                     timeScale = timeScales.getGST();
  731.                                                 } else if (timeScaleStr.equals(GLO)) {
  732.                                                     timeScale = timeScales.getGLONASS();
  733.                                                 } else if (timeScaleStr.equals(QZS)) {
  734.                                                     timeScale = timeScales.getQZSS();
  735.                                                 } else if (timeScaleStr.equals(BDT)) {
  736.                                                     timeScale = timeScales.getBDT();
  737.                                                 } else if (timeScaleStr.equals(IRN)) {
  738.                                                     timeScale = timeScales.getIRNSS();
  739.                                                 } else {
  740.                                                     throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
  741.                                                 }
  742.                                                 break;
  743.                                             default :
  744.                                                 throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  745.                                                                           lineNumber, name, line);
  746.                                         }

  747.                                         tFirstObs = new AbsoluteDate(parseInt(0, 6),
  748.                                                                      parseInt(6, 6),
  749.                                                                      parseInt(12, 6),
  750.                                                                      parseInt(18, 6),
  751.                                                                      parseInt(24, 6),
  752.                                                                      parseDouble(30, 13), timeScale);
  753.                                         inFirstObs = true;
  754.                                         break;
  755.                                     case TIME_OF_LAST_OBS :
  756.                                         tLastObs = new AbsoluteDate(parseInt(0, 6),
  757.                                                                     parseInt(6, 6),
  758.                                                                     parseInt(12, 6),
  759.                                                                     parseInt(18, 6),
  760.                                                                     parseInt(24, 6),
  761.                                                                     parseDouble(30, 13), timeScale);
  762.                                         break;
  763.                                     case LEAP_SECONDS :
  764.                                         leapSeconds = parseInt(0, 6);
  765.                                         leapSecondsFuture = parseInt(6, 6);
  766.                                         leapSecondsWeekNum = parseInt(12, 6);
  767.                                         leapSecondsDayNum = parseInt(18, 6);
  768.                                         //Time System Identifier must be added, last A3 String
  769.                                         break;
  770.                                     case PRN_NB_OF_OBS :
  771.                                         //Optional line in header, indicates number of Observations par Satellite
  772.                                         //Not stored for now
  773.                                         break;
  774.                                     case SYS_NB_OBS_TYPES :
  775.                                         obsTypesSystem = null;
  776.                                         typeObs.clear();

  777.                                         obsTypesSystem = SatelliteSystem.parseSatelliteSystem(parseString(0, 1));
  778.                                         nbTypes = parseInt(3, 3);

  779.                                         final int nbLinesTypesObs = (nbTypes + MAX_OBS_TYPES_PER_LINE_RNX3 - 1) / MAX_OBS_TYPES_PER_LINE_RNX3;
  780.                                         for (int j = 0; j < nbLinesTypesObs; j++) {
  781.                                             if (j > 0) {
  782.                                                 readLine(reader, true);
  783.                                             }
  784.                                             final int iMax = FastMath.min(MAX_OBS_TYPES_PER_LINE_RNX3, nbTypes - typeObs.size());
  785.                                             for (int i = 0; i < iMax; i++) {
  786.                                                 try {
  787.                                                     typeObs.add(ObservationType.valueOf(parseString(7 + (4 * i), 3)));
  788.                                                 } catch (IllegalArgumentException iae) {
  789.                                                     throw new OrekitException(iae, OrekitMessages.UNKNOWN_RINEX_FREQUENCY,
  790.                                                                               parseString(7 + (4 * i), 3), name, lineNumber);
  791.                                                 }
  792.                                             }
  793.                                         }
  794.                                         listTypeObs.put(obsTypesSystem, new ArrayList<>(typeObs));
  795.                                         inTypesObs = true;
  796.                                         break;
  797.                                     case SIGNAL_STRENGTH_UNIT :
  798.                                         sigStrengthUnit = parseString(0, 20);
  799.                                         break;
  800.                                     case SYS_DCBS_APPLIED :

  801.                                         listAppliedDCBs.add(new AppliedDCBS(SatelliteSystem.parseSatelliteSystem(parseString(0, 1)),
  802.                                                                             parseString(2, 17), parseString(20, 40)));
  803.                                         break;
  804.                                     case SYS_PCVS_APPLIED :

  805.                                         listAppliedPCVS.add(new AppliedPCVS(SatelliteSystem.parseSatelliteSystem(parseString(0, 1)),
  806.                                                                             parseString(2, 17), parseString(20, 40)));
  807.                                         break;
  808.                                     case SYS_SCALE_FACTOR :
  809.                                         satSystemScaleFactor  = null;
  810.                                         scaleFactor           = 1;
  811.                                         nbObsScaleFactor      = 0;

  812.                                         satSystemScaleFactor = SatelliteSystem.parseSatelliteSystem(parseString(0, 1));
  813.                                         scaleFactor          = parseInt(2, 4);
  814.                                         nbObsScaleFactor     = parseInt(8, 2);
  815.                                         final List<ObservationType> typesObsScaleFactor = new ArrayList<>(nbObsScaleFactor);

  816.                                         if (nbObsScaleFactor == 0) {
  817.                                             typesObsScaleFactor.addAll(listTypeObs.get(satSystemScaleFactor));
  818.                                         } else {
  819.                                             final int nbLinesTypesObsScaleFactor = (nbObsScaleFactor + MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE - 1) /
  820.                                                                                    MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE;
  821.                                             for (int j = 0; j < nbLinesTypesObsScaleFactor; j++) {
  822.                                                 if ( j > 0) {
  823.                                                     readLine(reader, true);
  824.                                                 }
  825.                                                 final int iMax = FastMath.min(MAX_OBS_TYPES_SCALE_FACTOR_PER_LINE, nbObsScaleFactor - typesObsScaleFactor.size());
  826.                                                 for (int i = 0; i < iMax; i++) {
  827.                                                     typesObsScaleFactor.add(ObservationType.valueOf(parseString(11 + (4 * i), 3)));
  828.                                                 }
  829.                                             }
  830.                                         }

  831.                                         scaleFactorCorrections.add(new ScaleFactorCorrection(satSystemScaleFactor,
  832.                                                                                              scaleFactor, typesObsScaleFactor));
  833.                                         break;
  834.                                     case SYS_PHASE_SHIFT  :
  835.                                     case SYS_PHASE_SHIFTS : {

  836.                                         nbSatPhaseShift     = 0;
  837.                                         satsPhaseShift      = null;
  838.                                         corrPhaseShift      = 0.0;
  839.                                         phaseShiftTypeObs   = null;
  840.                                         satSystemPhaseShift = null;

  841.                                         satSystemPhaseShift = SatelliteSystem.parseSatelliteSystem(parseString(0, 1));
  842.                                         final String to = parseString(2, 3);
  843.                                         phaseShiftTypeObs = to.isEmpty() ?
  844.                                                             null :
  845.                                                             ObservationType.valueOf(to.length() < 3 ? "L" + to : to);
  846.                                         nbSatPhaseShift = parseInt(16, 2);
  847.                                         corrPhaseShift = parseDouble(6, 8);

  848.                                         if (nbSatPhaseShift == 0) {
  849.                                             //If nbSat with Phase Shift is not indicated: all the satellites are affected for this Obs Type
  850.                                         } else {
  851.                                             satsPhaseShift = new String[nbSatPhaseShift];
  852.                                             final int nbLinesSatPhaseShift = (nbSatPhaseShift + MAX_N_SAT_PHSHIFT_PER_LINE - 1) / MAX_N_SAT_PHSHIFT_PER_LINE;
  853.                                             for (int j = 0; j < nbLinesSatPhaseShift; j++) {
  854.                                                 if (j > 0) {
  855.                                                     readLine(reader, true);
  856.                                                 }
  857.                                                 final int iMax = FastMath.min(MAX_N_SAT_PHSHIFT_PER_LINE, nbSatPhaseShift - j * MAX_N_SAT_PHSHIFT_PER_LINE);
  858.                                                 for (int i = 0; i < iMax; i++) {
  859.                                                     satsPhaseShift[i + 10 * j] = parseString(19 + 4 * i, 3);
  860.                                                 }
  861.                                             }
  862.                                         }
  863.                                         phaseShiftCorrections.add(new PhaseShiftCorrection(satSystemPhaseShift,
  864.                                                                                            phaseShiftTypeObs,
  865.                                                                                            corrPhaseShift,
  866.                                                                                            satsPhaseShift));
  867.                                         inPhaseShift = true;
  868.                                         break;
  869.                                     }
  870.                                     case GLONASS_SLOT_FRQ_NB :
  871.                                         // Not defined yet
  872.                                         break;
  873.                                     case GLONASS_COD_PHS_BIS :
  874.                                         // Not defined yet
  875.                                         break;
  876.                                     case END_OF_HEADER :
  877.                                         //We make sure that we have read all the mandatory fields inside the header of the Rinex
  878.                                         if (!inRinexVersion || !inMarkerName ||
  879.                                             !inObserver || !inRecType || !inAntType ||
  880.                                             !inAntDelta || !inTypesObs || !inFirstObs ||
  881.                                             formatVersion >= 3.01 && !inPhaseShift) {
  882.                                             throw new OrekitException(OrekitMessages.INCOMPLETE_HEADER, name);
  883.                                         }

  884.                                         //Header information gathered
  885.                                         rinexHeader = new RinexObservationHeader(formatVersion, satelliteSystem,
  886.                                                                       markerName, markerNumber, markerType,
  887.                                                                       observerName, agencyName, receiverNumber,
  888.                                                                       receiverType, receiverVersion, antennaNumber,
  889.                                                                       antennaType, approxPos, antHeight, eccentricities,
  890.                                                                       antRefPoint, obsCode, antPhaseCenter, antBSight,
  891.                                                                       antAzi, antZeroDir, centerMass, sigStrengthUnit,
  892.                                                                       interval, tFirstObs, tLastObs, clkOffset, listAppliedDCBs,
  893.                                                                       listAppliedPCVS, phaseShiftCorrections, leapSeconds,
  894.                                                                       leapSecondsFuture, leapSecondsWeekNum, leapSecondsDayNum);
  895.                                         break;
  896.                                     default :
  897.                                         if (rinexHeader == null) {
  898.                                             //There must be an error due to an unknown Label inside the Header
  899.                                             throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  900.                                                                       lineNumber, name, line);
  901.                                         }
  902.                                 }
  903.                             } else {
  904.                                 //If End of Header

  905.                                 //Start of a new Observation
  906.                                 rcvrClkOffset     =  0;
  907.                                 eventFlag         = -1;
  908.                                 nbSatObs          = -1;
  909.                                 tObs              = null;

  910.                                 //A line that starts with ">" correspond to a new observation epoch
  911.                                 if (parseString(0, 1).equals(">")) {

  912.                                     eventFlag = parseInt(31, 1);
  913.                                     //If eventFlag>1, we skip the corresponding lines to the next observation
  914.                                     if (eventFlag != 0) {
  915.                                         final int nbLinesSkip = parseInt(32, 3);
  916.                                         for (int i = 0; i < nbLinesSkip; i++) {
  917.                                             readLine(reader, true);
  918.                                         }
  919.                                     } else {

  920.                                         tObs = new AbsoluteDate(parseInt(2, 4),
  921.                                                                 parseInt(6, 3),
  922.                                                                 parseInt(9, 3),
  923.                                                                 parseInt(12, 3),
  924.                                                                 parseInt(15, 3),
  925.                                                                 parseDouble(18, 11), timeScale);

  926.                                         nbSatObs  = parseInt(32, 3);
  927.                                         //If the total number of satellites was indicated in the Header
  928.                                         if (nbSat != -1 && nbSatObs > nbSat) {
  929.                                             //we check that the number of Sat in the observation is consistent
  930.                                             throw new OrekitException(OrekitMessages.INCONSISTENT_NUMBER_OF_SATS,
  931.                                                                       lineNumber, name, nbSatObs, nbSat);
  932.                                         }
  933.                                         //Read the Receiver Clock offset, if present
  934.                                         rcvrClkOffset = parseDouble(41, 15);
  935.                                         if (Double.isNaN(rcvrClkOffset)) {
  936.                                             rcvrClkOffset = 0.0;
  937.                                         }

  938.                                         //For each one of the Satellites in this Observation
  939.                                         for (int i = 0; i < nbSatObs; i++) {

  940.                                             readLine(reader, true);

  941.                                             //We check that the Satellite type is consistent with Satellite System in the top of the file
  942.                                             final SatelliteSystem satelliteSystemSat = SatelliteSystem.parseSatelliteSystem(parseString(0, 1));
  943.                                             if (!satelliteSystem.equals(SatelliteSystem.MIXED)) {
  944.                                                 if (!satelliteSystemSat.equals(satelliteSystem)) {
  945.                                                     throw new OrekitException(OrekitMessages.INCONSISTENT_SATELLITE_SYSTEM,
  946.                                                                               lineNumber, name, satelliteSystem, satelliteSystemSat);
  947.                                                 }
  948.                                             }

  949.                                             final int prn = parseInt(1, 2);
  950.                                             final int prnNumber;
  951.                                             switch (satelliteSystemSat) {
  952.                                                 case GPS:
  953.                                                 case GLONASS:
  954.                                                 case GALILEO:
  955.                                                 case BEIDOU:
  956.                                                 case IRNSS:
  957.                                                     prnNumber = prn;
  958.                                                     break;
  959.                                                 case QZSS:
  960.                                                     prnNumber = prn + 192;
  961.                                                     break;
  962.                                                 case SBAS:
  963.                                                     prnNumber = prn + 100;
  964.                                                     break;
  965.                                                 default:
  966.                                                     // MIXED satellite system is not allowed here
  967.                                                     throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  968.                                                                               lineNumber, name, line);
  969.                                             }
  970.                                             final List<ObservationData> observationData = new ArrayList<>(nbSatObs);
  971.                                             for (int j = 0; j < listTypeObs.get(satelliteSystemSat).size(); j++) {
  972.                                                 final ObservationType rf = listTypeObs.get(satelliteSystemSat).get(j);
  973.                                                 boolean scaleFactorFound = false;
  974.                                                 //We look for the lines of ScaledFactorCorrections that correspond to this SatSystem
  975.                                                 int k = 0;
  976.                                                 double value = parseDouble(3 + j * 16, 14);
  977.                                                 while (k < scaleFactorCorrections.size() && !scaleFactorFound) {
  978.                                                     if (scaleFactorCorrections.get(k).getSatelliteSystem().equals(satelliteSystemSat)) {
  979.                                                         //We check if the next Observation Type to read needs to be scaled
  980.                                                         if (scaleFactorCorrections.get(k).getTypesObsScaled().contains(rf)) {
  981.                                                             value /= scaleFactorCorrections.get(k).getCorrection();
  982.                                                             scaleFactorFound = true;
  983.                                                         }
  984.                                                     }
  985.                                                     k++;
  986.                                                 }
  987.                                                 observationData.add(new ObservationData(rf,
  988.                                                                                         value,
  989.                                                                                         parseInt(17 + j * 16, 1),
  990.                                                                                         parseInt(18 + j * 16, 1)));
  991.                                             }
  992.                                             observationDataSets.add(new ObservationDataSet(rinexHeader, satelliteSystemSat, prnNumber,
  993.                                                                                            tObs, rcvrClkOffset, observationData));

  994.                                         }
  995.                                     }
  996.                                 }
  997.                             }
  998.                         }
  999.                         break;
  1000.                     }
  1001.                     default:
  1002.                         //If RINEX Version is neither 2 nor 3
  1003.                         throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, name);
  1004.                 }
  1005.             }
  1006.         }

  1007.         /** Read a new line.
  1008.          * @param reader reader from where to read line
  1009.          * @param complainIfEnd if true an exception should be thrown if end of file is encountered
  1010.          * @return true if a line has been read
  1011.          * @exception IOException if a read error occurs
  1012.          */
  1013.         private boolean readLine(final BufferedReader reader, final boolean complainIfEnd)
  1014.             throws IOException {
  1015.             line = reader.readLine();
  1016.             if (line == null && complainIfEnd) {
  1017.                 throw new OrekitException(OrekitMessages.UNEXPECTED_END_OF_FILE, name);
  1018.             }
  1019.             lineNumber++;
  1020.             return line != null;
  1021.         }

  1022.         /** Extract a string from a line.
  1023.          * @param start start index of the string
  1024.          * @param length length of the string
  1025.          * @return parsed string
  1026.          */
  1027.         private String parseString(final int start, final int length) {
  1028.             if (line.length() > start) {
  1029.                 return line.substring(start, FastMath.min(line.length(), start + length)).trim();
  1030.             } else {
  1031.                 return null;
  1032.             }
  1033.         }

  1034.         /** Extract an integer from a line.
  1035.          * @param start start index of the integer
  1036.          * @param length length of the integer
  1037.          * @return parsed integer
  1038.          */
  1039.         private int parseInt(final int start, final int length) {
  1040.             if (line.length() > start && !parseString(start, length).isEmpty()) {
  1041.                 return Integer.parseInt(parseString(start, length));
  1042.             } else {
  1043.                 return 0;
  1044.             }
  1045.         }

  1046.         /** Extract a double from a line.
  1047.          * @param start start index of the real
  1048.          * @param length length of the real
  1049.          * @return parsed real, or {@code Double.NaN} if field was empty
  1050.          */
  1051.         private double parseDouble(final int start, final int length) {
  1052.             if (line.length() > start && !parseString(start, length).isEmpty()) {
  1053.                 return Double.parseDouble(parseString(start, length));
  1054.             } else {
  1055.                 return Double.NaN;
  1056.             }
  1057.         }

  1058.         /** Phase Shift corrections.
  1059.          * Contains the phase shift corrections used to
  1060.          * generate phases consistent with respect to cycle shifts.
  1061.          */
  1062.         public class PhaseShiftCorrection {

  1063.             /** Satellite System. */
  1064.             private final SatelliteSystem satSystemPhaseShift;
  1065.             /** Carrier Phase Observation Code (may be null). */
  1066.             private final ObservationType typeObsPhaseShift;
  1067.             /** Phase Shift Corrections (cycles). */
  1068.             private final double phaseShiftCorrection;
  1069.             /** List of satellites involved. */
  1070.             private final String[] satsPhaseShift;

  1071.             /** Simple constructor.
  1072.              * @param satSystemPhaseShift Satellite System
  1073.              * @param typeObsPhaseShift Carrier Phase Observation Code (may be null)
  1074.              * @param phaseShiftCorrection Phase Shift Corrections (cycles)
  1075.              * @param satsPhaseShift List of satellites involved
  1076.              */
  1077.             private PhaseShiftCorrection(final SatelliteSystem satSystemPhaseShift,
  1078.                                          final ObservationType typeObsPhaseShift,
  1079.                                          final double phaseShiftCorrection, final String[] satsPhaseShift) {
  1080.                 this.satSystemPhaseShift = satSystemPhaseShift;
  1081.                 this.typeObsPhaseShift = typeObsPhaseShift;
  1082.                 this.phaseShiftCorrection = phaseShiftCorrection;
  1083.                 this.satsPhaseShift = satsPhaseShift;
  1084.             }

  1085.             /** Get the Satellite System.
  1086.              * @return Satellite System.
  1087.              */
  1088.             public SatelliteSystem getSatelliteSystem() {
  1089.                 return satSystemPhaseShift;
  1090.             }
  1091.             /** Get the Carrier Phase Observation Code.
  1092.              * <p>
  1093.              * The observation code may be null for the uncorrected reference
  1094.              * signal group
  1095.              * </p>
  1096.              * @return Carrier Phase Observation Code.
  1097.              */
  1098.             public ObservationType getTypeObs() {
  1099.                 return typeObsPhaseShift;
  1100.             }
  1101.             /** Get the Phase Shift Corrections.
  1102.              * @return Phase Shift Corrections (cycles)
  1103.              */
  1104.             public double getCorrection() {
  1105.                 return phaseShiftCorrection;
  1106.             }
  1107.             /** Get the list of satellites involved.
  1108.              * @return List of satellites involved (if null, all the sats are involved)
  1109.              */
  1110.             public String[] getSatsCorrected() {
  1111.                 //If empty, all the satellites of this constellation are affected for this Observation type
  1112.                 return satsPhaseShift == null ? null : satsPhaseShift.clone();
  1113.             }
  1114.         }

  1115.         /** Scale Factor to be applied.
  1116.          * Contains the scale factors of 10 applied to the data before
  1117.          * being stored into the RINEX file.
  1118.          */
  1119.         public class ScaleFactorCorrection {

  1120.             /** Satellite System. */
  1121.             private final SatelliteSystem satSystemScaleFactor;
  1122.             /** List of Observations types that have been scaled. */
  1123.             private final List<ObservationType> typesObsScaleFactor;
  1124.             /** Factor to divide stored observations with before use. */
  1125.             private final double scaleFactor;

  1126.             /** Simple constructor.
  1127.              * @param satSystemScaleFactor Satellite System
  1128.              * @param scaleFactor Factor to divide stored observations (1,10,100,1000)
  1129.              * @param typesObsScaleFactor List of Observations types that have been scaled
  1130.              */
  1131.             private ScaleFactorCorrection(final SatelliteSystem satSystemScaleFactor,
  1132.                                           final double scaleFactor,
  1133.                                           final List<ObservationType> typesObsScaleFactor) {
  1134.                 this.satSystemScaleFactor = satSystemScaleFactor;
  1135.                 this.scaleFactor = scaleFactor;
  1136.                 this.typesObsScaleFactor = typesObsScaleFactor;
  1137.             }
  1138.             /** Get the Satellite System.
  1139.              * @return Satellite System
  1140.              */
  1141.             public SatelliteSystem getSatelliteSystem() {
  1142.                 return satSystemScaleFactor;
  1143.             }
  1144.             /** Get the Scale Factor.
  1145.              * @return Scale Factor
  1146.              */
  1147.             public double getCorrection() {
  1148.                 return scaleFactor;
  1149.             }
  1150.             /** Get the list of Observation Types scaled.
  1151.              * @return List of Observation types scaled
  1152.              */
  1153.             public List<ObservationType> getTypesObsScaled() {
  1154.                 return typesObsScaleFactor;
  1155.             }
  1156.         }

  1157.     }

  1158. }