IIRVParser.java
/* Copyright 2024-2025 The Johns Hopkins University Applied Physics Laboratory
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.files.iirv;
import org.hipparchus.exception.LocalizedCoreFormats;
import org.orekit.data.DataSource;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitIllegalArgumentException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.general.EphemerisFileParser;
import org.orekit.time.UTCScale;
import org.orekit.utils.Constants;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
/**
* Parser of {@link IIRVEphemerisFile}s.
*
* @author Nick LaFarge
* @since 13.0
*/
public class IIRVParser implements EphemerisFileParser<IIRVEphemerisFile> {
/** Default number of sample for interpolating data (See: reference documents). */
public static final int DEFAULT_INTERPOLATION_SAMPLE = 10;
/** Line separator. */
private static final Pattern LINE_SEPARATOR_PATTERN = Pattern.compile(IIRVVector.LINE_SEPARATOR);
/** Standard gravitational parameter in m³/s². */
private final double mu;
/** Number of data points to use in interpolation. */
private final int interpolationSamples;
/** Year of the initial vector in the IIRV ephemeris file. */
private final int year;
/** UTC time scale. */
private final UTCScale utc;
/**
* Constructs a {@link IIRVParser} instance with default values.
* <p>
* Default gravitational parameter is {@link Constants#IERS96_EARTH_MU}. Default number of
* interpolation samples is 7.
*
* @param year year of the initial vector in the IIRV ephemeris file.
* @param utc UTC time scale
*/
public IIRVParser(final int year, final UTCScale utc) {
this(Constants.IERS96_EARTH_MU, DEFAULT_INTERPOLATION_SAMPLE, year, utc);
}
/**
* Constructs a {@link IIRVParser} instance.
*
* @param mu gravitational parameter (m^3/s^2)
* @param interpolationSamples is the number of samples to use when interpolating.
* @param year year of the initial vector in the IIRV ephemeris file.
* @param utc UTC time scale
*/
public IIRVParser(final double mu, final int interpolationSamples, final int year, final UTCScale utc) {
this.mu = mu;
this.interpolationSamples = interpolationSamples;
this.year = year;
this.utc = utc;
}
/** {@inheritDoc} */
@Override
public IIRVEphemerisFile parse(final DataSource source) {
if (source == null) {
throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "source");
}
final ArrayList<String> messageLines = new ArrayList<>();
try (Reader reader = source.getOpener().openReaderOnce();
BufferedReader bufferedReader = (reader == null) ? null : new BufferedReader(reader)) {
if (bufferedReader == null) {
throw new OrekitException(OrekitMessages.UNABLE_TO_FIND_FILE, source.getName());
}
// Message lines
final List<String> vectorLines = new ArrayList<>(Collections.nCopies(6, ""));
int currentIIRVLine = vectorLines.indexOf("");
String line = bufferedReader.readLine(); // Initialize on first line
if (line == null) {
throw new OrekitException(OrekitMessages.NO_DATA_IN_FILE, source.getName());
}
while (line != null) {
messageLines.add(line);
vectorLines.set(currentIIRVLine, line); // Set the line in the list
// If every line is set, create an IIRV vector and clear out the strings. Otherwise, increment
// the line counter
if (currentIIRVLine == 5) {
for (int i = 0; i < 6; i++) { // Reset each line + line counter
vectorLines.set(i, "");
}
currentIIRVLine = 0;
} else {
currentIIRVLine++;
}
// Expect two line breaks here (except end of file
final String linebreak1 = bufferedReader.readLine();
final String linebreak2 = bufferedReader.readLine();
if (linebreak1 == null || linebreak2 == null) {
break;
} else if (!linebreak1.isEmpty() || !linebreak2.isEmpty()) {
throw new OrekitException(OrekitMessages.IIRV_MISSING_LINEBREAK_IN_FILE, currentIIRVLine, source.getName());
}
line = bufferedReader.readLine();
}
} catch (IOException ioe) {
throw new OrekitException(ioe, LocalizedCoreFormats.SIMPLE_MESSAGE, ioe.getLocalizedMessage());
}
return parse(messageLines);
}
/**
* Parses a string representing an IIRV message.
*
* @param iirv String representation of an IIRV message
* @return newly created {@link IIRVSegment} object populated with ephemeris data parsed from
* {@code iirvVectorStrings}
*/
public IIRVEphemerisFile parse(final String iirv) {
return parse(Arrays.asList(LINE_SEPARATOR_PATTERN.split(iirv)));
}
/**
* Parses a list of strings that comprise an {@link IIRVMessage}.
*
* @param iirvVectorStrings list of Strings that comprise an {@link IIRVMessage}
* @return newly created {@link IIRVSegment} object populated with ephemeris data parsed from
* {@code iirvVectorStrings}
*/
public IIRVEphemerisFile parse(final List<String> iirvVectorStrings) {
final ArrayList<IIRVVector> vectors = new ArrayList<>();
if (iirvVectorStrings == null) {
throw new OrekitIllegalArgumentException(OrekitMessages.NULL_ARGUMENT, "iirvVectorStrings");
}
if (iirvVectorStrings.isEmpty()) {
throw new OrekitIllegalArgumentException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR, 1, "");
}
// The first vector in a message must *always* include metadata
Pattern line1Pattern = IIRVVector.LINE_1_PATTERN_METADATA_INCLUDED;
// Message lines
final List<String> vectorLines = new ArrayList<>(Collections.nCopies(6, ""));
int currentIIRVLine = vectorLines.indexOf("");
for (String line : iirvVectorStrings) {
// The second vector tells us whether to expect metadata in line 1 for the
// remainder of the file
if (vectors.size() == 1 && currentIIRVLine == 0 && IIRVVector.LINE_1_PATTERN_METADATA_OMITTED.matcher(line).matches()) {
line1Pattern = IIRVVector.LINE_1_PATTERN_METADATA_OMITTED;
}
// Check if this line matches an IIRV pattern based on the current line index
final boolean line1Valid = currentIIRVLine == 0 && line1Pattern.matcher(line).matches();
boolean isValidLine2to6 = false;
for (int i = currentIIRVLine; i < 6; i++) {
final boolean isValidForLineI = IIRVVector.validateLine(i, line);
if (isValidForLineI) {
if (currentIIRVLine == i) {
isValidLine2to6 = true; // Only valid if the line is validated at the right location
break;
} else {
// Valid for the wrong line-> invalid file
throw new OrekitException(OrekitMessages.IIRV_INVALID_LINE_IN_VECTOR,
currentIIRVLine, line);
}
}
}
// Continue if this line matches a pattern
if (line1Valid || isValidLine2to6) {
vectorLines.set(currentIIRVLine, line); // Set the line in the list
// If every line is set, create an IIRV vector and clear out the strings. Otherwise, increment
// the line counter
if (currentIIRVLine == 5) {
IIRVVector newVector = new IIRVVector(vectorLines, utc);
// Add metadata (if applicable)
if (!vectors.isEmpty() && line1Pattern == IIRVVector.LINE_1_PATTERN_METADATA_OMITTED) {
vectorLines.set(0, vectors.get(0).buildLine1(true));
newVector = new IIRVVector(vectorLines, utc);
}
vectors.add(newVector);
for (int i = 0; i < 6; i++) { // Reset each line + line counter
vectorLines.set(i, "");
}
currentIIRVLine = 0;
} else {
currentIIRVLine++;
}
}
}
return new IIRVEphemerisFile(mu, interpolationSamples, year, new IIRVMessage(vectors));
}
}