- /* Copyright 2002-2018 CS Systèmes d'Information
- * Licensed to CS Systèmes d'Information (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.ArrayList;
- import java.util.List;
- import java.util.SortedSet;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import org.orekit.data.DataLoader;
- import org.orekit.data.DataProvidersManager;
- import org.orekit.errors.OrekitException;
- import org.orekit.errors.OrekitMessages;
- import org.orekit.time.AbsoluteDate;
- import org.orekit.time.DateComponents;
- import org.orekit.time.TimeScalesFactory;
- import org.orekit.utils.Constants;
- import org.orekit.utils.IERSConventions;
- /** Loader for EOP xx C04 files.
- * <p>EOP xx C04 files contain {@link EOPEntry
- * Earth Orientation Parameters} consistent with ITRF20xx for one year periods, with various
- * xx (05, 08, 14) depending on the data source.</p>
- * <p>The EOP xx C04 files retrieved from the ftp site
- * <a href="ftp://ftp.iers.org/products/eop/long-term/">ftp://ftp.iers.org/products/eop/long-term/</a>
- * are recognized thanks to their base names, which must match one of the patterns
- * {@code eopc04_##_IAU2000.##} or {@code eopc04_##.##} (or the same ending with <code>.gz</code> for
- * gzip-compressed files) where # stands for a digit character.</p>
- * <p>
- * Beware that files retrieved from the web site
- * <a href="http://hpiers.obspm.fr/eoppc/eop/">http://hpiers.obspm.fr/eoppc/eop/</a> do
- * <em>not</em> follow the same naming convention. They lack the _05, _08 or _14
- * marker (and for EOP 14 C04 even the directory does not have this marker anymore...),
- * so all the files in this site have the same name and can easily be confused. This is the reason
- * why the default regular expression in {@link FramesFactory} does not recognize them and
- * enforces the year marker presence.
- * </p>
- * <p>Between 2002 and 2007, another series of Earth Orientation Parameters was
- * in use: EOPC04 (without the _##). These parameters were consistent with the
- * previous ITRS realization: ITRF2000.</p>
- * <p>Since 2008, several series of Earth Orientation Parameters have been available:
- * EOP 05 C04 consistent with ITRF 2005, EOP 08 C04 consistent with ITRF 2008, and
- * EOP 14 C04 consistent with ITRF 2014. As of mid 2017, only the EOP 08 C04 and
- * EOP 14 C04 are updated for current years.</p>
- * <p>Files are available at IERS FTP site: <a
- * href="ftp://ftp.iers.org/products/eop/long-term/">ftp://ftp.iers.org/products/eop/long-term/</a>.</p>
- * <p>
- * This class is immutable and hence thread-safe
- * </p>
- * @author Luc Maisonobe
- */
- class EOPC04FilesLoader implements EOPHistoryLoader {
- /** Pattern to match the columns header. */
- private static final Pattern COLUMNS_HEADER_PATTERN;
- /** Pattern for data lines. */
- private static final Pattern DATA_LINE_PATTERN;
- /** Year field. */
- private static final int YEAR_FIELD;
- /** Month field. */
- private static final int MONTH_FIELD;
- /** Day field. */
- private static final int DAY_FIELD;
- /** MJD field. */
- private static final int MJD_FIELD;
- /** X component of pole motion field. */
- private static final int POLE_X_FIELD;
- /** Y component of pole motion field. */
- private static final int POLE_Y_FIELD;
- /** UT1-UTC field. */
- private static final int UT1_UTC_FIELD;
- /** LoD field. */
- private static final int LOD_FIELD;
- /** Correction for nutation first field (either dX or dPsi). */
- private static final int NUT_0_FIELD;
- /** Correction for nutation second field (either dY or dEps). */
- private static final int NUT_1_FIELD;
- static {
- // Header have either the following form:
- // Date MJD x y UT1-UTC LOD dPsi dEps x Err y Err UT1-UTC Err LOD Err dPsi Err dEpsilon Err
- // " " s s " " " " s s " "
- // (0h UTC)
- // or the following form:
- // Date MJD x y UT1-UTC LOD dX dY x Err y Err UT1-UTC Err LOD Err dX Err dY Err
- // " " s s " " " " s s " "
- // (0h UTC)
- //
- COLUMNS_HEADER_PATTERN = Pattern.compile("^ *Date +MJD +x +y +UT1-UTC +LOD +((?:dPsi +dEps)|(?:dX +dY)) .*");
- // The data lines in the EOP C04 yearly data files have the following fixed form:
- // year month day MJD ...12 floating values fields in decimal format...
- // 2000 1 1 51544 0.043242 0.377915 0.3554777 ...
- // 2000 1 2 51545 0.043515 0.377753 0.3546065 ...
- // 2000 1 3 51546 0.043623 0.377452 0.3538444 ...
- // the corresponding fortran format is:
- // 3(I4),I7,2(F11.6),2(F12.7),2(F12.6),2(F11.6),2(F12.7),2F12.6
- DATA_LINE_PATTERN = Pattern.compile("^\\d+ +\\d+ +\\d+ +\\d+(?: +-?\\d+\\.\\d+){12}$");
- YEAR_FIELD = 0;
- MONTH_FIELD = 1;
- DAY_FIELD = 2;
- MJD_FIELD = 3;
- POLE_X_FIELD = 4;
- POLE_Y_FIELD = 5;
- UT1_UTC_FIELD = 6;
- LOD_FIELD = 7;
- NUT_0_FIELD = 8;
- NUT_1_FIELD = 9;
- }
- /** Regular expression for supported files names. */
- private final String supportedNames;
- /** Build a loader for IERS EOP C04 files.
- * @param supportedNames regular expression for supported files names
- */
- EOPC04FilesLoader(final String supportedNames) {
- this.supportedNames = supportedNames;
- }
- /** {@inheritDoc} */
- public void fillHistory(final IERSConventions.NutationCorrectionConverter converter,
- final SortedSet<EOPEntry> history)
- throws OrekitException {
- final Parser parser = new Parser(converter);
- DataProvidersManager.getInstance().feed(supportedNames, parser);
- history.addAll(parser.history);
- }
- /** Internal class performing the parsing. */
- private static class Parser implements DataLoader {
- /** Converter for nutation corrections. */
- private final IERSConventions.NutationCorrectionConverter converter;
- /** Configuration for ITRF versions. */
- private final ITRFVersionLoader itrfVersionLoader;
- /** History entries. */
- private final List<EOPEntry> history;
- /** Current line number. */
- private int lineNumber;
- /** Current line. */
- private String line;
- /** Indicator for header parsing. */
- private boolean inHeader;
- /** Indicator for Non-Rotating Origin. */
- private boolean isNonRotatingOrigin;
- /** Simple constructor.
- * @param converter converter to use
- * @exception OrekitException if ITRF version loader cannot be parsed
- */
- Parser(final IERSConventions.NutationCorrectionConverter converter)
- throws OrekitException {
- this.converter = converter;
- this.itrfVersionLoader = new ITRFVersionLoader(ITRFVersionLoader.SUPPORTED_NAMES);
- this.history = new ArrayList<EOPEntry>();
- this.lineNumber = 0;
- this.inHeader = true;
- this.isNonRotatingOrigin = false;
- }
- /** {@inheritDoc} */
- public boolean stillAcceptsData() {
- return true;
- }
- /** {@inheritDoc} */
- public void loadData(final InputStream input, final String name)
- throws IOException, OrekitException {
- ITRFVersionLoader.ITRFVersionConfiguration configuration = null;
- // set up a reader for line-oriented bulletin B files
- final BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
- // reset parse info to start new file (do not clear history!)
- lineNumber = 0;
- inHeader = true;
- isNonRotatingOrigin = false;
- // read all file
- for (line = reader.readLine(); line != null; line = reader.readLine()) {
- ++lineNumber;
- boolean parsed = false;
- if (inHeader) {
- final Matcher matcher = COLUMNS_HEADER_PATTERN.matcher(line);
- if (matcher.matches()) {
- if (matcher.group(1).startsWith("dX")) {
- isNonRotatingOrigin = true;
- }
- }
- }
- if (DATA_LINE_PATTERN.matcher(line).matches()) {
- inHeader = false;
- // this is a data line, build an entry from the extracted fields
- final String[] fields = line.split(" +");
- final DateComponents dc = new DateComponents(Integer.parseInt(fields[YEAR_FIELD]),
- Integer.parseInt(fields[MONTH_FIELD]),
- Integer.parseInt(fields[DAY_FIELD]));
- final int mjd = Integer.parseInt(fields[MJD_FIELD]);
- if (dc.getMJD() != mjd) {
- throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE,
- name, dc.getYear(), dc.getMonth(), dc.getDay(), mjd);
- }
- final AbsoluteDate date = new AbsoluteDate(dc, TimeScalesFactory.getUTC());
- // the first six fields are consistent with the expected format
- final double x = Double.parseDouble(fields[POLE_X_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS;
- final double y = Double.parseDouble(fields[POLE_Y_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS;
- final double dtu1 = Double.parseDouble(fields[UT1_UTC_FIELD]);
- final double lod = Double.parseDouble(fields[LOD_FIELD]);
- final double[] equinox;
- final double[] nro;
- if (isNonRotatingOrigin) {
- nro = new double[] {
- Double.parseDouble(fields[NUT_0_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS,
- Double.parseDouble(fields[NUT_1_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS
- };
- equinox = converter.toEquinox(date, nro[0], nro[1]);
- } else {
- equinox = new double[] {
- Double.parseDouble(fields[NUT_0_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS,
- Double.parseDouble(fields[NUT_1_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS
- };
- nro = converter.toNonRotating(date, equinox[0], equinox[1]);
- }
- if (configuration == null || !configuration.isValid(mjd)) {
- // get a configuration for current name and date range
- configuration = itrfVersionLoader.getConfiguration(name, mjd);
- }
- history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1],
- configuration.getVersion()));
- parsed = true;
- }
- if (!(inHeader || parsed)) {
- throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
- lineNumber, name, line);
- }
- }
- // check if we have read something
- if (inHeader) {
- throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, name);
- }
- }
- }
- }