RinexNavigationParser.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.navigation;

  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.Reader;
  21. import java.util.Arrays;
  22. import java.util.HashMap;
  23. import java.util.InputMismatchException;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Optional;
  27. import java.util.regex.Pattern;
  28. import java.util.stream.Stream;

  29. import org.orekit.annotation.DefaultDataContext;
  30. import org.orekit.data.DataContext;
  31. import org.orekit.data.DataSource;
  32. import org.orekit.errors.OrekitException;
  33. import org.orekit.errors.OrekitMessages;
  34. import org.orekit.gnss.SatelliteSystem;
  35. import org.orekit.gnss.TimeSystem;
  36. import org.orekit.gnss.navigation.RinexNavigation.TimeSystemCorrection;
  37. import org.orekit.propagation.analytical.gnss.data.BeidouNavigationMessage;
  38. import org.orekit.propagation.analytical.gnss.data.GLONASSNavigationMessage;
  39. import org.orekit.propagation.analytical.gnss.data.GPSNavigationMessage;
  40. import org.orekit.propagation.analytical.gnss.data.GalileoNavigationMessage;
  41. import org.orekit.propagation.analytical.gnss.data.IRNSSNavigationMessage;
  42. import org.orekit.propagation.analytical.gnss.data.QZSSNavigationMessage;
  43. import org.orekit.propagation.analytical.gnss.data.SBASNavigationMessage;
  44. import org.orekit.time.AbsoluteDate;
  45. import org.orekit.time.DateComponents;
  46. import org.orekit.time.GNSSDate;
  47. import org.orekit.time.TimeComponents;
  48. import org.orekit.time.TimeScale;
  49. import org.orekit.time.TimeScales;
  50. import org.orekit.utils.Constants;

  51. /**
  52.  * Parser for RINEX navigation messages files.
  53.  * <p>
  54.  * This parser handles RINEX version from 3.01 to 3.05. It is not adapted for RINEX 2.10 and 2.11 versions.
  55.  * </p>
  56.  * @see <a href="https://files.igs.org/pub/data/format/rinex301.pdf"> 3.01 navigation messages file format</a>
  57.  * @see <a href="https://files.igs.org/pub/data/format/rinex302.pdf"> 3.02 navigation messages file format</a>
  58.  * @see <a href="https://files.igs.org/pub/data/format/rinex303.pdf"> 3.03 navigation messages file format</a>
  59.  * @see <a href="https://files.igs.org/pub/data/format/rinex304.pdf"> 3.04 navigation messages file format</a>
  60.  * @see <a href="https://files.igs.org/pub/data/format/rinex305.pdf"> 3.05 navigation messages file format</a>
  61.  *
  62.  * @author Bryan Cazabonne
  63.  * @since 11.0
  64.  *
  65.  */
  66. public class RinexNavigationParser {

  67.     /** Handled clock file format versions. */
  68.     private static final List<Double> HANDLED_VERSIONS = Arrays.asList(3.01, 3.02, 3.03, 3.04, 3.05);

  69.     /** File Type. */
  70.     private static final String FILE_TYPE = "N";

  71.     /** Seconds to milliseconds converter. */
  72.     private static final Double SEC_TO_MILLI = 1000.0;

  73.     /** Kilometer to meters converter. */
  74.     private static final Double KM_TO_M = 1000.0;

  75.     /** Set of time scales. */
  76.     private final TimeScales timeScales;

  77.     /**
  78.      * Constructor.
  79.      * <p>This constructor uses the {@link DataContext#getDefault() default data context}.</p>
  80.      * @see #RinexNavigationParser(TimeScales)
  81.      *
  82.      */
  83.     @DefaultDataContext
  84.     public RinexNavigationParser() {
  85.         this(DataContext.getDefault().getTimeScales());
  86.     }

  87.     /**
  88.      * Constructor.
  89.      * @param timeScales the set of time scales used for parsing dates.
  90.      */
  91.     public RinexNavigationParser(final TimeScales timeScales) {
  92.         this.timeScales = timeScales;
  93.     }

  94.     /**
  95.      * Parse RINEX navigation messages.
  96.      * @param source source providing the data to parse
  97.      * @return a parsed  RINEX navigation messages file
  98.      * @throws IOException if {@code reader} throws one
  99.      */
  100.     public RinexNavigation parse(final DataSource source) throws IOException {

  101.         // initialize internal data structures
  102.         final ParseInfo pi = new ParseInfo();

  103.         int lineNumber = 0;
  104.         Stream<LineParser> candidateParsers = Stream.of(LineParser.HEADER_VERSION);
  105.         try (Reader reader = source.getOpener().openReaderOnce();
  106.              BufferedReader br = new BufferedReader(reader)) {
  107.             for (String line = br.readLine(); line != null; line = br.readLine()) {
  108.                 ++lineNumber;
  109.                 final String l = line;
  110.                 final Optional<LineParser> selected = candidateParsers.filter(p -> p.canHandle(l)).findFirst();
  111.                 if (selected.isPresent()) {
  112.                     try {
  113.                         selected.get().parse(line, pi);
  114.                     } catch (StringIndexOutOfBoundsException | NumberFormatException | InputMismatchException e) {
  115.                         throw new OrekitException(e,
  116.                                                   OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  117.                                                   lineNumber, source.getName(), line);
  118.                     }
  119.                     candidateParsers = selected.get().allowedNext();
  120.                 } else {
  121.                     throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  122.                                               lineNumber, source.getName(), line);
  123.                 }
  124.             }
  125.         }

  126.         return pi.file;

  127.     }

  128.     /**
  129.      * Parse a double value.
  130.      * @param line line to parse
  131.      * @param startIndex start index
  132.      * @param size size of the value
  133.      * @return the parsed value
  134.      */
  135.     private static double parseDouble(final String line, final int startIndex, final int size) {
  136.         return Double.parseDouble(line.substring(startIndex, startIndex + size).replace('D', 'E').trim());
  137.     }

  138.     /**
  139.      * Parse an integer value.
  140.      * @param line line to parse
  141.      * @param startIndex start index
  142.      * @param size size of the value
  143.      * @return the parsed value
  144.      */
  145.     private static int parseInt(final String line, final int startIndex, final int size) {
  146.         return Integer.parseInt(line.substring(startIndex, startIndex + size).trim());
  147.     }

  148.     /**
  149.      * Parse a string value.
  150.      * @param line line to parse
  151.      * @param startIndex start index
  152.      * @param size size of the value
  153.      * @return the parsed value
  154.      */
  155.     private static String parseString(final String line, final int startIndex, final int size) {
  156.         return line.substring(startIndex, startIndex + size).trim();
  157.     }

  158.     /** Transient data used for parsing a RINEX navigation messages file. */
  159.     private class ParseInfo {

  160.         /** Set of time scales for parsing dates. */
  161.         private final TimeScales timeScales;

  162.         /** The corresponding navigation messages file object. */
  163.         private RinexNavigation file;

  164.         /** The version of the navigation file. */
  165.         private double version;

  166.         /** Flag indicating the distinction between "alpha" and "beta" ionospheric coefficients. */
  167.         private boolean isIonosphereAlphaInitialized;

  168.         /** Satellite system line parser. */
  169.         private SatelliteSystemLineParser systemLineParser;

  170.         /** Current line number of the navigation message. */
  171.         private int lineNumber;

  172.         /** Container for GPS navigation message. */
  173.         private GPSNavigationMessage gpsNav;

  174.         /** Container for Galileo navigation message. */
  175.         private GalileoNavigationMessage galileoNav;

  176.         /** Container for Beidou navigation message. */
  177.         private BeidouNavigationMessage beidouNav;

  178.         /** Container for QZSS navigation message. */
  179.         private QZSSNavigationMessage qzssNav;

  180.         /** Container for IRNSS navigation message. */
  181.         private IRNSSNavigationMessage irnssNav;

  182.         /** Container for GLONASS navigation message. */
  183.         private GLONASSNavigationMessage glonassNav;

  184.         /** Container for SBAS navigation message. */
  185.         private SBASNavigationMessage sbasNav;

  186.         /** Constructor, build the ParseInfo object. */
  187.         ParseInfo() {
  188.             // Initialize default values for fields
  189.             this.timeScales                   = RinexNavigationParser.this.timeScales;
  190.             this.version                      = 1.0;
  191.             this.isIonosphereAlphaInitialized = false;
  192.             this.file                         = new RinexNavigation();
  193.             this.systemLineParser             = SatelliteSystemLineParser.GPS;
  194.             this.lineNumber                   = 0;
  195.             this.gpsNav                       = new GPSNavigationMessage();
  196.             this.galileoNav                   = new GalileoNavigationMessage();
  197.             this.beidouNav                    = new BeidouNavigationMessage();
  198.             this.qzssNav                      = new QZSSNavigationMessage();
  199.             this.irnssNav                     = new IRNSSNavigationMessage();
  200.             this.glonassNav                   = new GLONASSNavigationMessage();
  201.             this.sbasNav                      = new SBASNavigationMessage();
  202.         }

  203.     }

  204.     /** Parsers for specific lines. */
  205.     private enum LineParser {

  206.         /** Parser for version, file type and satellite system. */
  207.         HEADER_VERSION("^.+RINEX VERSION / TYPE( )*$") {

  208.             /** {@inheritDoc} */
  209.             @Override
  210.             public void parse(final String line, final ParseInfo pi) {

  211.                 // Rinex version
  212.                 pi.version = parseDouble(line, 0, 9);

  213.                 // Throw exception if format version is not handled
  214.                 if (!HANDLED_VERSIONS.contains(pi.version)) {
  215.                     throw new OrekitException(OrekitMessages.NAVIGATION_FILE_UNSUPPORTED_VERSION, pi.version);
  216.                 }
  217.                 pi.file.setFormatVersion(pi.version);

  218.                 // File type
  219.                 pi.file.setFileType(FILE_TYPE);

  220.                 // Satellite system
  221.                 final SatelliteSystem system = SatelliteSystem.parseSatelliteSystem(parseString(line, 40, 1));
  222.                 pi.file.setSatelliteSystem(system);

  223.             }

  224.             /** {@inheritDoc} */
  225.             @Override
  226.             public Stream<LineParser> allowedNext() {
  227.                 return Stream.of(HEADER_PROGRAM);
  228.             }

  229.         },

  230.         /** Parser for generating program and emiting agency. */
  231.         HEADER_PROGRAM("^.+PGM / RUN BY / DATE( )*$") {

  232.             /** {@inheritDoc} */
  233.             @Override
  234.             public void parse(final String line, final ParseInfo pi) {

  235.                 // Name of the generating program
  236.                 final String programName = parseString(line, 0, 20);
  237.                 pi.file.setProgramName(programName);

  238.                 // Name of the emiting agency
  239.                 final String agencyName = parseString(line, 20, 20);
  240.                 pi.file.setAgencyName(agencyName);

  241.                 // Date and time of file creation
  242.                 final String date     = parseString(line, 40, 8);
  243.                 final String time     = parseString(line, 49, 6);
  244.                 final String timeZone = parseString(line, 56, 4);
  245.                 pi.file.setCreationDateString(date);
  246.                 pi.file.setCreationTimeString(time);
  247.                 pi.file.setCreationTimeZoneString(timeZone);

  248.                 // Convert date and time to an Orekit absolute date
  249.                 final DateComponents dateComponents = new DateComponents(parseInt(date, 0, 4),
  250.                                                                          parseInt(date, 4, 2),
  251.                                                                          parseInt(date, 6, 2));
  252.                 final TimeComponents timeComponents = new TimeComponents(parseInt(time, 0, 2),
  253.                                                                          parseInt(time, 2, 2),
  254.                                                                          parseInt(time, 4, 2));
  255.                 pi.file.setCreationDate(new AbsoluteDate(dateComponents,
  256.                                                          timeComponents,
  257.                                                          TimeSystem.parseTimeSystem(timeZone).getTimeScale(pi.timeScales)));

  258.             }

  259.             /** {@inheritDoc} */
  260.             @Override
  261.             public Stream<LineParser> allowedNext() {
  262.                 return Stream.of(HEADER_COMMENT, HEADER_IONOSPHERIC, HEADER_TIME, HEADER_LEAP_SECONDS, HEADER_END);
  263.             }

  264.         },

  265.         /** Parser for comments. */
  266.         HEADER_COMMENT("^.+COMMENT( )*$") {

  267.             /** {@inheritDoc} */
  268.             @Override
  269.             public void parse(final String line, final ParseInfo pi) {
  270.                 pi.file.addComment(parseString(line, 0, 60));
  271.             }

  272.             /** {@inheritDoc} */
  273.             @Override
  274.             public Stream<LineParser> allowedNext() {
  275.                 return Stream.of(HEADER_COMMENT, HEADER_IONOSPHERIC, HEADER_TIME, HEADER_LEAP_SECONDS, HEADER_END);
  276.             }

  277.         },

  278.         /** Parser for ionospheric correction parameters. */
  279.         HEADER_IONOSPHERIC("^.+IONOSPHERIC CORR( )*$") {

  280.             /** {@inheritDoc} */
  281.             @Override
  282.             public void parse(final String line, final ParseInfo pi) {

  283.                 // Satellite system
  284.                 final String ionoType = parseString(line, 0, 3);
  285.                 pi.file.setIonosphericCorrectionType(ionoType);

  286.                 // Read coefficients
  287.                 final double[] parameters = new double[4];
  288.                 parameters[0] = parseDouble(line, 5,  12);
  289.                 parameters[1] = parseDouble(line, 17, 12);
  290.                 parameters[2] = parseDouble(line, 29, 12);
  291.                 parameters[3] = parseDouble(line, 41, 12);

  292.                 // Verify if we are parsing Galileo ionospheric parameters
  293.                 if ("GAL".equals(ionoType)) {

  294.                     // We are parsing Galileo ionospheric parameters
  295.                     pi.file.setNeQuickAlpha(parameters);

  296.                 } else {
  297.                     // We are parsing Klobuchar ionospheric parameters

  298.                     // Verify if we are parsing "alpha" or "beta" ionospheric parameters
  299.                     if (pi.isIonosphereAlphaInitialized) {

  300.                         // Ionospheric "beta" parameters
  301.                         pi.file.setKlobucharBeta(parameters);

  302.                     } else {

  303.                         // Ionospheric "alpha" parameters
  304.                         pi.file.setKlobucharAlpha(parameters);

  305.                         // Set the flag to true
  306.                         pi.isIonosphereAlphaInitialized = true;

  307.                     }

  308.                 }

  309.             }

  310.             /** {@inheritDoc} */
  311.             @Override
  312.             public Stream<LineParser> allowedNext() {
  313.                 return Stream.of(HEADER_COMMENT, HEADER_IONOSPHERIC, HEADER_TIME, HEADER_LEAP_SECONDS, HEADER_END);
  314.             }

  315.         },

  316.         /** Parser for corrections to transform the system time to UTC or to other time systems. */
  317.         HEADER_TIME("^.+TIME SYSTEM CORR( )*$") {

  318.             /** {@inheritDoc} */
  319.             @Override
  320.             public void parse(final String line, final ParseInfo pi) {

  321.                 // Read fields
  322.                 final String type    = parseString(line, 0,  4);
  323.                 final double a0      = parseDouble(line, 5,  17);
  324.                 final double a1      = parseDouble(line, 22, 16);
  325.                 final int    refTime = parseInt(line, 38, 7);
  326.                 final int    refWeek = parseInt(line, 46, 5);

  327.                 // Add to the list
  328.                 final TimeSystemCorrection tsc = new TimeSystemCorrection(type, a0, a1, refTime, refWeek);
  329.                 pi.file.addTimeSystemCorrections(tsc);

  330.             }

  331.             /** {@inheritDoc} */
  332.             @Override
  333.             public Stream<LineParser> allowedNext() {
  334.                 return Stream.of(HEADER_COMMENT, HEADER_TIME, HEADER_LEAP_SECONDS, HEADER_END);
  335.             }

  336.         },

  337.         /** Parser for leap seconds. */
  338.         HEADER_LEAP_SECONDS("^.+LEAP SECONDS( )*$") {

  339.             /** {@inheritDoc} */
  340.             @Override
  341.             public void parse(final String line, final ParseInfo pi) {
  342.                 // Current number of leap seconds
  343.                 pi.file.setNumberOfLeapSeconds(parseInt(line, 0, 6));
  344.             }

  345.             /** {@inheritDoc} */
  346.             @Override
  347.             public Stream<LineParser> allowedNext() {
  348.                 return Stream.of(HEADER_COMMENT, HEADER_IONOSPHERIC, HEADER_TIME, HEADER_END);
  349.             }

  350.         },

  351.         /** Parser for the end of header. */
  352.         HEADER_END("^.+END OF HEADER( )*$") {

  353.             /** {@inheritDoc} */
  354.             @Override
  355.             public void parse(final String line, final ParseInfo pi) {
  356.                 // do nothing...
  357.             }

  358.             /** {@inheritDoc} */
  359.             @Override
  360.             public Stream<LineParser> allowedNext() {
  361.                 return Stream.of(NAVIGATION_MESSAGE_FIRST);
  362.             }

  363.         },

  364.         /** Parser for navigation message first data line. */
  365.         NAVIGATION_MESSAGE_FIRST("(^G|^R|^E|^C|^I|^J|^S).+$") {

  366.             /** {@inheritDoc} */
  367.             @Override
  368.             public void parse(final String line, final ParseInfo pi) {

  369.                 // Set the line number to 0
  370.                 pi.lineNumber = 0;

  371.                 // Current satellite system
  372.                 final String key = parseString(line, 0, 1);

  373.                 // Initialize parser
  374.                 pi.systemLineParser = SatelliteSystemLineParser.getSatelliteSystemLineParser(key);

  375.                 // Read first line
  376.                 pi.systemLineParser.parseFirstLine(line, pi);

  377.             }

  378.             /** {@inheritDoc} */
  379.             @Override
  380.             public Stream<LineParser> allowedNext() {
  381.                 return Stream.of(NAVIGATION_BROADCAST_ORBIT);
  382.             }

  383.         },

  384.         /** Parser for broadcast orbit. */
  385.         NAVIGATION_BROADCAST_ORBIT("^    .+") {

  386.             /** {@inheritDoc} */
  387.             @Override
  388.             public void parse(final String line, final ParseInfo pi) {

  389.                 // Increment the line number
  390.                 pi.lineNumber++;

  391.                 // Read the corresponding line
  392.                 if (pi.lineNumber == 1) {
  393.                     // BROADCAST ORBIT – 1
  394.                     pi.systemLineParser.parseFirstBroadcastOrbit(line, pi);
  395.                 } else if (pi.lineNumber == 2) {
  396.                     // BROADCAST ORBIT – 2
  397.                     pi.systemLineParser.parseSecondBroadcastOrbit(line, pi);
  398.                 } else if (pi.lineNumber == 3) {
  399.                     // BROADCAST ORBIT – 3
  400.                     pi.systemLineParser.parseThirdBroadcastOrbit(line, pi);
  401.                 } else if (pi.lineNumber == 4) {
  402.                     // BROADCAST ORBIT – 4
  403.                     pi.systemLineParser.parseFourthBroadcastOrbit(line, pi);
  404.                 } else if (pi.lineNumber == 5) {
  405.                     // BROADCAST ORBIT – 5
  406.                     pi.systemLineParser.parseFifthBroadcastOrbit(line, pi);
  407.                 } else if (pi.lineNumber == 6) {
  408.                     // BROADCAST ORBIT – 6
  409.                     pi.systemLineParser.parseSixthBroadcastOrbit(line, pi);
  410.                 } else {
  411.                     // BROADCAST ORBIT – 7
  412.                     pi.systemLineParser.parseSeventhBroadcastOrbit(line, pi);
  413.                 }

  414.             }

  415.             /** {@inheritDoc} */
  416.             @Override
  417.             public Stream<LineParser> allowedNext() {
  418.                 return Stream.of(NAVIGATION_MESSAGE_FIRST, NAVIGATION_BROADCAST_ORBIT);
  419.             }

  420.         };

  421.         /** Pattern for identifying line. */
  422.         private final Pattern pattern;

  423.         /** Simple constructor.
  424.          * @param lineRegexp regular expression for identifying line
  425.          */
  426.         LineParser(final String lineRegexp) {
  427.             pattern = Pattern.compile(lineRegexp);
  428.         }

  429.         /** Parse a line.
  430.          * @param line line to parse
  431.          * @param pi holder for transient data
  432.          */
  433.         public abstract void parse(String line, ParseInfo pi);

  434.         /** Get the allowed parsers for next line.
  435.          * @return allowed parsers for next line
  436.          */
  437.         public abstract Stream<LineParser> allowedNext();

  438.         /** Check if parser can handle line.
  439.          * @param line line to parse
  440.          * @return true if parser can handle the specified line
  441.          */
  442.         public boolean canHandle(final String line) {
  443.             return pattern.matcher(line).matches();
  444.         }

  445.     }

  446.     /** Parsers for satellite system specific lines. */
  447.     private enum SatelliteSystemLineParser {

  448.         /** GPS. */
  449.         GPS("G") {

  450.             /** {@inheritDoc} */
  451.             @Override
  452.             public void parseFirstLine(final String line, final ParseInfo pi) {
  453.                 // PRN
  454.                 pi.gpsNav.setPRN(parseInt(line, 1, 2));

  455.                 // Toc
  456.                 final int gpsTocYear  = parseInt(line, 4, 4);
  457.                 final int gpsTocMonth = parseInt(line, 9, 2);
  458.                 final int gpsTocDay   = parseInt(line, 12, 2);
  459.                 final int gpsTocHours = parseInt(line, 15, 2);
  460.                 final int gpsTocMin   = parseInt(line, 18, 2);
  461.                 final int gpsTocSec   = parseInt(line, 21, 2);
  462.                 pi.gpsNav.setEpochToc(new AbsoluteDate(gpsTocYear, gpsTocMonth, gpsTocDay, gpsTocHours,
  463.                                                        gpsTocMin, gpsTocSec, pi.timeScales.getGPS()));

  464.                 // Af0, Af1, and Af2
  465.                 pi.gpsNav.setAf0(parseDouble(line, 23, 19));
  466.                 pi.gpsNav.setAf1(parseDouble(line, 42, 19));
  467.                 pi.gpsNav.setAf2(parseDouble(line, 61, 19));
  468.             }

  469.             /** {@inheritDoc} */
  470.             @Override
  471.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  472.                 // IODE
  473.                 pi.gpsNav.setIODE(parseDouble(line, 4, 19));
  474.                 // Crs
  475.                 pi.gpsNav.setCrs(parseDouble(line, 23, 19));
  476.                 // Delta n
  477.                 pi.gpsNav.setDeltaN(parseDouble(line, 42, 19));
  478.                 // M0
  479.                 pi.gpsNav.setM0(parseDouble(line, 61, 19));
  480.             }

  481.             /** {@inheritDoc} */
  482.             @Override
  483.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  484.                 // Cuc
  485.                 pi.gpsNav.setCuc(parseDouble(line, 4, 19));
  486.                 // e
  487.                 pi.gpsNav.setE(parseDouble(line, 23, 19));
  488.                 // Cus
  489.                 pi.gpsNav.setCus(parseDouble(line, 42, 19));
  490.                 // sqrt(A)
  491.                 pi.gpsNav.setSqrtA(parseDouble(line, 61, 19));
  492.             }

  493.             /** {@inheritDoc} */
  494.             @Override
  495.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  496.                 // Toe
  497.                 pi.gpsNav.setTime(parseDouble(line, 4, 19));
  498.                 // Cic
  499.                 pi.gpsNav.setCic(parseDouble(line, 23, 19));
  500.                 // Omega0
  501.                 pi.gpsNav.setOmega0(parseDouble(line, 42, 19));
  502.                 // Cis
  503.                 pi.gpsNav.setCis(parseDouble(line, 61, 19));
  504.             }

  505.             /** {@inheritDoc} */
  506.             @Override
  507.             public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) {
  508.                 // i0
  509.                 pi.gpsNav.setI0(parseDouble(line, 4, 19));
  510.                 // Crc
  511.                 pi.gpsNav.setCrc(parseDouble(line, 23, 19));
  512.                 // omega
  513.                 pi.gpsNav.setPa(parseDouble(line, 42, 19));
  514.                 // OMEGA DOT
  515.                 pi.gpsNav.setOmegaDot(parseDouble(line, 61, 19));
  516.             }

  517.             /** {@inheritDoc} */
  518.             @Override
  519.             public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) {
  520.                 // iDot
  521.                 pi.gpsNav.setIDot(parseDouble(line, 4, 19));
  522.                 // Codes on L2 channel (ignored)
  523.                 // parseDouble(line, 23, 19)
  524.                 // GPS week (to go with Toe)
  525.                 pi.gpsNav.setWeek((int) parseDouble(line, 42, 19));
  526.                 pi.gpsNav.setDate(new GNSSDate(pi.gpsNav.getWeek(),
  527.                                                SEC_TO_MILLI * pi.gpsNav.getTime(),
  528.                                                SatelliteSystem.GPS,
  529.                                                pi.timeScales).getDate());
  530.             }

  531.             /** {@inheritDoc} */
  532.             @Override
  533.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  534.                 // SV accuracy
  535.                 pi.gpsNav.setSvAccuracy(parseDouble(line, 4, 19));
  536.                 // Health
  537.                 pi.gpsNav.setSvHealth(parseDouble(line, 23, 19));
  538.                 // TGD
  539.                 pi.gpsNav.setTGD(parseDouble(line, 42, 19));
  540.                 // IODC
  541.                 pi.gpsNav.setIODC(parseDouble(line, 61, 19));
  542.             }

  543.             /** {@inheritDoc} */
  544.             @Override
  545.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  546.                 // Add the navigation message to the file
  547.                 pi.file.addGPSNavigationMessage(pi.gpsNav);
  548.                 // Reinitialized the container for navigation data
  549.                 pi.gpsNav = new GPSNavigationMessage();
  550.             }

  551.         },

  552.         /** Galileo. */
  553.         GALILEO("E") {

  554.             /** {@inheritDoc} */
  555.             @Override
  556.             public void parseFirstLine(final String line, final ParseInfo pi) {
  557.                 // PRN
  558.                 pi.galileoNav.setPRN(parseInt(line, 1, 2));

  559.                 // Toc
  560.                 final int galileoTocYear  = parseInt(line, 4, 4);
  561.                 final int galileoTocMonth = parseInt(line, 9, 2);
  562.                 final int galileoTocDay   = parseInt(line, 12, 2);
  563.                 final int galileoTocHours = parseInt(line, 15, 2);
  564.                 final int galileoTocMin   = parseInt(line, 18, 2);
  565.                 final int galileoTocSec   = parseInt(line, 21, 2);
  566.                 pi.galileoNav.setEpochToc(new AbsoluteDate(galileoTocYear, galileoTocMonth, galileoTocDay, galileoTocHours,
  567.                                                        galileoTocMin, galileoTocSec, pi.timeScales.getGST()));

  568.                 // Af0, Af1, and Af2
  569.                 pi.galileoNav.setAf0(parseDouble(line, 23, 19));
  570.                 pi.galileoNav.setAf1(parseDouble(line, 42, 19));
  571.                 pi.galileoNav.setAf2(parseDouble(line, 61, 19));
  572.             }

  573.             /** {@inheritDoc} */
  574.             @Override
  575.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  576.                 // IODNav
  577.                 pi.galileoNav.setIODNav(parseDouble(line, 4, 19));
  578.                 // Crs
  579.                 pi.galileoNav.setCrs(parseDouble(line, 23, 19));
  580.                 // Delta n
  581.                 pi.galileoNav.setDeltaN(parseDouble(line, 42, 19));
  582.                 // M0
  583.                 pi.galileoNav.setM0(parseDouble(line, 61, 19));
  584.             }

  585.             /** {@inheritDoc} */
  586.             @Override
  587.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  588.                 // Cuc
  589.                 pi.galileoNav.setCuc(parseDouble(line, 4, 19));
  590.                 // e
  591.                 pi.galileoNav.setE(parseDouble(line, 23, 19));
  592.                 // Cus
  593.                 pi.galileoNav.setCus(parseDouble(line, 42, 19));
  594.                 // sqrt(A)
  595.                 pi.galileoNav.setSqrtA(parseDouble(line, 61, 19));
  596.             }

  597.             /** {@inheritDoc} */
  598.             @Override
  599.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  600.                 // Toe
  601.                 pi.galileoNav.setTime(parseDouble(line, 4, 19));
  602.                 // Cic
  603.                 pi.galileoNav.setCic(parseDouble(line, 23, 19));
  604.                 // Omega0
  605.                 pi.galileoNav.setOmega0(parseDouble(line, 42, 19));
  606.                 // Cis
  607.                 pi.galileoNav.setCis(parseDouble(line, 61, 19));
  608.             }

  609.             /** {@inheritDoc} */
  610.             @Override
  611.             public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) {
  612.                 // i0
  613.                 pi.galileoNav.setI0(parseDouble(line, 4, 19));
  614.                 // Crc
  615.                 pi.galileoNav.setCrc(parseDouble(line, 23, 19));
  616.                 // omega
  617.                 pi.galileoNav.setPa(parseDouble(line, 42, 19));
  618.                 // OMEGA DOT
  619.                 pi.galileoNav.setOmegaDot(parseDouble(line, 61, 19));
  620.             }

  621.             /** {@inheritDoc} */
  622.             @Override
  623.             public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) {
  624.                 // iDot
  625.                 pi.galileoNav.setIDot(parseDouble(line, 4, 19));
  626.                 // Data sources (ignored)
  627.                 // parseDouble(line, 23, 19)
  628.                 // GAL week (to go with Toe)
  629.                 pi.galileoNav.setWeek((int) parseDouble(line, 42, 19));
  630.                 pi.galileoNav.setDate(new GNSSDate(pi.galileoNav.getWeek(),
  631.                                                    SEC_TO_MILLI * pi.galileoNav.getTime(),
  632.                                                    SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week!
  633.                                                    pi.timeScales).getDate());
  634.             }

  635.             /** {@inheritDoc} */
  636.             @Override
  637.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  638.                 // SISA
  639.                 pi.galileoNav.setSisa(parseDouble(line, 4, 19));
  640.                 // Health
  641.                 pi.galileoNav.setSvHealth(parseDouble(line, 23, 19));
  642.                 // E5a/E1 BGD
  643.                 pi.galileoNav.setBGDE1E5a(parseDouble(line, 42, 19));
  644.                 // E5b/E1 BGD
  645.                 pi.galileoNav.setBGDE5bE1(parseDouble(line, 61, 19));
  646.             }

  647.             /** {@inheritDoc} */
  648.             @Override
  649.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  650.                 // Add the navigation message to the file
  651.                 pi.file.addGalileoNavigationMessage(pi.galileoNav);
  652.                 // Reinitialized the container for navigation data
  653.                 pi.galileoNav = new GalileoNavigationMessage();
  654.             }

  655.         },

  656.         /** Beidou. */
  657.         BEIDOU("C") {

  658.             /** {@inheritDoc} */
  659.             @Override
  660.             public void parseFirstLine(final String line, final ParseInfo pi) {
  661.                 // PRN
  662.                 pi.beidouNav.setPRN(parseInt(line, 1, 2));

  663.                 // Toc
  664.                 final int beidouTocYear  = parseInt(line, 4, 4);
  665.                 final int beidouTocMonth = parseInt(line, 9, 2);
  666.                 final int beidouTocDay   = parseInt(line, 12, 2);
  667.                 final int beidouTocHours = parseInt(line, 15, 2);
  668.                 final int beidouTocMin   = parseInt(line, 18, 2);
  669.                 final int beidouTocSec   = parseInt(line, 21, 2);
  670.                 pi.beidouNav.setEpochToc(new AbsoluteDate(beidouTocYear, beidouTocMonth, beidouTocDay, beidouTocHours,
  671.                                                        beidouTocMin, beidouTocSec, pi.timeScales.getBDT()));

  672.                 // Af0, Af1, and Af2
  673.                 pi.beidouNav.setAf0(parseDouble(line, 23, 19));
  674.                 pi.beidouNav.setAf1(parseDouble(line, 42, 19));
  675.                 pi.beidouNav.setAf2(parseDouble(line, 61, 19));
  676.             }

  677.             /** {@inheritDoc} */
  678.             @Override
  679.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  680.                 // AODE
  681.                 pi.beidouNav.setAODE(parseDouble(line, 4, 19));
  682.                 // Crs
  683.                 pi.beidouNav.setCrs(parseDouble(line, 23, 19));
  684.                 // Delta n
  685.                 pi.beidouNav.setDeltaN(parseDouble(line, 42, 19));
  686.                 // M0
  687.                 pi.beidouNav.setM0(parseDouble(line, 61, 19));
  688.             }

  689.             /** {@inheritDoc} */
  690.             @Override
  691.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  692.                 // Cuc
  693.                 pi.beidouNav.setCuc(parseDouble(line, 4, 19));
  694.                 // e
  695.                 pi.beidouNav.setE(parseDouble(line, 23, 19));
  696.                 // Cus
  697.                 pi.beidouNav.setCus(parseDouble(line, 42, 19));
  698.                 // sqrt(A)
  699.                 pi.beidouNav.setSqrtA(parseDouble(line, 61, 19));
  700.             }

  701.             /** {@inheritDoc} */
  702.             @Override
  703.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  704.                 // Toe
  705.                 pi.beidouNav.setTime(parseDouble(line, 4, 19));
  706.                 // Cic
  707.                 pi.beidouNav.setCic(parseDouble(line, 23, 19));
  708.                 // Omega0
  709.                 pi.beidouNav.setOmega0(parseDouble(line, 42, 19));
  710.                 // Cis
  711.                 pi.beidouNav.setCis(parseDouble(line, 61, 19));
  712.             }

  713.             /** {@inheritDoc} */
  714.             @Override
  715.             public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) {
  716.                 // i0
  717.                 pi.beidouNav.setI0(parseDouble(line, 4, 19));
  718.                 // Crc
  719.                 pi.beidouNav.setCrc(parseDouble(line, 23, 19));
  720.                 // omega
  721.                 pi.beidouNav.setPa(parseDouble(line, 42, 19));
  722.                 // OMEGA DOT
  723.                 pi.beidouNav.setOmegaDot(parseDouble(line, 61, 19));
  724.             }

  725.             /** {@inheritDoc} */
  726.             @Override
  727.             public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) {
  728.                 // iDot
  729.                 pi.beidouNav.setIDot(parseDouble(line, 4, 19));
  730.                 // BDT week (to go with Toe)
  731.                 pi.beidouNav.setWeek((int) parseDouble(line, 42, 19));
  732.                 pi.beidouNav.setDate(new GNSSDate(pi.beidouNav.getWeek(),
  733.                                                   SEC_TO_MILLI * pi.beidouNav.getTime(),
  734.                                                   SatelliteSystem.BEIDOU,
  735.                                                   pi.timeScales).getDate());
  736.             }

  737.             /** {@inheritDoc} */
  738.             @Override
  739.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  740.                 // SV accuracy
  741.                 pi.beidouNav.setSvAccuracy(parseDouble(line, 4, 19));
  742.                 // SatH1 (ignored)
  743.                 // parseDouble(line, 23, 19)
  744.                 // TGD1
  745.                 pi.beidouNav.setTGD1(parseDouble(line, 42, 19));
  746.                 // TGD2
  747.                 pi.beidouNav.setTGD2(parseDouble(line, 61, 19));
  748.             }

  749.             /** {@inheritDoc} */
  750.             @Override
  751.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  752.                 // Transmission time of message (ignored)
  753.                 // parseDouble(line, 4, 19);
  754.                 // AODC
  755.                 pi.beidouNav.setAODC(parseDouble(line, 23, 19));
  756.                 // Add the navigation message to the file
  757.                 pi.file.addBeidouNavigationMessage(pi.beidouNav);
  758.                 // Reinitialized the container for navigation data
  759.                 pi.beidouNav = new BeidouNavigationMessage();

  760.             }

  761.         },

  762.         /** QZSS. */
  763.         QZSS("J") {

  764.             /** {@inheritDoc} */
  765.             @Override
  766.             public void parseFirstLine(final String line, final ParseInfo pi) {
  767.                 // PRN
  768.                 pi.qzssNav.setPRN(parseInt(line, 1, 2));

  769.                 // Toc
  770.                 final int qzssTocYear  = parseInt(line, 4, 4);
  771.                 final int qzssTocMonth = parseInt(line, 9, 2);
  772.                 final int qzssTocDay   = parseInt(line, 12, 2);
  773.                 final int qzssTocHours = parseInt(line, 15, 2);
  774.                 final int qzssTocMin   = parseInt(line, 18, 2);
  775.                 final int qzssTocSec   = parseInt(line, 21, 2);
  776.                 pi.qzssNav.setEpochToc(new AbsoluteDate(qzssTocYear, qzssTocMonth, qzssTocDay, qzssTocHours,
  777.                                                        qzssTocMin, qzssTocSec, pi.timeScales.getQZSS()));

  778.                 // Af0, Af1, and Af2
  779.                 pi.qzssNav.setAf0(parseDouble(line, 23, 19));
  780.                 pi.qzssNav.setAf1(parseDouble(line, 42, 19));
  781.                 pi.qzssNav.setAf2(parseDouble(line, 61, 19));
  782.             }

  783.             /** {@inheritDoc} */
  784.             @Override
  785.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  786.                 // IODE
  787.                 pi.qzssNav.setIODE(parseDouble(line, 4, 19));
  788.                 // Crs
  789.                 pi.qzssNav.setCrs(parseDouble(line, 23, 19));
  790.                 // Delta n
  791.                 pi.qzssNav.setDeltaN(parseDouble(line, 42, 19));
  792.                 // M0
  793.                 pi.qzssNav.setM0(parseDouble(line, 61, 19));
  794.             }

  795.             /** {@inheritDoc} */
  796.             @Override
  797.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  798.                 // Cuc
  799.                 pi.qzssNav.setCuc(parseDouble(line, 4, 19));
  800.                 // e
  801.                 pi.qzssNav.setE(parseDouble(line, 23, 19));
  802.                 // Cus
  803.                 pi.qzssNav.setCus(parseDouble(line, 42, 19));
  804.                 // sqrt(A)
  805.                 pi.qzssNav.setSqrtA(parseDouble(line, 61, 19));
  806.             }

  807.             /** {@inheritDoc} */
  808.             @Override
  809.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  810.                 // Toe
  811.                 pi.qzssNav.setTime(parseDouble(line, 4, 19));
  812.                 // Cic
  813.                 pi.qzssNav.setCic(parseDouble(line, 23, 19));
  814.                 // Omega0
  815.                 pi.qzssNav.setOmega0(parseDouble(line, 42, 19));
  816.                 // Cis
  817.                 pi.qzssNav.setCis(parseDouble(line, 61, 19));
  818.             }

  819.             /** {@inheritDoc} */
  820.             @Override
  821.             public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) {
  822.                 // i0
  823.                 pi.qzssNav.setI0(parseDouble(line, 4, 19));
  824.                 // Crc
  825.                 pi.qzssNav.setCrc(parseDouble(line, 23, 19));
  826.                 // omega
  827.                 pi.qzssNav.setPa(parseDouble(line, 42, 19));
  828.                 // OMEGA DOT
  829.                 pi.qzssNav.setOmegaDot(parseDouble(line, 61, 19));
  830.             }

  831.             /** {@inheritDoc} */
  832.             @Override
  833.             public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) {
  834.                 // iDot
  835.                 pi.qzssNav.setIDot(parseDouble(line, 4, 19));
  836.                 // Codes on L2 channel (ignored)
  837.                 // parseDouble(line, 23, 19)
  838.                 // GPS week (to go with Toe)
  839.                 pi.qzssNav.setWeek((int) parseDouble(line, 42, 19));
  840.                 pi.qzssNav.setDate(new GNSSDate(pi.qzssNav.getWeek(),
  841.                                                 SEC_TO_MILLI * pi.qzssNav.getTime(),
  842.                                                 SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week!
  843.                                                 pi.timeScales).getDate());
  844.             }

  845.             /** {@inheritDoc} */
  846.             @Override
  847.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  848.                 // SV accuracy
  849.                 pi.qzssNav.setSvAccuracy(parseDouble(line, 4, 19));
  850.                 // Health
  851.                 pi.qzssNav.setSvHealth(parseDouble(line, 23, 19));
  852.                 // TGD
  853.                 pi.qzssNav.setTGD(parseDouble(line, 42, 19));
  854.                 // IODC
  855.                 pi.qzssNav.setIODC(parseDouble(line, 61, 19));
  856.             }

  857.             /** {@inheritDoc} */
  858.             @Override
  859.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  860.                 // Add the navigation message to the file
  861.                 pi.file.addQZSSNavigationMessage(pi.qzssNav);
  862.                 // Reinitialized the container for navigation data
  863.                 pi.qzssNav = new QZSSNavigationMessage();
  864.             }

  865.         },

  866.         /** IRNSS. */
  867.         IRNSS("I") {

  868.             /** {@inheritDoc} */
  869.             @Override
  870.             public void parseFirstLine(final String line, final ParseInfo pi) {
  871.                 // PRN
  872.                 pi.irnssNav.setPRN(parseInt(line, 1, 2));

  873.                 // Toc
  874.                 final int irnssTocYear  = parseInt(line, 4, 4);
  875.                 final int irnssTocMonth = parseInt(line, 9, 2);
  876.                 final int irnssTocDay   = parseInt(line, 12, 2);
  877.                 final int irnssTocHours = parseInt(line, 15, 2);
  878.                 final int irnssTocMin   = parseInt(line, 18, 2);
  879.                 final int irnssTocSec   = parseInt(line, 21, 2);
  880.                 pi.irnssNav.setEpochToc(new AbsoluteDate(irnssTocYear, irnssTocMonth, irnssTocDay, irnssTocHours,
  881.                                                          irnssTocMin, irnssTocSec, pi.timeScales.getIRNSS()));

  882.                 // Af0, Af1, and Af2
  883.                 pi.irnssNav.setAf0(parseDouble(line, 23, 19));
  884.                 pi.irnssNav.setAf1(parseDouble(line, 42, 19));
  885.                 pi.irnssNav.setAf2(parseDouble(line, 61, 19));
  886.             }

  887.             /** {@inheritDoc} */
  888.             @Override
  889.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  890.                 // IODEC
  891.                 pi.irnssNav.setIODEC(parseDouble(line, 4, 19));
  892.                 // Crs
  893.                 pi.irnssNav.setCrs(parseDouble(line, 23, 19));
  894.                 // Delta n
  895.                 pi.irnssNav.setDeltaN(parseDouble(line, 42, 19));
  896.                 // M0
  897.                 pi.irnssNav.setM0(parseDouble(line, 61, 19));
  898.             }

  899.             /** {@inheritDoc} */
  900.             @Override
  901.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  902.                 // Cuc
  903.                 pi.irnssNav.setCuc(parseDouble(line, 4, 19));
  904.                 // e
  905.                 pi.irnssNav.setE(parseDouble(line, 23, 19));
  906.                 // Cus
  907.                 pi.irnssNav.setCus(parseDouble(line, 42, 19));
  908.                 // sqrt(A)
  909.                 pi.irnssNav.setSqrtA(parseDouble(line, 61, 19));
  910.             }

  911.             /** {@inheritDoc} */
  912.             @Override
  913.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  914.                 // Toe
  915.                 pi.irnssNav.setTime(parseDouble(line, 4, 19));
  916.                 // Cic
  917.                 pi.irnssNav.setCic(parseDouble(line, 23, 19));
  918.                 // Omega0
  919.                 pi.irnssNav.setOmega0(parseDouble(line, 42, 19));
  920.                 // Cis
  921.                 pi.irnssNav.setCis(parseDouble(line, 61, 19));
  922.             }

  923.             /** {@inheritDoc} */
  924.             @Override
  925.             public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) {
  926.                 // i0
  927.                 pi.irnssNav.setI0(parseDouble(line, 4, 19));
  928.                 // Crc
  929.                 pi.irnssNav.setCrc(parseDouble(line, 23, 19));
  930.                 // omega
  931.                 pi.irnssNav.setPa(parseDouble(line, 42, 19));
  932.                 // OMEGA DOT
  933.                 pi.irnssNav.setOmegaDot(parseDouble(line, 61, 19));
  934.             }

  935.             /** {@inheritDoc} */
  936.             @Override
  937.             public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) {
  938.                 // iDot
  939.                 pi.irnssNav.setIDot(parseDouble(line, 4, 19));
  940.                 // IRNSS week (to go with Toe)
  941.                 pi.irnssNav.setWeek((int) parseDouble(line, 42, 19));
  942.                 pi.irnssNav.setDate(new GNSSDate(pi.irnssNav.getWeek(),
  943.                                                  SEC_TO_MILLI * pi.irnssNav.getTime(),
  944.                                                  SatelliteSystem.GPS, // in Rinex files, week number is aligned to GPS week!
  945.                                                  pi.timeScales).getDate());
  946.             }

  947.             /** {@inheritDoc} */
  948.             @Override
  949.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  950.                 // SV accuracy
  951.                 pi.irnssNav.setURA(parseDouble(line, 4, 19));
  952.                 // Health
  953.                 pi.irnssNav.setSvHealth(parseDouble(line, 23, 19));
  954.                 // TGD
  955.                 pi.irnssNav.setTGD(parseDouble(line, 42, 19));
  956.             }

  957.             /** {@inheritDoc} */
  958.             @Override
  959.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  960.                 // Add the navigation message to the file
  961.                 pi.file.addIRNSSNavigationMessage(pi.irnssNav);
  962.                 // Reinitialized the container for navigation data
  963.                 pi.irnssNav = new IRNSSNavigationMessage();

  964.             }

  965.         },

  966.         /** Glonass. */
  967.         GLONASS("R") {

  968.             /** {@inheritDoc} */
  969.             @Override
  970.             public void parseFirstLine(final String line, final ParseInfo pi) {
  971.                 // PRN
  972.                 pi.glonassNav.setPRN(parseInt(line, 1, 2));

  973.                 // Toc
  974.                 final int glonassTocYear  = parseInt(line, 4, 4);
  975.                 final int glonassTocMonth = parseInt(line, 9, 2);
  976.                 final int glonassTocDay   = parseInt(line, 12, 2);
  977.                 final int glonassTocHours = parseInt(line, 15, 2);
  978.                 final int glonassTocMin   = parseInt(line, 18, 2);
  979.                 final int glonassTocSec   = parseInt(line, 21, 2);
  980.                 final AbsoluteDate date = new AbsoluteDate(glonassTocYear, glonassTocMonth, glonassTocDay, glonassTocHours, glonassTocMin, glonassTocSec, pi.timeScales.getUTC());

  981.                 pi.glonassNav.setEpochToc(date);

  982.                 // TauN (we read -TauN) and GammaN
  983.                 pi.glonassNav.setTauN(-parseDouble(line, 23, 19));
  984.                 pi.glonassNav.setGammaN(parseDouble(line, 42, 19));

  985.                 // Date
  986.                 pi.glonassNav.setDate(date.getDate());

  987.                 // Time
  988.                 pi.glonassNav.setTime(fmod(parseDouble(line, 61, 19), Constants.JULIAN_DAY));

  989.             }

  990.             /** {@inheritDoc} */
  991.             @Override
  992.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  993.                 // X
  994.                 pi.glonassNav.setX(parseDouble(line, 4, 19) * KM_TO_M);
  995.                 // Vx
  996.                 pi.glonassNav.setXDot(parseDouble(line, 23, 19) * KM_TO_M);
  997.                 // Ax
  998.                 pi.glonassNav.setXDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  999.                 // Health
  1000.                 pi.glonassNav.setHealth(parseDouble(line, 61, 19));
  1001.             }

  1002.             /** {@inheritDoc} */
  1003.             @Override
  1004.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  1005.                 // Y
  1006.                 pi.glonassNav.setY(parseDouble(line, 4, 19) * KM_TO_M);
  1007.                 // Vy
  1008.                 pi.glonassNav.setYDot(parseDouble(line, 23, 19) * KM_TO_M);
  1009.                 // Ay
  1010.                 pi.glonassNav.setYDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1011.                 // Frequency number
  1012.                 pi.glonassNav.setFrequencyNumber(parseDouble(line, 61, 19));
  1013.             }

  1014.             /** {@inheritDoc} */
  1015.             @Override
  1016.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  1017.                 // Z
  1018.                 pi.glonassNav.setZ(parseDouble(line, 4, 19) * KM_TO_M);
  1019.                 // Vz
  1020.                 pi.glonassNav.setZDot(parseDouble(line, 23, 19) * KM_TO_M);
  1021.                 // Az
  1022.                 pi.glonassNav.setZDotDot(parseDouble(line, 42, 19) * KM_TO_M);

  1023.                 // Add the navigation message to the file
  1024.                 pi.file.addGlonassNavigationMessage(pi.glonassNav);
  1025.                 // Reinitialized the container for navigation data
  1026.                 pi.glonassNav = new GLONASSNavigationMessage();
  1027.             }

  1028.             /** {@inheritDoc} */
  1029.             @Override
  1030.             public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) {
  1031.                 // Nothing to do for GLONASS
  1032.             }

  1033.             /** {@inheritDoc} */
  1034.             @Override
  1035.             public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) {
  1036.                 // Nothing to do for GLONASS
  1037.             }

  1038.             /** {@inheritDoc} */
  1039.             @Override
  1040.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  1041.                 // Nothing to do for GLONASS
  1042.             }

  1043.             /** {@inheritDoc} */
  1044.             @Override
  1045.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  1046.                 // Nothing to do for GLONASS
  1047.             }

  1048.         },

  1049.         /** SBAS. */
  1050.         SBAS("S") {

  1051.             /** {@inheritDoc} */
  1052.             @Override
  1053.             public void parseFirstLine(final String line, final ParseInfo pi) {
  1054.                 // PRN
  1055.                 pi.sbasNav.setPRN(parseInt(line, 1, 2));

  1056.                 // Toc
  1057.                 final int sbasTocYear  = parseInt(line, 4, 4);
  1058.                 final int sbasTocMonth = parseInt(line, 9, 2);
  1059.                 final int sbasTocDay   = parseInt(line, 12, 2);
  1060.                 final int sbasTocHours = parseInt(line, 15, 2);
  1061.                 final int sbasTocMin   = parseInt(line, 18, 2);
  1062.                 final int sbasTocSec   = parseInt(line, 21, 2);
  1063.                 // Time scale (UTC for Rinex 3.01 and GPS for other RINEX versions)
  1064.                 final TimeScale    timeScale = ((int) pi.version * 100 == 301) ? pi.timeScales.getUTC() : pi.timeScales.getGPS();
  1065.                 final AbsoluteDate refEpoch   = new AbsoluteDate(sbasTocYear, sbasTocMonth, sbasTocDay, sbasTocHours,
  1066.                                                                  sbasTocMin, sbasTocSec, timeScale);
  1067.                 pi.sbasNav.setEpochToc(refEpoch);

  1068.                 // AGf0 and AGf1
  1069.                 pi.sbasNav.setAGf0(parseDouble(line, 23, 19));
  1070.                 pi.sbasNav.setAGf1(parseDouble(line, 42, 19));
  1071.                 pi.sbasNav.setTime(parseDouble(line, 61, 19));

  1072.                 // Set the ephemeris epoch (same as time of clock epoch)
  1073.                 pi.sbasNav.setDate(refEpoch);
  1074.             }

  1075.             /** {@inheritDoc} */
  1076.             @Override
  1077.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  1078.                 // X
  1079.                 pi.sbasNav.setX(parseDouble(line, 4, 19) * KM_TO_M);
  1080.                 // Vx
  1081.                 pi.sbasNav.setXDot(parseDouble(line, 23, 19) * KM_TO_M);
  1082.                 // Ax
  1083.                 pi.sbasNav.setXDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1084.                 // Health
  1085.                 pi.sbasNav.setHealth(parseDouble(line, 61, 19));
  1086.             }

  1087.             /** {@inheritDoc} */
  1088.             @Override
  1089.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  1090.                 // Y
  1091.                 pi.sbasNav.setY(parseDouble(line, 4, 19) * KM_TO_M);
  1092.                 // Vy
  1093.                 pi.sbasNav.setYDot(parseDouble(line, 23, 19) * KM_TO_M);
  1094.                 // Ay
  1095.                 pi.sbasNav.setYDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1096.                 // URA
  1097.                 pi.sbasNav.setURA(parseDouble(line, 61, 19));
  1098.             }

  1099.             /** {@inheritDoc} */
  1100.             @Override
  1101.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  1102.                 // Z
  1103.                 pi.sbasNav.setZ(parseDouble(line, 4, 19) * KM_TO_M);
  1104.                 // Vz
  1105.                 pi.sbasNav.setZDot(parseDouble(line, 23, 19) * KM_TO_M);
  1106.                 // Az
  1107.                 pi.sbasNav.setZDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1108.                 // IODN
  1109.                 pi.sbasNav.setIODN(parseDouble(line, 61, 19));

  1110.                 // Add the navigation message to the file
  1111.                 pi.file.addSBASNavigationMessage(pi.sbasNav);

  1112.                 // Reinitialized the container for navigation data
  1113.                 pi.sbasNav = new SBASNavigationMessage();

  1114.             }

  1115.             /** {@inheritDoc} */
  1116.             @Override
  1117.             public void parseFourthBroadcastOrbit(final String line, final ParseInfo pi) {
  1118.                 // Nothing to do for SBAS
  1119.             }

  1120.             /** {@inheritDoc} */
  1121.             @Override
  1122.             public void parseFifthBroadcastOrbit(final String line, final ParseInfo pi) {
  1123.                 // Nothing to do for SBAS
  1124.             }

  1125.             /** {@inheritDoc} */
  1126.             @Override
  1127.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  1128.                 // Nothing to do for SBAS
  1129.             }

  1130.             /** {@inheritDoc} */
  1131.             @Override
  1132.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  1133.                 // Nothing to do for SBAS
  1134.             }

  1135.         };

  1136.         /** Parsing map. */
  1137.         private static final Map<String, SatelliteSystemLineParser> KEYS_MAP = new HashMap<>();
  1138.         static {
  1139.             for (final SatelliteSystemLineParser satelliteSystem : values()) {
  1140.                 KEYS_MAP.put(satelliteSystem.getKey(), satelliteSystem);
  1141.             }
  1142.         }

  1143.         /** Satellite system key. */
  1144.         private String key;

  1145.         /**
  1146.          * Constructor.
  1147.          * @param key satellite system key
  1148.          */
  1149.         SatelliteSystemLineParser(final String key) {
  1150.             this.key = key;
  1151.         }

  1152.         /**
  1153.          * Getter for the satellite system key.
  1154.          * @return the satellite system key
  1155.          */
  1156.         public String getKey() {
  1157.             return key;
  1158.         }

  1159.         /** Parse a string to get the satellite system.
  1160.          * <p>
  1161.          * The string first character must be the satellite system.
  1162.          * </p>
  1163.          * @param s string to parse
  1164.          * @return the satellite system
  1165.          */
  1166.         public static SatelliteSystemLineParser getSatelliteSystemLineParser(final String s) {
  1167.             return KEYS_MAP.get(s);
  1168.         }

  1169.         /**
  1170.          * Parse the first line of the navigation message.
  1171.          * @param line line to read
  1172.          * @param pi holder for transient data
  1173.          */
  1174.         public abstract void parseFirstLine(String line, ParseInfo pi);

  1175.         /**
  1176.          * Parse the "BROADCASTORBIT - 1" line.
  1177.          * @param line line to read
  1178.          * @param pi holder for transient data
  1179.          */
  1180.         public abstract void parseFirstBroadcastOrbit(String line, ParseInfo pi);

  1181.         /**
  1182.          * Parse the "BROADCASTORBIT - 2" line.
  1183.          * @param line line to read
  1184.          * @param pi holder for transient data
  1185.          */
  1186.         public abstract void parseSecondBroadcastOrbit(String line, ParseInfo pi);

  1187.         /**
  1188.          * Parse the "BROADCASTORBIT - 3" line.
  1189.          * @param line line to read
  1190.          * @param pi holder for transient data
  1191.          */
  1192.         public abstract void parseThirdBroadcastOrbit(String line, ParseInfo pi);

  1193.         /**
  1194.          * Parse the "BROADCASTORBIT - 4" line.
  1195.          * @param line line to read
  1196.          * @param pi holder for transient data
  1197.          */
  1198.         public abstract void parseFourthBroadcastOrbit(String line, ParseInfo pi);

  1199.         /**
  1200.          * Parse the "BROADCASTORBIT - 5" line.
  1201.          * @param line line to read
  1202.          * @param pi holder for transient data
  1203.          */
  1204.         public abstract void parseFifthBroadcastOrbit(String line, ParseInfo pi);

  1205.         /**
  1206.          * Parse the "BROADCASTORBIT - 6" line.
  1207.          * @param line line to read
  1208.          * @param pi holder for transient data
  1209.          */
  1210.         public abstract void parseSixthBroadcastOrbit(String line, ParseInfo pi);

  1211.         /**
  1212.          * Parse the "BROADCASTORBIT - 7" line.
  1213.          * @param line line to read
  1214.          * @param pi holder for transient data
  1215.          */
  1216.         public abstract void parseSeventhBroadcastOrbit(String line, ParseInfo pi);

  1217.         /**
  1218.          * Calculates the floating-point remainder of a / b.
  1219.          * <p>
  1220.          * fmod = a - x * b
  1221.          * where x = (int) a / b
  1222.          * </p>
  1223.          * @param a numerator
  1224.          * @param b denominator
  1225.          * @return the floating-point remainder of a / b
  1226.          */
  1227.         private static double fmod(final double a, final double b) {
  1228.             final double x = (int) (a / b);
  1229.             return a - x * b;
  1230.         }

  1231.     }

  1232. }