org.orekit.time.TAIUTCDatFilesLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of orekit Show documentation
Show all versions of orekit Show documentation
OREKIT (ORbits Extrapolation KIT) is a low level space dynamics library.
It provides basic elements (orbits, dates, attitude, frames ...) and
various algorithms to handle them (conversions, analytical and numerical
propagation, pointing ...).
/* Copyright 2002-2024 CS GROUP
* 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.time;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.hipparchus.util.FastMath;
import org.orekit.annotation.DefaultDataContext;
import org.orekit.data.AbstractSelfFeedingLoader;
import org.orekit.data.DataContext;
import org.orekit.data.DataProvidersManager;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
/** Loader for UTC-TAI extracted from tai-utc.dat file from USNO.
*
* This class is immutable and hence thread-safe
*
* @author Luc Maisonobe
* @since 7.1
*/
public class TAIUTCDatFilesLoader extends AbstractSelfFeedingLoader
implements UTCTAIOffsetsLoader {
/** Default supported files name pattern. */
public static final String DEFAULT_SUPPORTED_NAMES = "^tai-utc\\.dat$";
/**
* Build a loader for tai-utc.dat file from USNO. This constructor uses the {@link
* DataContext#getDefault() default data context}.
*
* @param supportedNames regular expression for supported files names
* @see #TAIUTCDatFilesLoader(String, DataProvidersManager)
*/
@DefaultDataContext
public TAIUTCDatFilesLoader(final String supportedNames) {
this(supportedNames, DataContext.getDefault().getDataProvidersManager());
}
/**
* Build a loader for tai-utc.dat file from USNO.
*
* @param supportedNames regular expression for supported files names
* @param manager provides access to the {@code tai-utc.dat} file.
*/
public TAIUTCDatFilesLoader(final String supportedNames,
final DataProvidersManager manager) {
super(supportedNames, manager);
}
/** {@inheritDoc} */
@Override
public List loadOffsets() {
final UtcTaiOffsetLoader parser = new UtcTaiOffsetLoader(new Parser());
this.feed(parser);
return parser.getOffsets();
}
/** Internal class performing the parsing. */
public static class Parser implements UTCTAIOffsetsLoader.Parser {
/** Regular expression for optional blanks. */
private static final String BLANKS = "\\p{Blank}*";
/** Regular expression for storage start. */
private static final String STORAGE_START = "(";
/** Regular expression for storage end. */
private static final String STORAGE_END = ")";
/** Regular expression for alternative. */
private static final String ALTERNATIVE = "|";
/** Regular expression matching blanks at start of line. */
private static final String LINE_START_REGEXP = "^" + BLANKS;
/** Regular expression matching blanks at end of line. */
private static final String LINE_END_REGEXP = BLANKS + "$";
/** Regular expression matching integers. */
private static final String INTEGER_REGEXP = "[-+]?\\p{Digit}+";
/** Regular expression matching real numbers. */
private static final String REAL_REGEXP = "[-+]?(?:(?:\\p{Digit}+(?:\\.\\p{Digit}*)?)|(?:\\.\\p{Digit}+))(?:[eE][-+]?\\p{Digit}+)?";
/** Regular expression matching an integer field to store. */
private static final String STORED_INTEGER_FIELD = BLANKS + STORAGE_START + INTEGER_REGEXP + STORAGE_END;
/** Regular expression matching a real field to store. */
private static final String STORED_REAL_FIELD = BLANKS + STORAGE_START + REAL_REGEXP + STORAGE_END;
/** Data lines pattern. */
private Pattern dataPattern;
/** Simple constructor.
*/
public Parser() {
// data lines read:
// 1965 SEP 1 =JD 2439004.5 TAI-UTC= 3.8401300 S + (MJD - 38761.) X 0.001296 S
// 1966 JAN 1 =JD 2439126.5 TAI-UTC= 4.3131700 S + (MJD - 39126.) X 0.002592 S
// 1968 FEB 1 =JD 2439887.5 TAI-UTC= 4.2131700 S + (MJD - 39126.) X 0.002592 S
// 1972 JAN 1 =JD 2441317.5 TAI-UTC= 10.0 S + (MJD - 41317.) X 0.0 S
// 1972 JUL 1 =JD 2441499.5 TAI-UTC= 11.0 S + (MJD - 41317.) X 0.0 S
// 1973 JAN 1 =JD 2441683.5 TAI-UTC= 12.0 S + (MJD - 41317.) X 0.0 S
// 1974 JAN 1 =JD 2442048.5 TAI-UTC= 13.0 S + (MJD - 41317.) X 0.0 S
// month as a three letters upper case abbreviation
final StringBuilder builder = new StringBuilder(BLANKS + STORAGE_START);
for (final Month month : Month.values()) {
builder.append(month.getUpperCaseAbbreviation());
builder.append(ALTERNATIVE);
}
builder.delete(builder.length() - 1, builder.length());
builder.append(STORAGE_END);
final String monthField = builder.toString();
dataPattern = Pattern.compile(LINE_START_REGEXP +
STORED_INTEGER_FIELD + monthField + STORED_INTEGER_FIELD +
"\\p{Blank}+=JD" + STORED_REAL_FIELD +
"\\p{Blank}+TAI-UTC=" + STORED_REAL_FIELD +
"\\p{Blank}+S\\p{Blank}+\\+\\p{Blank}+\\(MJD\\p{Blank}+-" + STORED_REAL_FIELD +
"\\p{Blank}*\\)\\p{Blank}+X" + STORED_REAL_FIELD +
"\\p{Blank}*S" + LINE_END_REGEXP);
}
/** Load UTC-TAI offsets entries read from some file.
* The time steps are extracted from some {@code tai-utc.dat} file.
* Since entries are stored in a {@link java.util.SortedMap SortedMap},
* they are chronologically sorted and only one entry remains for a given date.
* @param input data input stream
* @param name name of the file (or zip entry)
* @exception IOException if data can't be read
*/
@Override
public List parse(final InputStream input, final String name)
throws IOException {
final List offsets = new ArrayList<>();
int lineNumber = 0;
DateComponents lastDate = null;
String line = null;
// set up a reader for line-oriented file
try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
// read all file, ignoring not recognized lines
for (line = reader.readLine(); line != null; line = reader.readLine()) {
++lineNumber;
// check matching for data lines
final Matcher matcher = dataPattern.matcher(line);
if (matcher.matches()) {
// build an entry from the extracted fields
final DateComponents dc1 = new DateComponents(Integer.parseInt(matcher.group(1)),
Month.parseMonth(matcher.group(2)),
Integer.parseInt(matcher.group(3)));
final DateComponents dc2 = new DateComponents(DateComponents.JULIAN_EPOCH,
(int) FastMath.ceil(Double.parseDouble(matcher.group(4))));
if (!dc1.equals(dc2)) {
throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE,
name, dc1.getYear(), dc1.getMonth(), dc1.getDay(), dc2.getMJD());
}
if (lastDate != null && dc1.compareTo(lastDate) <= 0) {
throw new OrekitException(OrekitMessages.NON_CHRONOLOGICAL_DATES_IN_FILE,
name, lineNumber);
}
lastDate = dc1;
final double offset = Double.parseDouble(matcher.group(5));
final double mjdRef = Double.parseDouble(matcher.group(6));
final double slope = Double.parseDouble(matcher.group(7));
offsets.add(new OffsetModel(dc1, (int) FastMath.rint(mjdRef), offset, slope));
}
}
} catch (NumberFormatException nfe) {
throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
lineNumber, name, line);
}
if (offsets.isEmpty()) {
throw new OrekitException(OrekitMessages.NO_ENTRIES_IN_IERS_UTC_TAI_HISTORY_FILE, name);
}
return offsets;
}
}
}