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

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

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

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

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

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

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

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

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

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

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

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

  127.         return pi.file;

  128.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  204.     }

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

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

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

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

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

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

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

  224.             }

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

  230.         },

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

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

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

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

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

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

  259.             }

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

  265.         },

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

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

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

  278.         },

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

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

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

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

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

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

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

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

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

  303.                     } else {

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

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

  308.                     }

  309.                 }

  310.             }

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

  316.         },

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

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

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

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

  331.             }

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

  337.         },

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

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

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

  351.         },

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

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

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

  364.         },

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

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

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

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

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

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

  378.             }

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

  384.         },

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

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

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

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

  415.             }

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

  421.         };

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

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

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

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

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

  446.     }

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

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

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

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

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

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

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

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

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

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

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

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

  552.         },

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

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

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

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

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

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

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

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

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

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

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

  656.         },

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

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

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

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

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

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

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

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

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

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

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

  761.             }

  762.         },

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

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

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

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

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

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

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

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

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

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

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

  866.         },

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

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

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

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

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

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

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

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

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

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

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

  965.             }

  966.         },

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

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

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

  983.                 // Build a GPS date
  984.                 final GNSSDate gpsEpoch = new GNSSDate(date, SatelliteSystem.GPS, pi.timeScales);

  985.                 // Toc rounded by 15 min in UTC
  986.                 final double secInWeek = FastMath.floor((0.001 * gpsEpoch.getMilliInWeek() + 450.0) / 900.0) * 900.0;
  987.                 final AbsoluteDate rounded = new GNSSDate(gpsEpoch.getWeekNumber(),
  988.                                                           SEC_TO_MILLI * secInWeek,
  989.                                                           SatelliteSystem.GPS, pi.timeScales).getDate();

  990.                 pi.glonassNav.setEpochToc(rounded);

  991.                 // TauN (we read -TauN) and GammaN
  992.                 pi.glonassNav.setTauN(-parseDouble(line, 23, 19));
  993.                 pi.glonassNav.setGammaN(parseDouble(line, 42, 19));

  994.                 // Date
  995.                 pi.glonassNav.setDate(rounded);

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

  998.             }

  999.             /** {@inheritDoc} */
  1000.             @Override
  1001.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  1002.                 // X
  1003.                 pi.glonassNav.setX(parseDouble(line, 4, 19) * KM_TO_M);
  1004.                 // Vx
  1005.                 pi.glonassNav.setXDot(parseDouble(line, 23, 19) * KM_TO_M);
  1006.                 // Ax
  1007.                 pi.glonassNav.setXDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1008.                 // Health
  1009.                 pi.glonassNav.setHealth(parseDouble(line, 61, 19));
  1010.             }

  1011.             /** {@inheritDoc} */
  1012.             @Override
  1013.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  1014.                 // Y
  1015.                 pi.glonassNav.setY(parseDouble(line, 4, 19) * KM_TO_M);
  1016.                 // Vy
  1017.                 pi.glonassNav.setYDot(parseDouble(line, 23, 19) * KM_TO_M);
  1018.                 // Ay
  1019.                 pi.glonassNav.setYDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1020.                 // Frequency number
  1021.                 pi.glonassNav.setFrequencyNumber(parseDouble(line, 61, 19));
  1022.             }

  1023.             /** {@inheritDoc} */
  1024.             @Override
  1025.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  1026.                 // Z
  1027.                 pi.glonassNav.setZ(parseDouble(line, 4, 19) * KM_TO_M);
  1028.                 // Vz
  1029.                 pi.glonassNav.setZDot(parseDouble(line, 23, 19) * KM_TO_M);
  1030.                 // Az
  1031.                 pi.glonassNav.setZDotDot(parseDouble(line, 42, 19) * KM_TO_M);

  1032.                 // Add the navigation message to the file
  1033.                 pi.file.addGlonassNavigationMessage(pi.glonassNav);
  1034.                 // Reinitialized the container for navigation data
  1035.                 pi.glonassNav = new GLONASSNavigationMessage();
  1036.             }

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

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

  1047.             /** {@inheritDoc} */
  1048.             @Override
  1049.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  1050.                 // Nothing to do for GLONASS
  1051.             }

  1052.             /** {@inheritDoc} */
  1053.             @Override
  1054.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  1055.                 // Nothing to do for GLONASS
  1056.             }

  1057.         },

  1058.         /** SBAS. */
  1059.         SBAS("S") {

  1060.             /** {@inheritDoc} */
  1061.             @Override
  1062.             public void parseFirstLine(final String line, final ParseInfo pi) {
  1063.                 // PRN
  1064.                 pi.sbasNav.setPRN(parseInt(line, 1, 2));

  1065.                 // Toc
  1066.                 final int sbasTocYear  = parseInt(line, 4, 4);
  1067.                 final int sbasTocMonth = parseInt(line, 9, 2);
  1068.                 final int sbasTocDay   = parseInt(line, 12, 2);
  1069.                 final int sbasTocHours = parseInt(line, 15, 2);
  1070.                 final int sbasTocMin   = parseInt(line, 18, 2);
  1071.                 final int sbasTocSec   = parseInt(line, 21, 2);
  1072.                 // Time scale (UTC for Rinex 3.01 and GPS for other RINEX versions)
  1073.                 final TimeScale    timeScale = ((int) pi.version * 100 == 301) ? pi.timeScales.getUTC() : pi.timeScales.getGPS();
  1074.                 final AbsoluteDate refEpoch   = new AbsoluteDate(sbasTocYear, sbasTocMonth, sbasTocDay, sbasTocHours,
  1075.                                                                  sbasTocMin, sbasTocSec, timeScale);
  1076.                 pi.sbasNav.setEpochToc(refEpoch);

  1077.                 // AGf0 and AGf1
  1078.                 pi.sbasNav.setAGf0(parseDouble(line, 23, 19));
  1079.                 pi.sbasNav.setAGf1(parseDouble(line, 42, 19));
  1080.                 pi.sbasNav.setTime(parseDouble(line, 61, 19));

  1081.                 // Set the ephemeris epoch (same as time of clock epoch)
  1082.                 pi.sbasNav.setDate(refEpoch);
  1083.             }

  1084.             /** {@inheritDoc} */
  1085.             @Override
  1086.             public void parseFirstBroadcastOrbit(final String line, final ParseInfo pi) {
  1087.                 // X
  1088.                 pi.sbasNav.setX(parseDouble(line, 4, 19) * KM_TO_M);
  1089.                 // Vx
  1090.                 pi.sbasNav.setXDot(parseDouble(line, 23, 19) * KM_TO_M);
  1091.                 // Ax
  1092.                 pi.sbasNav.setXDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1093.                 // Health
  1094.                 pi.sbasNav.setHealth(parseDouble(line, 61, 19));
  1095.             }

  1096.             /** {@inheritDoc} */
  1097.             @Override
  1098.             public void parseSecondBroadcastOrbit(final String line, final ParseInfo pi) {
  1099.                 // Y
  1100.                 pi.sbasNav.setY(parseDouble(line, 4, 19) * KM_TO_M);
  1101.                 // Vy
  1102.                 pi.sbasNav.setYDot(parseDouble(line, 23, 19) * KM_TO_M);
  1103.                 // Ay
  1104.                 pi.sbasNav.setYDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1105.                 // URA
  1106.                 pi.sbasNav.setURA(parseDouble(line, 61, 19));
  1107.             }

  1108.             /** {@inheritDoc} */
  1109.             @Override
  1110.             public void parseThirdBroadcastOrbit(final String line, final ParseInfo pi) {
  1111.                 // Z
  1112.                 pi.sbasNav.setZ(parseDouble(line, 4, 19) * KM_TO_M);
  1113.                 // Vz
  1114.                 pi.sbasNav.setZDot(parseDouble(line, 23, 19) * KM_TO_M);
  1115.                 // Az
  1116.                 pi.sbasNav.setZDotDot(parseDouble(line, 42, 19) * KM_TO_M);
  1117.                 // IODN
  1118.                 pi.sbasNav.setIODN(parseDouble(line, 61, 19));

  1119.                 // Add the navigation message to the file
  1120.                 pi.file.addSBASNavigationMessage(pi.sbasNav);

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

  1123.             }

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

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

  1134.             /** {@inheritDoc} */
  1135.             @Override
  1136.             public void parseSixthBroadcastOrbit(final String line, final ParseInfo pi) {
  1137.                 // Nothing to do for SBAS
  1138.             }

  1139.             /** {@inheritDoc} */
  1140.             @Override
  1141.             public void parseSeventhBroadcastOrbit(final String line, final ParseInfo pi) {
  1142.                 // Nothing to do for SBAS
  1143.             }

  1144.         };

  1145.         /** Parsing map. */
  1146.         private static final Map<String, SatelliteSystemLineParser> KEYS_MAP = new HashMap<>();
  1147.         static {
  1148.             for (final SatelliteSystemLineParser satelliteSystem : values()) {
  1149.                 KEYS_MAP.put(satelliteSystem.getKey(), satelliteSystem);
  1150.             }
  1151.         }

  1152.         /** Satellite system key. */
  1153.         private String key;

  1154.         /**
  1155.          * Constructor.
  1156.          * @param key satellite system key
  1157.          */
  1158.         SatelliteSystemLineParser(final String key) {
  1159.             this.key = key;
  1160.         }

  1161.         /**
  1162.          * Getter for the satellite system key.
  1163.          * @return the satellite system key
  1164.          */
  1165.         public String getKey() {
  1166.             return key;
  1167.         }

  1168.         /** Parse a string to get the satellite system.
  1169.          * <p>
  1170.          * The string first character must be the satellite system.
  1171.          * </p>
  1172.          * @param s string to parse
  1173.          * @return the satellite system
  1174.          */
  1175.         public static SatelliteSystemLineParser getSatelliteSystemLineParser(final String s) {
  1176.             return KEYS_MAP.get(s);
  1177.         }

  1178.         /**
  1179.          * Parse the first line of the navigation message.
  1180.          * @param line line to read
  1181.          * @param pi holder for transient data
  1182.          */
  1183.         public abstract void parseFirstLine(String line, ParseInfo pi);

  1184.         /**
  1185.          * Parse the "BROADCASTORBIT - 1" line.
  1186.          * @param line line to read
  1187.          * @param pi holder for transient data
  1188.          */
  1189.         public abstract void parseFirstBroadcastOrbit(String line, ParseInfo pi);

  1190.         /**
  1191.          * Parse the "BROADCASTORBIT - 2" line.
  1192.          * @param line line to read
  1193.          * @param pi holder for transient data
  1194.          */
  1195.         public abstract void parseSecondBroadcastOrbit(String line, ParseInfo pi);

  1196.         /**
  1197.          * Parse the "BROADCASTORBIT - 3" line.
  1198.          * @param line line to read
  1199.          * @param pi holder for transient data
  1200.          */
  1201.         public abstract void parseThirdBroadcastOrbit(String line, ParseInfo pi);

  1202.         /**
  1203.          * Parse the "BROADCASTORBIT - 4" line.
  1204.          * @param line line to read
  1205.          * @param pi holder for transient data
  1206.          */
  1207.         public abstract void parseFourthBroadcastOrbit(String line, ParseInfo pi);

  1208.         /**
  1209.          * Parse the "BROADCASTORBIT - 5" line.
  1210.          * @param line line to read
  1211.          * @param pi holder for transient data
  1212.          */
  1213.         public abstract void parseFifthBroadcastOrbit(String line, ParseInfo pi);

  1214.         /**
  1215.          * Parse the "BROADCASTORBIT - 6" line.
  1216.          * @param line line to read
  1217.          * @param pi holder for transient data
  1218.          */
  1219.         public abstract void parseSixthBroadcastOrbit(String line, ParseInfo pi);

  1220.         /**
  1221.          * Parse the "BROADCASTORBIT - 7" line.
  1222.          * @param line line to read
  1223.          * @param pi holder for transient data
  1224.          */
  1225.         public abstract void parseSeventhBroadcastOrbit(String line, ParseInfo pi);

  1226.         /**
  1227.          * Calculates the floating-point remainder of a / b.
  1228.          * <p>
  1229.          * fmod = a - x * b
  1230.          * where x = (int) a / b
  1231.          * </p>
  1232.          * @param a numerator
  1233.          * @param b denominator
  1234.          * @return the floating-point remainder of a / b
  1235.          */
  1236.         private static double fmod(final double a, final double b) {
  1237.             final double x = (int) (a / b);
  1238.             return a - x * b;
  1239.         }

  1240.     }

  1241. }