IIRVParser.java

  1. /* Copyright 2024-2025 The Johns Hopkins University Applied Physics Laboratory
  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.files.iirv;

  18. import org.hipparchus.exception.LocalizedCoreFormats;
  19. import org.orekit.data.DataSource;
  20. import org.orekit.errors.OrekitException;
  21. import org.orekit.errors.OrekitIllegalArgumentException;
  22. import org.orekit.errors.OrekitMessages;
  23. import org.orekit.files.general.EphemerisFileParser;
  24. import org.orekit.time.UTCScale;
  25. import org.orekit.utils.Constants;

  26. import java.io.BufferedReader;
  27. import java.io.IOException;
  28. import java.io.Reader;
  29. import java.util.ArrayList;
  30. import java.util.Arrays;
  31. import java.util.Collections;
  32. import java.util.List;
  33. import java.util.regex.Pattern;

  34. /**
  35.  * Parser of {@link IIRVEphemerisFile}s.
  36.  *
  37.  * @author Nick LaFarge
  38.  * @since 13.0
  39.  */
  40. public class IIRVParser implements EphemerisFileParser<IIRVEphemerisFile> {

  41.     /** Default number of sample for interpolating data (See: reference documents). */
  42.     public static final int DEFAULT_INTERPOLATION_SAMPLE = 10;

  43.     /** Line separator. */
  44.     private static final Pattern LINE_SEPARATOR_PATTERN = Pattern.compile(IIRVVector.LINE_SEPARATOR);

  45.     /** Standard gravitational parameter in m³/s². */
  46.     private final double mu;

  47.     /** Number of data points to use in interpolation. */
  48.     private final int interpolationSamples;

  49.     /** Year of the initial vector in the IIRV ephemeris file. */
  50.     private final int year;

  51.     /** UTC time scale. */
  52.     private final UTCScale utc;

  53.     /**
  54.      * Constructs a {@link IIRVParser} instance with default values.
  55.      * <p>
  56.      * Default gravitational parameter is {@link Constants#IERS96_EARTH_MU}. Default number of
  57.      * interpolation samples is 7.
  58.      *
  59.      * @param year year of the initial vector in the IIRV ephemeris file.
  60.      * @param utc  UTC time scale
  61.      */
  62.     public IIRVParser(final int year, final UTCScale utc) {
  63.         this(Constants.IERS96_EARTH_MU, DEFAULT_INTERPOLATION_SAMPLE, year, utc);
  64.     }

  65.     /**
  66.      * Constructs a {@link IIRVParser} instance.
  67.      *
  68.      * @param mu                   gravitational parameter (m^3/s^2)
  69.      * @param interpolationSamples is the number of samples to use when interpolating.
  70.      * @param year                 year of the initial vector in the IIRV ephemeris file.
  71.      * @param utc                  UTC time scale
  72.      */
  73.     public IIRVParser(final double mu, final int interpolationSamples, final int year, final UTCScale utc) {
  74.         this.mu = mu;
  75.         this.interpolationSamples = interpolationSamples;
  76.         this.year = year;
  77.         this.utc = utc;
  78.     }

  79.     /** {@inheritDoc} */
  80.     @Override
  81.     public IIRVEphemerisFile parse(final DataSource source) {
  82.         if (source == null) {
  83.             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "source");
  84.         }

  85.         final ArrayList<String> messageLines = new ArrayList<>();

  86.         try (Reader reader = source.getOpener().openReaderOnce();
  87.              BufferedReader bufferedReader = (reader == null) ? null : new BufferedReader(reader)) {

  88.             if (bufferedReader == null) {
  89.                 throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, source.getName());
  90.             }

  91.             // Message lines
  92.             final List<String> vectorLines = new ArrayList<>(Collections.nCopies(6, ""));
  93.             int currentIIRVLine = vectorLines.indexOf("");
  94.             String line = bufferedReader.readLine();  // Initialize on first line

  95.             if (line == null) {
  96.                 throw new OrekitException(OrekitMessages.NO_DATA_IN_FILE, source.getName());
  97.             }

  98.             while (line != null) {
  99.                 messageLines.add(line);
  100.                 vectorLines.set(currentIIRVLine, line);  // Set the line in the list

  101.                 // If every line is set, create an IIRV vector and clear out the strings. Otherwise, increment
  102.                 // the line counter
  103.                 if (currentIIRVLine == 5) {
  104.                     for (int i = 0; i < 6; i++) { // Reset each line + line counter
  105.                         vectorLines.set(i, "");
  106.                     }
  107.                     currentIIRVLine = 0;
  108.                 } else {
  109.                     currentIIRVLine++;
  110.                 }

  111.                 // Expect two line breaks here (except end of file
  112.                 final String linebreak1 = bufferedReader.readLine();
  113.                 final String linebreak2 = bufferedReader.readLine();
  114.                 if (linebreak1 == null || linebreak2 == null) {
  115.                     break;
  116.                 } else if (!linebreak1.isEmpty() || !linebreak2.isEmpty()) {
  117.                     throw new OrekitException(OrekitMessages.IIRV_MISSING_LINEBREAK_IN_FILE, currentIIRVLine, source.getName());
  118.                 }

  119.                 line = bufferedReader.readLine();
  120.             }

  121.         } catch (IOException ioe) {
  122.             throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage());
  123.         }
  124.         return parse(messageLines);
  125.     }


  126.     /**
  127.      * Parses a string representing an IIRV message.
  128.      *
  129.      * @param iirv String representation of an IIRV message
  130.      * @return newly created {@link IIRVSegment} object populated with ephemeris data parsed from
  131.      * {@code iirvVectorStrings}
  132.      */
  133.     public IIRVEphemerisFile parse(final String iirv) {
  134.         return parse(Arrays.asList(LINE_SEPARATOR_PATTERN.split(iirv)));
  135.     }

  136.     /**
  137.      * Parses a list of strings that comprise an {@link IIRVMessage}.
  138.      *
  139.      * @param iirvVectorStrings list of Strings that comprise an {@link IIRVMessage}
  140.      * @return newly created {@link IIRVSegment} object populated with ephemeris data parsed from
  141.      * {@code iirvVectorStrings}
  142.      */
  143.     public IIRVEphemerisFile parse(final List<String> iirvVectorStrings) {
  144.         final ArrayList<IIRVVector> vectors = new ArrayList<>();

  145.         if (iirvVectorStrings == null) {
  146.             throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "iirvVectorStrings");
  147.         }

  148.         if (iirvVectorStrings.isEmpty()) {
  149.             throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR, 1, "");
  150.         }

  151.         // The first vector in a message must *always* include metadata
  152.         Pattern line1Pattern = IIRVVector.LINE_1_PATTERN_METADATA_INCLUDED;

  153.         // Message lines
  154.         final List<String> vectorLines = new ArrayList<>(Collections.nCopies(6, ""));
  155.         int currentIIRVLine = vectorLines.indexOf("");

  156.         for (String line : iirvVectorStrings) {

  157.             // The second vector tells us whether to expect metadata in line 1 for the
  158.             // remainder of the file
  159.             if (vectors.size() == 1 && currentIIRVLine == 0 && IIRVVector.LINE_1_PATTERN_METADATA_OMITTED.matcher(line).matches()) {
  160.                 line1Pattern = IIRVVector.LINE_1_PATTERN_METADATA_OMITTED;
  161.             }

  162.             // Check if this line matches an IIRV pattern based on the current line index
  163.             final boolean line1Valid = currentIIRVLine == 0 && line1Pattern.matcher(line).matches();

  164.             boolean isValidLine2to6 = false;
  165.             for (int i = currentIIRVLine; i < 6; i++) {
  166.                 final boolean isValidForLineI = IIRVVector.validateLine(i, line);
  167.                 if (isValidForLineI) {
  168.                     if (currentIIRVLine == i) {
  169.                         isValidLine2to6 = true;  // Only valid if the line is validated at the right location
  170.                         break;
  171.                     } else {
  172.                         // Valid for the wrong line-> invalid file
  173.                         throw new OrekitException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR,
  174.                             currentIIRVLine, line);
  175.                     }
  176.                 }
  177.             }

  178.             // Continue if this line matches a pattern
  179.             if (line1Valid || isValidLine2to6) {
  180.                 vectorLines.set(currentIIRVLine, line);  // Set the line in the list

  181.                 // If every line is set, create an IIRV vector and clear out the strings. Otherwise, increment
  182.                 // the line counter
  183.                 if (currentIIRVLine == 5) {
  184.                     IIRVVector newVector = new IIRVVector(vectorLines, utc);

  185.                     // Add metadata (if applicable)
  186.                     if (!vectors.isEmpty() && line1Pattern == IIRVVector.LINE_1_PATTERN_METADATA_OMITTED) {
  187.                         vectorLines.set(0, vectors.get(0).buildLine1(true));
  188.                         newVector = new IIRVVector(vectorLines, utc);
  189.                     }

  190.                     vectors.add(newVector);

  191.                     for (int i = 0; i < 6; i++) { // Reset each line + line counter
  192.                         vectorLines.set(i, "");
  193.                     }
  194.                     currentIIRVLine = 0;
  195.                 } else {
  196.                     currentIIRVLine++;
  197.                 }
  198.             }
  199.         }
  200.         return new IIRVEphemerisFile(mu, interpolationSamples, year, new IIRVMessage(vectors));
  201.     }
  202. }