Commit 7d4f4a1d authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Added support for IERS Rapid Data and Prediction files.

These files ares the finals.all, finals.data and finals.daily files.
Both IAU-1980 and IAU-2000 are supported and both columns and
XML formats are supported.
 
parent 914222e0
......@@ -5,7 +5,7 @@
<groupId>org.orekit</groupId>
<artifactId>orekit</artifactId>
<packaging>bundle</packaging>
<version>5.0.1</version>
<version>5.0.2</version>
<name>ORbit Extrapolation KIT</name>
<url>http://www.orekit.org/</url>
......
......@@ -102,12 +102,24 @@ import org.orekit.utils.Constants;
*/
public class FramesFactory implements Serializable {
/** Default regular expression for the Rapid Data and Prediction EOP columns files (IAU1980 compatibles). */
public static final String RAPID_DATA_PREDICITON_COLUMNS_1980_FILENAME = "^finals\\.[^.]*$";
/** Default regular expression for the Rapid Data and Prediction EOP XML files (IAU1980 compatibles). */
public static final String RAPID_DATA_PREDICITON_XML_1980_FILENAME = "^finals\\..*\\.xml$";
/** Default regular expression for the EOPC04 files (IAU1980 compatibles). */
public static final String EOPC04_1980_FILENAME = "^eopc04\\.(\\d\\d)$";
/** Default regular expression for the BulletinB files (IAU1980 compatibles). */
public static final String BULLETINB_1980_FILENAME = "^bulletinb((-\\d\\d\\d\\.txt)|(\\.\\d\\d\\d))$";
/** Default regular expression for the Rapid Data and Prediction EOP columns files (IAU2000 compatibles). */
public static final String RAPID_DATA_PREDICITON_COLUMNS_2000_FILENAME = "^finals2000A\\.[^.]*$";
/** Default regular expression for the Rapid Data and Prediction EOP XML files (IAU2000 compatibles). */
public static final String RAPID_DATA_PREDICITON_XML_2000_FILENAME = "^finals2000A\\..*\\.xml$";
/** Default regular expression for the EOPC04 files (IAU2000 compatibles). */
public static final String EOPC04_2000_FILENAME = "^eopc04_IAU2000\\.(\\d\\d)$";
......@@ -188,6 +200,12 @@ public class FramesFactory implements Serializable {
* <p>
* The default loaders look for IERS EOP 05 C04 and bulletins B files.
* </p>
* @param rapidDataColumnsSupportedNames regular expression for supported
* rapid data columns EOP files names
* (may be null if the default IERS file names are used)
* @param rapidDataXMLSupportedNames regular expression for supported
* rapid data XML EOP files names
* (may be null if the default IERS file names are used)
* @param eopC04SupportedNames regular expression for supported EOP05 C04 files names
* (may be null if the default IERS file names are used)
* @param bulletinBSupportedNames regular expression for supported bulletin B files names
......@@ -196,12 +214,20 @@ public class FramesFactory implements Serializable {
* @see <a href="http://hpiers.obspm.fr/eoppc/bul/bulb/">IERS bulletins B</a>
* @see #addEOP1980HistoryLoader(EOP1980HistoryLoader)
* @see #clearEOP1980HistoryLoaders()
* @see #addDefaultEOP2000HistoryLoaders(String, String)
* @see #addDefaultEOP2000HistoryLoaders(String, String, String)
*/
public static void addDefaultEOP1980HistoryLoaders(final String eopC04SupportedNames,
public static void addDefaultEOP1980HistoryLoaders(final String rapidDataColumnsSupportedNames,
final String rapidDataXMLSupportedNames,
final String eopC04SupportedNames,
final String bulletinBSupportedNames) {
final String rapidColNames =
(rapidDataColumnsSupportedNames == null) ? RAPID_DATA_PREDICITON_COLUMNS_1980_FILENAME : rapidDataColumnsSupportedNames;
addEOP1980HistoryLoader(new RapidDataAndPredictionColumnsLoader(rapidColNames));
final String rapidXmlNames =
(rapidDataXMLSupportedNames == null) ? RAPID_DATA_PREDICITON_XML_1980_FILENAME : rapidDataXMLSupportedNames;
addEOP1980HistoryLoader(new RapidDataAndPredictionXMLLoader(rapidXmlNames));
final String eopcNames =
(eopC04SupportedNames == null) ? EOPC04_1980_FILENAME : eopC04SupportedNames;
(eopC04SupportedNames == null) ? EOPC04_1980_FILENAME : eopC04SupportedNames;
addEOP1980HistoryLoader(new EOP05C04FilesLoader(eopcNames));
final String bulBNames =
(bulletinBSupportedNames == null) ? BULLETINB_1980_FILENAME : bulletinBSupportedNames;
......@@ -234,7 +260,7 @@ public class FramesFactory implements Serializable {
public static EOP1980History getEOP1980History() throws OrekitException {
final EOP1980History history = new EOP1980History();
if (EOP_1980_LOADERS.isEmpty()) {
addDefaultEOP1980HistoryLoaders(null, null);
addDefaultEOP1980HistoryLoaders(null, null, null, null);
}
for (final EOP1980HistoryLoader loader : EOP_1980_LOADERS) {
loader.fillHistory(history);
......@@ -259,6 +285,12 @@ public class FramesFactory implements Serializable {
* <p>
* The default loaders look for IERS EOP 05 C04 and bulletins B files.
* </p>
* @param rapidDataColumnsSupportedNames regular expression for supported
* rapid data columns EOP files names
* (may be null if the default IERS file names are used)
* @param rapidDataXMLSupportedNames regular expression for supported
* rapid data XML EOP files names
* (may be null if the default IERS file names are used)
* @param eopC04SupportedNames regular expression for supported EOP05 C04 files names
* (may be null if the default IERS file names are used)
* @param bulletinBSupportedNames regular expression for supported bulletin B files names
......@@ -267,10 +299,18 @@ public class FramesFactory implements Serializable {
* @see <a href="http://hpiers.obspm.fr/eoppc/bul/bulb/">IERS bulletins B</a>
* @see #addEOP2000HistoryLoader(EOP2000HistoryLoader)
* @see #clearEOP2000HistoryLoaders()
* @see #addDefaultEOP1980HistoryLoaders(String, String)
* @see #addDefaultEOP1980HistoryLoaders(String, String, String)
*/
public static void addDefaultEOP2000HistoryLoaders(final String eopC04SupportedNames,
public static void addDefaultEOP2000HistoryLoaders(final String rapidDataColumnsSupportedNames,
final String rapidDataXMLSupportedNames,
final String eopC04SupportedNames,
final String bulletinBSupportedNames) {
final String rapidColNames =
(rapidDataColumnsSupportedNames == null) ? RAPID_DATA_PREDICITON_COLUMNS_2000_FILENAME : rapidDataColumnsSupportedNames;
addEOP2000HistoryLoader(new RapidDataAndPredictionColumnsLoader(rapidColNames));
final String rapidXmlNames =
(rapidDataXMLSupportedNames == null) ? RAPID_DATA_PREDICITON_XML_2000_FILENAME : rapidDataXMLSupportedNames;
addEOP2000HistoryLoader(new RapidDataAndPredictionXMLLoader(rapidXmlNames));
final String eopcNames =
(eopC04SupportedNames == null) ? EOPC04_2000_FILENAME : eopC04SupportedNames;
addEOP2000HistoryLoader(new EOP05C04FilesLoader(eopcNames));
......@@ -305,7 +345,7 @@ public class FramesFactory implements Serializable {
public static EOP2000History getEOP2000History() throws OrekitException {
final EOP2000History history = new EOP2000History();
if (EOP_2000_LOADERS.isEmpty()) {
addDefaultEOP2000HistoryLoaders(null, null);
addDefaultEOP2000HistoryLoaders(null, null, null, null);
}
for (final EOP2000HistoryLoader loader : EOP_2000_LOADERS) {
loader.fillHistory(history);
......
/* Copyright 2002-2011 CS Communication & Systèmes
* Licensed to CS Communication & Systèmes (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.frames;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.orekit.data.DataProvidersManager;
import org.orekit.errors.OrekitException;
import org.orekit.time.DateComponents;
/** Loader for IERS rapid data and prediction files in columns format (finals file).
* <p>Rapid data and prediction files contain {@link TimeStampedEntry
* Earth Orientation Parameters} for several years periods, in one file
* only that is updated regularly.</p>
* <p>
* These files contain both the data from IERS Bulletin A and IERS bulletin B.
* This class parses only the part from Bulletin A.
* </p>
* <p>The rapid data and prediction file is recognized thanks to its base name,
* which must match one of the the patterns <code>finals.*</code> or
* <code>finals2000A.*</code> (or the same ending with <code>.gz</code>
* for gzip-compressed files) where * stands for a word like "all", "daily",
* or "data". The file with 2000A in their name correspond to the
* IAU-2000 precession-nutation model whereas the files without any identifier
* correspond to the IAU-1980 precession-nutation model. The files with the all
* suffix start from 1973-01-01, the file with the data suffix start
* from 1992-01-01 and the files with the daily suffix.</p>
* @author Romain Di Costanzo
* @see <a href="http://maia.usno.navy.mil/ser7/readme.finals2000A">file format description at USNO</a>
*/
class RapidDataAndPredictionColumnsLoader implements EOP1980HistoryLoader, EOP2000HistoryLoader {
/** Conversion factor. */
private static final double ARC_SECONDS_TO_RADIANS = 2 * Math.PI / 1296000;
/** Conversion factor. */
private static final double MILLI_ARC_SECONDS_TO_RADIANS = ARC_SECONDS_TO_RADIANS / 1000;
/** Conversion factor. */
private static final double MILLI_SECONDS_TO_SECONDS = 1.0e-3;
/** Field for year, month and day parsing. */
private static final String INTEGER2_FIELD = "((?:\\p{Blank}|\\p{Digit})\\p{Digit})";
/** Field for modified Julian day parsing. */
private static final String MJD_FIELD = "\\p{Blank}+(\\p{Digit}+)(?:\\.00*)";
/** Field for separator parsing. */
private static final String SEPARATOR = "[IP]";
/** Field for real parsing. */
private static final String REAL_FIELD = "\\p{Blank}*(-?\\p{Digit}*\\.\\p{Digit}*)";
/** Start index of the date part of the line. */
private static int DATE_START = 0;
/** end index of the date part of the line. */
private static int DATE_END = 15;
/** Pattern to match the date part of the line (always present). */
private static final Pattern DATE_PATTERN = Pattern.compile(INTEGER2_FIELD + INTEGER2_FIELD + INTEGER2_FIELD + MJD_FIELD);
/** Start index of the pole part of the line. */
private static int POLE_START = 16;
/** end index of the pole part of the line. */
private static int POLE_END = 55;
/** Pattern to match the pole part of the line. */
private static final Pattern POLE_PATTERN = Pattern.compile(SEPARATOR + REAL_FIELD + REAL_FIELD + REAL_FIELD + REAL_FIELD);
/** Start index of the UT1-UTC part of the line. */
private static int UT1_UTC_START = 57;
/** end index of the UT1-UTC part of the line. */
private static int UT1_UTC_END = 78;
/** Pattern to match the UT1-UTC part of the line. */
private static final Pattern UT1_UTC_PATTERN = Pattern.compile(SEPARATOR + REAL_FIELD + REAL_FIELD);
/** Start index of the LOD part of the line. */
private static int LOD_START = 79;
/** end index of the LOD part of the line. */
private static int LOD_END = 93;
/** Pattern to match the LOD part of the line. */
private static final Pattern LOD_PATTERN = Pattern.compile(REAL_FIELD + REAL_FIELD);
/** Start index of the nutation part of the line. */
private static int NUTATION_START = 95;
/** end index of the nutation part of the line. */
private static int NUTATION_END = 134;
/** Pattern to match the nutation part of the line. */
private static final Pattern NUTATION_PATTERN = Pattern.compile(SEPARATOR + REAL_FIELD + REAL_FIELD + REAL_FIELD + REAL_FIELD);
/** History entries for IAU1980. */
private EOP1980History history1980;
/** History entries for IAU2000. */
private EOP2000History history2000;
/** File supported name. */
private String supportedNames;
/**
* Build a loader for IERS bulletins B files.
*
* @param supportedNames
* regular expression for supported files names
*/
public RapidDataAndPredictionColumnsLoader(final String supportedNames) {
this.supportedNames = supportedNames;
}
/** {@inheritDoc} */
public boolean stillAcceptsData() {
return true;
}
/** {@inheritDoc} */
public void loadData(final InputStream input, final String name)
throws OrekitException, IOException {
// set up a reader for line-oriented bulletin B files
final BufferedReader reader = new BufferedReader(new InputStreamReader(input));
// Init
double x = 0;
double y = 0;
double dtu1 = 0;
double lod = 0;
double dpsi = 0;
double deps = 0;
int date = 0;
for (String line = reader.readLine(); line != null; line = reader.readLine()) {
// split the lines in its various columns (some of them can be blank)
final String datePart = (line.length() >= DATE_END) ? line.substring(DATE_START, DATE_END).trim() : "";
final String polePart = (line.length() >= POLE_END) ? line.substring(POLE_START, POLE_END).trim() : "";
final String ut1utcPart = (line.length() >= UT1_UTC_END ) ? line.substring(UT1_UTC_START, UT1_UTC_END).trim() : "";
final String lodPart = (line.length() >= LOD_END) ? line.substring(LOD_START, LOD_END).trim() : "";
final String nutationPart = (line.length() >= NUTATION_END) ? line.substring(NUTATION_START, NUTATION_END).trim() : "";
// parse the date part
final Matcher dateMatcher = DATE_PATTERN.matcher(datePart);
if (dateMatcher.matches()) {
final int yy = Integer.parseInt(dateMatcher.group(1).trim());
final int mm = Integer.parseInt(dateMatcher.group(2).trim());
final int dd = Integer.parseInt(dateMatcher.group(3).trim());
final int yyyy = (yy > 70) ? yy + 1900 : yy + 2000;
date = Integer.parseInt(dateMatcher.group(4).trim());
final int reconstructedDate = new DateComponents(yyyy, mm, dd).getMJD();
if (reconstructedDate != date) {
notifyUnexpectedErrorEncountered(name);
}
} else {
notifyUnexpectedErrorEncountered(name);
}
// parse the pole part
if (polePart.length() == 0) {
// pole part is blank
x = 0;
y = 0;
} else {
final Matcher poleMatcher = POLE_PATTERN.matcher(polePart);
if (poleMatcher.matches()) {
x = ARC_SECONDS_TO_RADIANS * Double.parseDouble(poleMatcher.group(1));
y = ARC_SECONDS_TO_RADIANS * Double.parseDouble(poleMatcher.group(3));
} else {
notifyUnexpectedErrorEncountered(name);
}
}
// parse the UT1-UTC part
if (ut1utcPart.length() == 0) {
// UT1-UTC part is blank
dtu1 = 0;
} else {
final Matcher ut1utcMatcher = UT1_UTC_PATTERN.matcher(ut1utcPart);
if (ut1utcMatcher.matches()) {
dtu1 = Double.parseDouble(ut1utcMatcher.group(1));
} else {
notifyUnexpectedErrorEncountered(name);
}
}
// parse the lod part
if (lodPart.length() == 0) {
// lod part is blank
lod = 0;
} else {
final Matcher lodMatcher = LOD_PATTERN.matcher(lodPart);
if (lodMatcher.matches()) {
lod = MILLI_SECONDS_TO_SECONDS * Double.parseDouble(lodMatcher.group(1));
} else {
notifyUnexpectedErrorEncountered(name);
}
}
// parse the nutation part
if (nutationPart.length() == 0) {
// nutation part is blank
dpsi = 0;
deps = 0;
} else {
final Matcher nutationMatcher = NUTATION_PATTERN.matcher(nutationPart);
if (nutationMatcher.matches()) {
dpsi = MILLI_ARC_SECONDS_TO_RADIANS * Double.parseDouble(nutationMatcher.group(1));
deps = MILLI_ARC_SECONDS_TO_RADIANS * Double.parseDouble(nutationMatcher.group(3));
} else {
notifyUnexpectedErrorEncountered(name);
}
}
if (history1980 != null) {
history1980.addEntry(new EOP1980Entry(date, dtu1, lod, dpsi, deps));
}
if (history2000 != null) {
history2000.addEntry(new EOP2000Entry(date, dtu1, lod, x, y));
}
}
}
/** {@inheritDoc} */
public void fillHistory(final EOP1980History history) throws OrekitException {
synchronized (this) {
history1980 = history;
history2000 = null;
DataProvidersManager.getInstance().feed(supportedNames, this);
}
}
/** {@inheritDoc} */
public void fillHistory(final EOP2000History history) throws OrekitException {
synchronized (this) {
history1980 = null;
history2000 = history;
DataProvidersManager.getInstance().feed(supportedNames, this);
}
}
/** Throw an exception for an unexpected format error.
* @param name name of the file (or zip entry)
* @exception OrekitException always thrown to notify an unexpected error has been
* encountered by the caller
*/
private void notifyUnexpectedErrorEncountered(final String name) throws OrekitException {
throw new OrekitException("file {0} is not a supported IERS data file", name);
}
}
/* Copyright 2002-2011 CS Communication & Systèmes
* Licensed to CS Communication & Systèmes (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.frames;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import org.orekit.data.DataProvidersManager;
import org.orekit.errors.OrekitException;
import org.orekit.time.DateComponents;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
/** Loader for IERS rapid data and prediction file in XML format (finals file).
* <p>Rapid data and prediction file contain {@link TimeStampedEntry
* Earth Orientation Parameters} for several years periods, in one file
* only that is updated regularly.</p>
* <p>The XML EOP files are recognized thanks to their base names, which
* must match one of the the patterns <code>finals.2000A.*.xml</code> or
* <code>finals.*.xml</code> (or the same ending with <code>.gz</code> for
* gzip-compressed files) where * stands for a word like "all", "daily",
* or "data".</p>
* <p>Files containing data (back to 1973) are available at IERS web site: <a
* href="http://www.iers.org/IERS/EN/DataProducts/EarthOrientationData/eop.html">Earth orientation data</a>.</p>
* @author Luc Maisonobe
* @version $Revision$ $Date$
*/
class RapidDataAndPredictionXMLLoader implements EOP1980HistoryLoader, EOP2000HistoryLoader {
/** Conversion factor. */
private static final double ARC_SECONDS_TO_RADIANS = 2 * Math.PI / 1296000;
/** Conversion factor. */
private static final double MILLI_ARC_SECONDS_TO_RADIANS = ARC_SECONDS_TO_RADIANS / 1000;
/** Conversion factor for milli seconds entries. */
private static final double MILLI_SECONDS_TO_SECONDS = 1.0 / 1000.0;
/** Regular expression for supported files names. */
private final String supportedNames;
/** History entries for IAU1980. */
private EOP1980History history1980;
/** History entries for IAU2000. */
private EOP2000History history2000;
/** Build a loader for IERS XML EOP files.
* @param supportedNames regular expression for supported files names
*/
public RapidDataAndPredictionXMLLoader(final String supportedNames) {
this.supportedNames = supportedNames;
}
/** {@inheritDoc} */
public boolean stillAcceptsData() {
return true;
}
/** {@inheritDoc} */
public void loadData(final InputStream input, final String name)
throws IOException, OrekitException {
try {
// set up a reader for line-oriented bulletin B files
final XMLReader reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
reader.setContentHandler(new EOPContentHandler(name));
// read all file, ignoring header
synchronized (this) {
reader.parse(new InputSource(new InputStreamReader(input)));
}
} catch (SAXException se) {
if ((se.getCause() != null) && (se.getCause() instanceof OrekitException)) {
throw (OrekitException) se.getCause();
}
throw new OrekitException(se, "{0}", se.getMessage());
} catch (ParserConfigurationException pce) {
throw new OrekitException(pce, "{0}", pce.getMessage());
}
}
/** {@inheritDoc} */
public void fillHistory(final EOP1980History history)
throws OrekitException {
synchronized (this) {
history1980 = history;
history2000 = null;
DataProvidersManager.getInstance().feed(supportedNames, this);
}
}
/** {@inheritDoc} */
public void fillHistory(final EOP2000History history)
throws OrekitException {
synchronized (this) {
history1980 = null;
history2000 = history;
DataProvidersManager.getInstance().feed(supportedNames, this);
}
}
/** Local content handler for XML EOP files. */
private class EOPContentHandler extends DefaultHandler {
// CHECKSTYLE: stop JavadocVariable check
// elements and attributes used in both daily and finals data files
private static final String MJD_ELT = "MJD";
private static final String LOD_ELT = "LOD";
private static final String X_ELT = "X";
private static final String Y_ELT = "Y";
private static final String DPSI_ELT = "dPsi";
private static final String DEPSILON_ELT = "dEpsilon";
// elements and attributes specific to daily data files
private static final String DATA_EOP_ELT = "dataEOP";
private static final String TIME_SERIES_ELT = "timeSeries";
private static final String DATE_YEAR_ELT = "dateYear";
private static final String DATE_MONTH_ELT = "dateMonth";
private static final String DATE_DAY_ELT = "dateDay";
private static final String POLE_ELT = "pole";
private static final String UT_ELT = "UT";
private static final String UT1_U_UTC_ELT = "UT1_UTC";
private static final String NUTATION_ELT = "nutation";
private static final String SOURCE_ATTR = "source";
private static final String BULLETIN_A_SOURCE = "BulletinA";
// elements and attributes specific to finals data files
private static final String FINALS_ELT = "Finals";
private static final String DATE_ELT = "date";
private static final String EOP_SET_ELT = "EOPSet";
private static final String BULLETIN_A_ELT = "bulletinA";
private static final String UT1_M_UTC_ELT = "UT1-UTC";
private boolean inBulletinA;
private int year;
private int month;
private int day;
private int mjd;
private double dtu1;
private double lod;
private double x;
private double y;
private double dpsi;
private double deps;
// CHECKSTYLE: resume JavadocVariable check
/** File name. */
private final String name;
/** Buffer for read characters. */
private final StringBuffer buffer;
/** Indicator for daily data XML format or final data XML format. */
private DataFileContent content;
/** Simple constructor.
* @param name file name
*/