IgsSsrMessageType.java

  1. /* Copyright 2002-2025 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. import org.orekit.time.TimeScales;

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

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

  69.         /** {@inheritDoc} */
  70.         @Override
  71.         public ParsedMessage parse(final EncodedMessage encodedMessage,
  72.                                    final int messageNumber,
  73.                                    final TimeScales timeScales) {

  74.             // Satellite system
  75.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

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

  85.             // Number of satellites
  86.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  87.             igm01Header.setNumberOfSatellites(satNumber);

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

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

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

  94.                 // GNSS IOD
  95.                 final int igm01Iod = IgsSsrDataField.IDF012.intValue(encodedMessage);

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

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

  109.                 // Update the list
  110.                 igm01Data.add(currentIgm01Data);

  111.             }

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

  114.         }

  115.     },

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

  118.         /** {@inheritDoc} */
  119.         @Override
  120.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber,
  121.                                    final TimeScales timeScales) {

  122.             // Satellite system
  123.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  124.             // Header data
  125.             final SsrIgm02Header igm02Header = new SsrIgm02Header();
  126.             igm02Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  127.             igm02Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  128.             igm02Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  129.             igm02Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  130.             igm02Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  131.             igm02Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));

  132.             // Number of satellites
  133.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  134.             igm02Header.setNumberOfSatellites(satNumber);

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

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

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

  141.                 // Clock correction
  142.                 final ClockCorrection igm02ClockCorr =
  143.                                 new ClockCorrection(IgsSsrDataField.IDF019.doubleValue(encodedMessage),  // IGM02 C0
  144.                                                     IgsSsrDataField.IDF020.doubleValue(encodedMessage),  // IGM02 C1
  145.                                                     IgsSsrDataField.IDF021.doubleValue(encodedMessage)); // IGM02 C2

  146.                 // Initialize a new container and fill data
  147.                 final SsrIgm02Data currentIgm02Data = new SsrIgm02Data();
  148.                 currentIgm02Data.setSatelliteID(igm02SatId);
  149.                 currentIgm02Data.setClockCorrection(igm02ClockCorr);

  150.                 // Update the list
  151.                 igm02Data.add(currentIgm02Data);

  152.             }

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

  155.         }

  156.     },

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

  159.         /** {@inheritDoc} */
  160.         @Override
  161.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber,
  162.                                    final TimeScales timeScales) {

  163.             // Satellite system
  164.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  165.             // Header data
  166.             final SsrIgm03Header igm03Header = new SsrIgm03Header();
  167.             igm03Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  168.             igm03Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  169.             igm03Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  170.             igm03Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  171.             igm03Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  172.             igm03Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));
  173.             igm03Header.setCrsIndicator(IgsSsrDataField.IDF006.intValue(encodedMessage));

  174.             // Number of satellites
  175.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  176.             igm03Header.setNumberOfSatellites(satNumber);

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

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

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

  183.                 // GNSS IOD
  184.                 final int igm03Iod = IgsSsrDataField.IDF012.intValue(encodedMessage);

  185.                 // Orbit correction
  186.                 final OrbitCorrection igm03OrbitCorr =
  187.                                 new OrbitCorrection(IgsSsrDataField.IDF013.doubleValue(encodedMessage),   // IGM03 dRadial
  188.                                                     IgsSsrDataField.IDF014.doubleValue(encodedMessage),   // IGM03 dAlongTrack
  189.                                                     IgsSsrDataField.IDF015.doubleValue(encodedMessage),   // IGM03 dCrossTrack
  190.                                                     IgsSsrDataField.IDF016.doubleValue(encodedMessage),   // IGM03 dRadialDot
  191.                                                     IgsSsrDataField.IDF017.doubleValue(encodedMessage),   // IGM03 dAlongTrackDot
  192.                                                     IgsSsrDataField.IDF018.doubleValue(encodedMessage));  // IGM03 dCrossTrackDot

  193.                 // Clock correction
  194.                 final ClockCorrection igm03ClockCorr =
  195.                                 new ClockCorrection(IgsSsrDataField.IDF019.doubleValue(encodedMessage),  // IGM03 C0
  196.                                                     IgsSsrDataField.IDF020.doubleValue(encodedMessage),  // IGM03 C1
  197.                                                     IgsSsrDataField.IDF021.doubleValue(encodedMessage)); // IGM03 C2

  198.                 // Initialize a new container and fill data
  199.                 final SsrIgm03Data currentIgm03Data = new SsrIgm03Data();
  200.                 currentIgm03Data.setSatelliteID(igm03SatId);
  201.                 currentIgm03Data.setGnssIod(igm03Iod);
  202.                 currentIgm03Data.setOrbitCorrection(igm03OrbitCorr);
  203.                 currentIgm03Data.setClockCorrection(igm03ClockCorr);

  204.                 // Update the list
  205.                 igm03Data.add(currentIgm03Data);

  206.             }

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

  209.         }

  210.     },

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

  213.         /** {@inheritDoc} */
  214.         @Override
  215.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber,
  216.                                    final TimeScales timeScales) {

  217.             // Satellite system
  218.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  219.             // Header data
  220.             final SsrIgm04Header igm04Header = new SsrIgm04Header();
  221.             igm04Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  222.             igm04Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  223.             igm04Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  224.             igm04Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  225.             igm04Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  226.             igm04Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));

  227.             // Number of satellites
  228.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  229.             igm04Header.setNumberOfSatellites(satNumber);

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

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

  234.                 // Initialize a new container
  235.                 final SsrIgm04Data currentIgm04Data = new SsrIgm04Data();
  236.                 currentIgm04Data.setSatelliteID(getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage)));
  237.                 currentIgm04Data.setHighRateClockCorrection(IgsSsrDataField.IDF022.doubleValue(encodedMessage));

  238.                 // Update the list
  239.                 igm04Data.add(currentIgm04Data);

  240.             }

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

  243.         }

  244.     },

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

  247.         /** {@inheritDoc} */
  248.         @Override
  249.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber,
  250.                                    final TimeScales timeScales) {

  251.             // Satellite system
  252.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  253.             // Header data
  254.             final SsrIgm05Header igm05Header = new SsrIgm05Header();
  255.             igm05Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  256.             igm05Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  257.             igm05Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  258.             igm05Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  259.             igm05Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  260.             igm05Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));

  261.             // Number of satellites
  262.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  263.             igm05Header.setNumberOfSatellites(satNumber);

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

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

  268.                 // Initialize a new container
  269.                 final SsrIgm05Data currentIgm05Data = new SsrIgm05Data();
  270.                 currentIgm05Data.setSatelliteID(getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage)));

  271.                 // Number of biases
  272.                 final int biasesNumber = IgsSsrDataField.IDF023.intValue(encodedMessage);
  273.                 currentIgm05Data.setNumberOfBiasesProcessed(biasesNumber);

  274.                 // Loop on biases
  275.                 for (int biasIndex = 0; biasIndex < biasesNumber; biasIndex++) {
  276.                     // Initialize a new code bias
  277.                     final CodeBias codeBias = new CodeBias(IgsSsrDataField.IDF024.intValue(encodedMessage),
  278.                                                            IgsSsrDataField.IDF025.doubleValue(encodedMessage));
  279.                     // Add the codeBias to the container
  280.                     currentIgm05Data.addCodeBias(codeBias);
  281.                 }

  282.                 // Update the list of data
  283.                 igm05Data.add(currentIgm05Data);

  284.             }

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

  287.         }

  288.     },

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

  291.         /** {@inheritDoc} */
  292.         @Override
  293.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber,
  294.                                    final TimeScales timeScales) {

  295.             // Satellite system
  296.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  297.             // Header data
  298.             final SsrIgm06Header igm06Header = new SsrIgm06Header();
  299.             igm06Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  300.             igm06Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  301.             igm06Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  302.             igm06Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  303.             igm06Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  304.             igm06Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));
  305.             igm06Header.setIsConsistencyMaintained(IgsSsrDataField.IDF032.booleanValue(encodedMessage));
  306.             igm06Header.setIsMelbourneWubbenaConsistencyMaintained(IgsSsrDataField.IDF033.booleanValue(encodedMessage));

  307.             // Number of satellites
  308.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  309.             igm06Header.setNumberOfSatellites(satNumber);

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

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

  314.                 // Initialize a new container
  315.                 final SsrIgm06Data currentIgm06Data = new SsrIgm06Data();
  316.                 currentIgm06Data.setSatelliteID(getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage)));

  317.                 // Number of biases
  318.                 final int biasesNumber = IgsSsrDataField.IDF023.intValue(encodedMessage);
  319.                 currentIgm06Data.setNumberOfBiasesProcessed(biasesNumber);

  320.                 // Yaw angle and rate
  321.                 currentIgm06Data.setYawAngle(IgsSsrDataField.IDF026.doubleValue(encodedMessage));
  322.                 currentIgm06Data.setYawRate(IgsSsrDataField.IDF027.doubleValue(encodedMessage));

  323.                 // Loop on biases
  324.                 for (int biasIndex = 0; biasIndex < biasesNumber; biasIndex++) {
  325.                     // Initialize a new phase bias
  326.                     final PhaseBias phaseBias = new PhaseBias(IgsSsrDataField.IDF024.intValue(encodedMessage),
  327.                                                               IgsSsrDataField.IDF029.booleanValue(encodedMessage),
  328.                                                               IgsSsrDataField.IDF030.intValue(encodedMessage),
  329.                                                               IgsSsrDataField.IDF031.intValue(encodedMessage),
  330.                                                               IgsSsrDataField.IDF028.doubleValue(encodedMessage));
  331.                     // Add the codeBias to the container
  332.                     currentIgm06Data.addPhaseBias(phaseBias);
  333.                 }

  334.                 // Update the list of data
  335.                 igm06Data.add(currentIgm06Data);

  336.             }

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

  339.         }

  340.     },

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

  343.         /** {@inheritDoc} */
  344.         @Override
  345.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber,
  346.                                    final TimeScales timeScales) {

  347.             // Satellite system
  348.             final SatelliteSystem system = messageNumberToSatelliteSystem(messageNumber);

  349.             // Header data
  350.             final SsrIgm07Header igm07Header = new SsrIgm07Header();
  351.             igm07Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  352.             igm07Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  353.             igm07Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  354.             igm07Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  355.             igm07Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  356.             igm07Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));

  357.             // Number of satellites
  358.             final int satNumber = IgsSsrDataField.IDF010.intValue(encodedMessage);
  359.             igm07Header.setNumberOfSatellites(satNumber);

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

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

  364.                 // Initialize a new container
  365.                 final SsrIgm07Data currentIgm07Data = new SsrIgm07Data();
  366.                 currentIgm07Data.setSatelliteID(getSatelliteId(system, IgsSsrDataField.IDF011.intValue(encodedMessage)));
  367.                 currentIgm07Data.setSsrUra(IgsSsrDataField.IDF034.intValue(encodedMessage));

  368.                 // Update the list
  369.                 igm07Data.add(currentIgm07Data);

  370.             }

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

  373.         }

  374.     },

  375.     /** SSR Ionosphere VTEC Spherical Harmonics Message. */
  376.     IM_201("201") {

  377.         /** {@inheritDoc} */
  378.         @Override
  379.         public ParsedMessage parse(final EncodedMessage encodedMessage, final int messageNumber,
  380.                                    final TimeScales timeScales) {

  381.             // Header data
  382.             final SsrIm201Header im201Header = new SsrIm201Header();
  383.             im201Header.setSsrEpoch1s(IgsSsrDataField.IDF003.intValue(encodedMessage));
  384.             im201Header.setSsrUpdateInterval(IgsSsrDataField.IDF004.intValue(encodedMessage));
  385.             im201Header.setSsrMultipleMessageIndicator(IgsSsrDataField.IDF005.intValue(encodedMessage));
  386.             im201Header.setIodSsr(IgsSsrDataField.IDF007.intValue(encodedMessage));
  387.             im201Header.setSsrProviderId(IgsSsrDataField.IDF008.intValue(encodedMessage));
  388.             im201Header.setSsrSolutionId(IgsSsrDataField.IDF009.intValue(encodedMessage));
  389.             im201Header.setVtecQualityIndicator(IgsSsrDataField.IDF041.doubleValue(encodedMessage));

  390.             // Number of ionospheric layers
  391.             final int numberOfIonosphericLayers = IgsSsrDataField.IDF035.intValue(encodedMessage);
  392.             im201Header.setNumberOfIonosphericLayers(numberOfIonosphericLayers);

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

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

  397.                 // Initialize a new container
  398.                 final SsrIm201Data currentIm201Data = new SsrIm201Data();

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

  401.                 // Degree and order of spherical harmonics
  402.                 final int n = IgsSsrDataField.IDF037.intValue(encodedMessage);
  403.                 final int m = IgsSsrDataField.IDF038.intValue(encodedMessage);

  404.                 // Initialize arrays
  405.                 final double[][] cnm = new double[n + 1][m + 1];
  406.                 final double[][] snm = new double[n + 1][m + 1];

  407.                 ////
  408.                 // Cosine coefficients
  409.                 ////

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

  417.                 ////
  418.                 // Sine coefficients
  419.                 ////

  420.                 // Loop on degree
  421.                 for (int order = 1; order <= m; order++) {
  422.                     // Loop on order
  423.                     for (int degree = order; degree <= n; degree++) {
  424.                         snm[degree][order] = IgsSsrDataField.IDF040.doubleValue(encodedMessage);
  425.                     }
  426.                 }

  427.                 currentIm201Data.setSphericalHarmonicsDegree(n);
  428.                 currentIm201Data.setSphericalHarmonicsOrder(m);
  429.                 currentIm201Data.setCnm(cnm);
  430.                 currentIm201Data.setSnm(snm);

  431.                 // Update the list
  432.                 im201Data.add(currentIm201Data);

  433.             }

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

  436.         }

  437.     };

  438.     /** Codes map. */
  439.     private static final Map<Pattern, IgsSsrMessageType> CODES_MAP = new HashMap<>();
  440.     static {
  441.         for (final IgsSsrMessageType type : values()) {
  442.             CODES_MAP.put(type.getPattern(), type);
  443.         }
  444.     }

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

  447.     /** Simple constructor.
  448.      * @param regex message regular expression
  449.      */
  450.     IgsSsrMessageType(final String regex) {
  451.         this.pattern = Pattern.compile(regex);
  452.     }

  453.     /** Get the message number.
  454.      * @return message number
  455.      */
  456.     public Pattern getPattern() {
  457.         return pattern;
  458.     }

  459.     /** Get the message type corresponding to a message number.
  460.      * @param number message number
  461.      * @return the message type corresponding to the message number
  462.      */
  463.     public static IgsSsrMessageType getMessageType(final String number) {
  464.         // Try to find a match with an existing message type
  465.         for (Map.Entry<Pattern, IgsSsrMessageType> entry : CODES_MAP.entrySet()) {
  466.             // Matcher
  467.             final Matcher matcher = entry.getKey().matcher(number);
  468.             // Check the match !
  469.             if (matcher.matches()) {
  470.                 // return the message type
  471.                 return entry.getValue();
  472.             }
  473.         }
  474.         // No match found
  475.         throw new OrekitException(OrekitMessages.UNKNOWN_ENCODED_MESSAGE_NUMBER, number);
  476.     }

  477.     /**
  478.      * Find the satellite system corresponding to the sub-type message number.
  479.      * <p>
  480.      * See Table 5 of reference
  481.      * </p>
  482.      * @param subTypeMessage message umber
  483.      * @return the corresponding satellite system
  484.      */
  485.     public static SatelliteSystem messageNumberToSatelliteSystem(final int subTypeMessage) {

  486.         if (subTypeMessage > 20 && subTypeMessage <= 40) {
  487.             // GPS messages
  488.             return SatelliteSystem.GPS;
  489.         } else if (subTypeMessage <= 60) {
  490.             // GLONASS messages
  491.             return SatelliteSystem.GLONASS;
  492.         } else if (subTypeMessage <= 80) {
  493.             // Galileo messages
  494.             return SatelliteSystem.GALILEO;
  495.         } else if (subTypeMessage <= 100) {
  496.             // QZSS messages
  497.             return SatelliteSystem.QZSS;
  498.         } else if (subTypeMessage <= 120) {
  499.             // Beidou messages
  500.             return SatelliteSystem.BEIDOU;
  501.         } else if (subTypeMessage <= 140) {
  502.             // SBAS messages
  503.             return SatelliteSystem.SBAS;
  504.         } else {
  505.             // NavIC messages
  506.             return SatelliteSystem.NAVIC;
  507.         }

  508.     }

  509.     /**
  510.      * Transform the satellite ID parsed from the IGS SSR message to the real ID.
  511.      * @param system the satellite system of the parsed message
  512.      * @param id the parsed satellite ID
  513.      * @return the real satellite ID
  514.      */
  515.     public static int getSatelliteId(final SatelliteSystem system, final int id) {

  516.         // Switch on satellite systems
  517.         switch (system) {
  518.             case QZSS:
  519.                 // ID = ID(parsed) + 192
  520.                 return id + 192;
  521.             case SBAS:
  522.                 // ID = ID(parsed) + 119
  523.                 return id + 119;
  524.             default:
  525.                 // For GPS, GLONASS, Beidou, and Galileo the id is unchanged
  526.                 return id;
  527.         }

  528.     }

  529. }