IgsSsrMessageType.java

  1. /* Copyright 2002-2023 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.metric.parser;

  18. import java.util.ArrayList;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.regex.Matcher;
  23. import java.util.regex.Pattern;

  24. import org.orekit.errors.OrekitException;
  25. import org.orekit.errors.OrekitMessages;
  26. import org.orekit.gnss.SatelliteSystem;
  27. import org.orekit.gnss.metric.messages.ParsedMessage;
  28. import org.orekit.gnss.metric.messages.common.ClockCorrection;
  29. import org.orekit.gnss.metric.messages.common.CodeBias;
  30. import org.orekit.gnss.metric.messages.common.OrbitCorrection;
  31. import org.orekit.gnss.metric.messages.common.PhaseBias;
  32. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm01;
  33. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm01Data;
  34. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm01Header;
  35. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm02;
  36. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm02Data;
  37. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm02Header;
  38. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm03;
  39. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm03Data;
  40. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm03Header;
  41. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm04;
  42. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm04Data;
  43. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm04Header;
  44. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm05;
  45. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm05Data;
  46. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm05Header;
  47. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm06;
  48. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm06Data;
  49. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm06Header;
  50. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm07;
  51. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm07Data;
  52. import org.orekit.gnss.metric.messages.ssr.igm.SsrIgm07Header;
  53. import org.orekit.gnss.metric.messages.ssr.subtype.SsrIm201;
  54. import org.orekit.gnss.metric.messages.ssr.subtype.SsrIm201Data;
  55. import org.orekit.gnss.metric.messages.ssr.subtype.SsrIm201Header;

  56. /** Enum containing the supported IGS SSR messages types.
  57.  *
  58.  * @author Luc Maisonobe
  59.  * @author Bryan Cazabonne
  60.  *
  61.  * @see "IGS State Space Representation (SSR) Format, Version 1.00, October 2020."
  62.  *
  63.  * @since 11.0
  64.  */
  65. public enum IgsSsrMessageType implements MessageType {

  66.     /** SSR Orbit Correction. */
  67.     IGM_01("21|41|61|81|101|121") {

  68.         /** {@inheritDoc} */
  69.         @Override
  70.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) {

  71.             // Satellite system
  72.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  73.             // Header data
  74.             final SsrIgm01Header igm01Header = new SsrIgm01Header();
  75.             igm01Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  76.             igm01Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  77.             igm01Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  78.             igm01Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  79.             igm01Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  80.             igm01Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));
  81.             igm01Header.setCrsIndicator(IgsSsrDataField.IDF006.intValue(encodedMessage));

  82.             // Number of satellites
  83.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  84.             igm01Header.setNumberOfSatellites(satNumber);

  85.             // Initialize list of data
  86.             final List<SsrIgm01Data> igm01Data = new ArrayList<>();

  87.             // Loop on satellites and fill data
  88.             for (int index = 0; index < satNumber; index++) {

  89.                 // Satellite ID
  90.                 final int igm01SatId = getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage));

  91.                 // GNSS IOD
  92.                 final int igm01Iod = IgsSsrDataField.IDF012.intValue(encodedMessage);

  93.                 // Orbit correction
  94.                 final OrbitCorrection igm01OrbitCorr =
  95.                                 new OrbitCorrection(IgsSsrDataField.IDF013.doubleValue(encodedMessage),   // IGM01 dRadial
  96.                                                     IgsSsrDataField.IDF014.doubleValue(encodedMessage),   // IGM01 dAlongTrack
  97.                                                     IgsSsrDataField.IDF015.doubleValue(encodedMessage),   // IGM01 dCrossTrack
  98.                                                     IgsSsrDataField.IDF016.doubleValue(encodedMessage),   // IGM01 dRadialDot
  99.                                                     IgsSsrDataField.IDF017.doubleValue(encodedMessage),   // IGM01 dAlongTrackDot
  100.                                                     IgsSsrDataField.IDF018.doubleValue(encodedMessage));  // IGM01 dCrossTrackDot

  101.                 // Initialize a new container and fill data
  102.                 final SsrIgm01Data currentIgm01Data = new SsrIgm01Data();
  103.                 currentIgm01Data.setSatelliteID(igm01SatId);
  104.                 currentIgm01Data.setGnssIod(igm01Iod);
  105.                 currentIgm01Data.setOrbitCorrection(igm01OrbitCorr);

  106.                 // Update the list
  107.                 igm01Data.add(currentIgm01Data);

  108.             }

  109.             // Return the parsed message
  110.             return new SsrIgm01(messageNumber, system, igm01Header, igm01Data);

  111.         }

  112.     },

  113.     /** SSR Clock Correction. */
  114.     IGM_02("22|42|62|82|102|122") {

  115.         /** {@inheritDoc} */
  116.         @Override
  117.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) {

  118.             // Satellite system
  119.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  120.             // Header data
  121.             final SsrIgm02Header igm02Header = new SsrIgm02Header();
  122.             igm02Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  123.             igm02Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  124.             igm02Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  125.             igm02Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  126.             igm02Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  127.             igm02Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));

  128.             // Number of satellites
  129.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  130.             igm02Header.setNumberOfSatellites(satNumber);

  131.             // Initialize list of data
  132.             final List<SsrIgm02Data> igm02Data = new ArrayList<>();

  133.             // Loop on satellites and fill data
  134.             for (int index = 0; index < satNumber; index++) {

  135.                 // Satellite ID
  136.                 final int igm02SatId = getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage));

  137.                 // Clock correction
  138.                 final ClockCorrection igm02ClockCorr =
  139.                                 new ClockCorrection(IgsSsrDataField.IDF019.doubleValue(encodedMessage),  // IGM02 C0
  140.                                                     IgsSsrDataField.IDF020.doubleValue(encodedMessage),  // IGM02 C1
  141.                                                     IgsSsrDataField.IDF021.doubleValue(encodedMessage)); // IGM02 C2

  142.                 // Initialize a new container and fill data
  143.                 final SsrIgm02Data currentIgm02Data = new SsrIgm02Data();
  144.                 currentIgm02Data.setSatelliteID(igm02SatId);
  145.                 currentIgm02Data.setClockCorrection(igm02ClockCorr);

  146.                 // Update the list
  147.                 igm02Data.add(currentIgm02Data);

  148.             }

  149.             // Return the parsed message
  150.             return new SsrIgm02(messageNumber, system, igm02Header, igm02Data);

  151.         }

  152.     },

  153.     /** SSR Combined Orbit and Clock Correction. */
  154.     IGM_03("23|43|63|83|103|123") {

  155.         /** {@inheritDoc} */
  156.         @Override
  157.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) {

  158.             // Satellite system
  159.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  160.             // Header data
  161.             final SsrIgm03Header igm03Header = new SsrIgm03Header();
  162.             igm03Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  163.             igm03Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  164.             igm03Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  165.             igm03Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  166.             igm03Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  167.             igm03Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));
  168.             igm03Header.setCrsIndicator(IgsSsrDataField.IDF006.intValue(encodedMessage));

  169.             // Number of satellites
  170.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  171.             igm03Header.setNumberOfSatellites(satNumber);

  172.             // Initialize list of data
  173.             final List<SsrIgm03Data> igm03Data = new ArrayList<>();

  174.             // Loop on satellites and fill data
  175.             for (int index = 0; index < satNumber; index++) {

  176.                 // Satellite ID
  177.                 final int igm03SatId = getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage));

  178.                 // GNSS IOD
  179.                 final int igm03Iod = IgsSsrDataField.IDF012.intValue(encodedMessage);

  180.                 // Orbit correction
  181.                 final OrbitCorrection igm03OrbitCorr =
  182.                                 new OrbitCorrection(IgsSsrDataField.IDF013.doubleValue(encodedMessage),   // IGM03 dRadial
  183.                                                     IgsSsrDataField.IDF014.doubleValue(encodedMessage),   // IGM03 dAlongTrack
  184.                                                     IgsSsrDataField.IDF015.doubleValue(encodedMessage),   // IGM03 dCrossTrack
  185.                                                     IgsSsrDataField.IDF016.doubleValue(encodedMessage),   // IGM03 dRadialDot
  186.                                                     IgsSsrDataField.IDF017.doubleValue(encodedMessage),   // IGM03 dAlongTrackDot
  187.                                                     IgsSsrDataField.IDF018.doubleValue(encodedMessage));  // IGM03 dCrossTrackDot

  188.                 // Clock correction
  189.                 final ClockCorrection igm03ClockCorr =
  190.                                 new ClockCorrection(IgsSsrDataField.IDF019.doubleValue(encodedMessage),  // IGM03 C0
  191.                                                     IgsSsrDataField.IDF020.doubleValue(encodedMessage),  // IGM03 C1
  192.                                                     IgsSsrDataField.IDF021.doubleValue(encodedMessage)); // IGM03 C2

  193.                 // Initialize a new container and fill data
  194.                 final SsrIgm03Data currentIgm03Data = new SsrIgm03Data();
  195.                 currentIgm03Data.setSatelliteID(igm03SatId);
  196.                 currentIgm03Data.setGnssIod(igm03Iod);
  197.                 currentIgm03Data.setOrbitCorrection(igm03OrbitCorr);
  198.                 currentIgm03Data.setClockCorrection(igm03ClockCorr);

  199.                 // Update the list
  200.                 igm03Data.add(currentIgm03Data);

  201.             }

  202.             // Return the parsed message
  203.             return new SsrIgm03(messageNumber, system, igm03Header, igm03Data);

  204.         }

  205.     },

  206.     /** SSR High Rate Clock Correction. */
  207.     IGM_04("24|44|64|84|104|124") {

  208.         /** {@inheritDoc} */
  209.         @Override
  210.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) {

  211.             // Satellite system
  212.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  213.             // Header data
  214.             final SsrIgm04Header igm04Header = new SsrIgm04Header();
  215.             igm04Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  216.             igm04Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  217.             igm04Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  218.             igm04Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  219.             igm04Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  220.             igm04Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));

  221.             // Number of satellites
  222.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  223.             igm04Header.setNumberOfSatellites(satNumber);

  224.             // Initialize list of data
  225.             final List<SsrIgm04Data> igm04Data = new ArrayList<>();

  226.             // Loop on satellites and fill data
  227.             for (int index = 0; index < satNumber; index++) {

  228.                 // Initialize a new container
  229.                 final SsrIgm04Data currentIgm04Data = new SsrIgm04Data();
  230.                 currentIgm04Data.setSatelliteID(getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage)));
  231.                 currentIgm04Data.setHighRateClockCorrection(IgsSsrDataField.IDF022.doubleValue(encodedMessage));

  232.                 // Update the list
  233.                 igm04Data.add(currentIgm04Data);

  234.             }

  235.             // Return the parsed message
  236.             return new SsrIgm04(messageNumber, system, igm04Header, igm04Data);

  237.         }

  238.     },

  239.     /** SSR Code Bias. */
  240.     IGM_05("25|45|65|85|105|125") {

  241.         /** {@inheritDoc} */
  242.         @Override
  243.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) {

  244.             // Satellite system
  245.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  246.             // Header data
  247.             final SsrIgm05Header igm05Header = new SsrIgm05Header();
  248.             igm05Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  249.             igm05Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  250.             igm05Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  251.             igm05Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  252.             igm05Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  253.             igm05Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));

  254.             // Number of satellites
  255.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  256.             igm05Header.setNumberOfSatellites(satNumber);

  257.             // Initialize list of data
  258.             final List<SsrIgm05Data> igm05Data = new ArrayList<>();

  259.             // Loop on satellites
  260.             for (int index = 0; index < satNumber; index++) {

  261.                 // Initialize a new container
  262.                 final SsrIgm05Data currentIgm05Data = new SsrIgm05Data();
  263.                 currentIgm05Data.setSatelliteID(getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage)));

  264.                 // Number of biases
  265.                 final int biasesNumber = IgsSsrDataField.IDF023.intValue(encodedMessage);
  266.                 currentIgm05Data.setNumberOfBiasesProcessed(biasesNumber);

  267.                 // Loop on biases
  268.                 for (int biasIndex = 0; biasIndex < biasesNumber; biasIndex++) {
  269.                     // Initialize a new code bias
  270.                     final CodeBias codeBias = new CodeBias(IgsSsrDataField.IDF024.intValue(encodedMessage),
  271.                                                            IgsSsrDataField.IDF025.doubleValue(encodedMessage));
  272.                     // Add the codeBias to the container
  273.                     currentIgm05Data.addCodeBias(codeBias);
  274.                 }

  275.                 // Update the list of data
  276.                 igm05Data.add(currentIgm05Data);

  277.             }

  278.             // Return the parsed message
  279.             return new SsrIgm05(messageNumber, system, igm05Header, igm05Data);

  280.         }

  281.     },

  282.     /** SSR Phase Bias. */
  283.     IGM_06("26|46|66|86|106|126") {

  284.         /** {@inheritDoc} */
  285.         @Override
  286.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) {

  287.             // Satellite system
  288.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  289.             // Header data
  290.             final SsrIgm06Header igm06Header = new SsrIgm06Header();
  291.             igm06Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  292.             igm06Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  293.             igm06Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  294.             igm06Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  295.             igm06Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  296.             igm06Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));
  297.             igm06Header.setIsConsistencyMaintained(IgsSsrDataField.IDF032.booleanValue(encodedMessage));
  298.             igm06Header.setIsMelbourneWubbenaConsistencyMaintained(IgsSsrDataField.IDF033.booleanValue(encodedMessage));

  299.             // Number of satellites
  300.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  301.             igm06Header.setNumberOfSatellites(satNumber);

  302.             // Initialize list of data
  303.             final List<SsrIgm06Data> igm06Data = new ArrayList<>();

  304.             // Loop on satellites
  305.             for (int index = 0; index < satNumber; index++) {

  306.                 // Initialize a new container
  307.                 final SsrIgm06Data currentIgm06Data = new SsrIgm06Data();
  308.                 currentIgm06Data.setSatelliteID(getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage)));

  309.                 // Number of biases
  310.                 final int biasesNumber = IgsSsrDataField.IDF023.intValue(encodedMessage);
  311.                 currentIgm06Data.setNumberOfBiasesProcessed(biasesNumber);

  312.                 // Yaw angle and rate
  313.                 currentIgm06Data.setYawAngle(IgsSsrDataField.IDF026.doubleValue(encodedMessage));
  314.                 currentIgm06Data.setYawRate(IgsSsrDataField.IDF027.doubleValue(encodedMessage));

  315.                 // Loop on biases
  316.                 for (int biasIndex = 0; biasIndex < biasesNumber; biasIndex++) {
  317.                     // Initialize a new phase bias
  318.                     final PhaseBias phaseBias = new PhaseBias(IgsSsrDataField.IDF024.intValue(encodedMessage),
  319.                                                               IgsSsrDataField.IDF029.booleanValue(encodedMessage),
  320.                                                               IgsSsrDataField.IDF030.intValue(encodedMessage),
  321.                                                               IgsSsrDataField.IDF031.intValue(encodedMessage),
  322.                                                               IgsSsrDataField.IDF028.doubleValue(encodedMessage));
  323.                     // Add the codeBias to the container
  324.                     currentIgm06Data.addPhaseBias(phaseBias);
  325.                 }

  326.                 // Update the list of data
  327.                 igm06Data.add(currentIgm06Data);

  328.             }

  329.             // Return the parsed message
  330.             return new SsrIgm06(messageNumber, system, igm06Header, igm06Data);

  331.         }

  332.     },

  333.     /** SSR URA. */
  334.     IGM_07("27|47|67|87|107|127") {

  335.         /** {@inheritDoc} */
  336.         @Override
  337.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) {

  338.             // Satellite system
  339.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  340.             // Header data
  341.             final SsrIgm07Header igm07Header = new SsrIgm07Header();
  342.             igm07Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  343.             igm07Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  344.             igm07Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  345.             igm07Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  346.             igm07Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  347.             igm07Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));

  348.             // Number of satellites
  349.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  350.             igm07Header.setNumberOfSatellites(satNumber);

  351.             // Initialize list of data
  352.             final List<SsrIgm07Data> igm07Data = new ArrayList<>();

  353.             // Loop on satellites and fill data
  354.             for (int index = 0; index < satNumber; index++) {

  355.                 // Initialize a new container
  356.                 final SsrIgm07Data currentIgm07Data = new SsrIgm07Data();
  357.                 currentIgm07Data.setSatelliteID(getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage)));
  358.                 currentIgm07Data.setSsrUra(IgsSsrDataField.IDF034.intValue(encodedMessage));

  359.                 // Update the list
  360.                 igm07Data.add(currentIgm07Data);

  361.             }

  362.             // Return the parsed message
  363.             return new SsrIgm07(messageNumber, system, igm07Header, igm07Data);

  364.         }

  365.     },

  366.     /** SSR Ionosphere VTEC Spherical Harmonics Message. */
  367.     IM_201("201") {

  368.         /** {@inheritDoc} */
  369.         @Override
  370.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber) {

  371.             // Header data
  372.             final SsrIm201Header im201Header = new SsrIm201Header();
  373.             im201Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  374.             im201Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  375.             im201Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  376.             im201Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  377.             im201Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  378.             im201Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));
  379.             im201Header.setVtecQualityIndicator(IgsSsrDataField.IDF041.doubleValue(encodedMessage));

  380.             // Number of ionospheric layers
  381.             final int numberOfIonosphericLayers = IgsSsrDataField.IDF035.intValue(encodedMessage);
  382.             im201Header.setNumberOfIonosphericLayers(numberOfIonosphericLayers);

  383.             // Initialize list of data
  384.             final List<SsrIm201Data> im201Data = new ArrayList<>();

  385.             // Loop on ionospheric layers
  386.             for (int layerIndex = 0; layerIndex < numberOfIonosphericLayers; layerIndex++) {

  387.                 // Initialize a new container
  388.                 final SsrIm201Data currentIm201Data = new SsrIm201Data();

  389.                 // Height of the ionospheric layer
  390.                 currentIm201Data.setHeightIonosphericLayer(IgsSsrDataField.IDF036.doubleValue(encodedMessage));

  391.                 // Degree and order of spherical harmonics
  392.                 final int n = IgsSsrDataField.IDF037.intValue(encodedMessage);
  393.                 final int m = IgsSsrDataField.IDF038.intValue(encodedMessage);

  394.                 // Initialize arrays
  395.                 final double[][] cnm = new double[n + 1][m + 1];
  396.                 final double[][] snm = new double[n + 1][m + 1];

  397.                 ////
  398.                 // Cosine coefficients
  399.                 ////

  400.                 // Loop on degree
  401.                 for (int order = 0; order <= m; order++) {
  402.                     // Loop on order
  403.                     for (int degree = order; degree <= n; degree++) {
  404.                         cnm[degree][order] = IgsSsrDataField.IDF039.doubleValue(encodedMessage);
  405.                     }
  406.                 }

  407.                 ////
  408.                 // Sine coefficients
  409.                 ////

  410.                 // Loop on degree
  411.                 for (int order = 1; order <= m; order++) {
  412.                     // Loop on order
  413.                     for (int degree = order; degree <= n; degree++) {
  414.                         snm[degree][order] = IgsSsrDataField.IDF040.doubleValue(encodedMessage);
  415.                     }
  416.                 }

  417.                 currentIm201Data.setSphericalHarmonicsDegree(n);
  418.                 currentIm201Data.setSphericalHarmonicsOrder(m);
  419.                 currentIm201Data.setCnm(cnm);
  420.                 currentIm201Data.setSnm(snm);

  421.                 // Update the list
  422.                 im201Data.add(currentIm201Data);

  423.             }

  424.             // Return the parsed message
  425.             return new SsrIm201(messageNumber, im201Header, im201Data);

  426.         }

  427.     };

  428.     /** Codes map. */
  429.     private static final Map<Pattern, IgsSsrMessageType> CODES_MAP = new HashMap<>();
  430.     static {
  431.         for (final IgsSsrMessageType type : values()) {
  432.             CODES_MAP.put(type.getPattern(), type);
  433.         }
  434.     }

  435.     /** Message pattern (i.e. allowed message numbers). */
  436.     private final Pattern pattern;

  437.     /** Simple constructor.
  438.      * @param regex message regular expression
  439.      */
  440.     IgsSsrMessageType(final String regex) {
  441.         this.pattern = Pattern.compile(regex);
  442.     }

  443.     /** Get the message number.
  444.      * @return message number
  445.      */
  446.     public Pattern getPattern() {
  447.         return pattern;
  448.     }

  449.     /** Get the message type corresponding to a message number.
  450.      * @param number message number
  451.      * @return the message type corresponding to the message number
  452.      */
  453.     public static IgsSsrMessageType getMessageType(final String number) {
  454.         // Try to find a match with an existing message type
  455.         for (Map.Entry<Pattern, IgsSsrMessageType> entry : CODES_MAP.entrySet()) {
  456.             // Matcher
  457.             final Matcher matcher = entry.getKey().matcher(number);
  458.             // Check the match !
  459.             if (matcher.matches()) {
  460.                 // return the message type
  461.                 return entry.getValue();
  462.             }
  463.         }
  464.         // No match found
  465.         throw new OrekitException(OrekitMessages.UNKNOWN_ENCODED_MESSAGE_NUMBER, number);
  466.     }

  467.     /**
  468.      * Find the satellite system corresponding to the sub-type message number.
  469.      * <p>
  470.      * See Table 5 of reference
  471.      * </p>
  472.      * @param subTypeMessage message umber
  473.      * @return the corresponding satellite system
  474.      */
  475.     public static SatelliteSystem messageNumberToSatelliteSystem(final int subTypeMessage) {

  476.         if (subTypeMessage > 20 && subTypeMessage <= 40) {
  477.             // GPS messages
  478.             return SatelliteSystem.GPS;
  479.         } else if (subTypeMessage <= 60) {
  480.             // GLONASS messages
  481.             return SatelliteSystem.GLONASS;
  482.         } else if (subTypeMessage <= 80) {
  483.             // Galileo messages
  484.             return SatelliteSystem.GALILEO;
  485.         } else if (subTypeMessage <= 100) {
  486.             // QZSS messages
  487.             return SatelliteSystem.QZSS;
  488.         } else if (subTypeMessage <= 120) {
  489.             // Beidou messages
  490.             return SatelliteSystem.BEIDOU;
  491.         } else if (subTypeMessage <= 140) {
  492.             // SBAS messages
  493.             return SatelliteSystem.SBAS;
  494.         } else {
  495.             // IRNSS messages
  496.             return SatelliteSystem.IRNSS;
  497.         }

  498.     }

  499.     /**
  500.      * Transform the satellite ID parsed from the IGS SSR message to the real ID.
  501.      * @param system the satellite system of the parsed message
  502.      * @param id the parsed satellite ID
  503.      * @return the real satellite ID
  504.      */
  505.     public static int getSatelliteId(final SatelliteSystem system, final int id) {

  506.         // Switch on satellite systems
  507.         switch (system) {
  508.             case QZSS:
  509.                 // ID = ID(parsed) + 192
  510.                 return id + 192;
  511.             case SBAS:
  512.                 // ID = ID(parsed) + 119
  513.                 return id + 119;
  514.             default:
  515.                 // For GPS, GLONASS, Beidou, and Galileo the id is unchanged
  516.                 return id;
  517.         }

  518.     }

  519. }