EOP08C04FilesLoader.java

  1. /* Copyright 2002-2013 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.frames;

  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.util.ArrayList;
  23. import java.util.List;
  24. import java.util.SortedSet;
  25. import java.util.regex.Matcher;
  26. import java.util.regex.Pattern;

  27. import org.orekit.data.DataLoader;
  28. import org.orekit.data.DataProvidersManager;
  29. import org.orekit.errors.OrekitException;
  30. import org.orekit.errors.OrekitMessages;
  31. import org.orekit.time.AbsoluteDate;
  32. import org.orekit.time.DateComponents;
  33. import org.orekit.time.TimeScalesFactory;
  34. import org.orekit.utils.Constants;
  35. import org.orekit.utils.IERSConventions;

  36. /** Loader for EOP 08 C04 files.
  37.  * <p>EOP 08 C04 files contain {@link EOPEntry
  38.  * Earth Orientation Parameters} consistent with ITRF2008 for one year periods.</p>
  39.  * <p>The EOP 08 C04 files are recognized thanks to their base names, which
  40.  * must match one of the the patterns <code>eopc04_08_IAU2000.##</code> or
  41.  * <code>eopc04_08.##</code> (or the same ending with <code>.gz</code> for
  42.  * gzip-compressed files) where # stands for a digit character.</p>
  43.  * <p>Between 2002 and 2007, another series of Earth Orientation Parameters was
  44.  * in use: EOPC04 (without the 08). These parameters were consistent with the
  45.  * previous ITRS realization: ITRF2000.</p>
  46.  * <p>Between 2008 and 20011, another series of Earth Orientation Parameters was
  47.  * in use: EOP 05 C04 (instead of 08). These parameters were consistent with the
  48.  * previous ITRS realization: ITRF2005.</p>
  49.  * <p>These files are no longer provided by IERS and only the new files consistent
  50.  * with ITRF 2008 are available now (as of early 2013). The content of the older
  51.  * pre-2005 files is not the same as the content of the new files supported by this class,
  52.  * however IERS uses the same file naming convention for all of them. If a file from the older
  53.  * series is found by this class, a parse error will be triggered. Users must remove
  54.  * such files to avoid being lured in believing they do have EOP data.</p>
  55.  * <p>Files containing old data (back to 1962) have been regenerated in the new file
  56.  * format and are available at IERS web site: <a
  57.  * href="http://hpiers.obspm.fr/eoppc/eop/eopc04/">Index of /eoppc/eop/eopc04</a>.</p>
  58.  * <p>
  59.  * This class is immutable and hence thread-safe
  60.  * </p>
  61.  * @author Luc Maisonobe
  62.  */
  63. class EOP08C04FilesLoader implements EOPHistoryLoader {

  64.     /** Pattern to match the columns header. */
  65.     private static final Pattern COLUMNS_HEADER_PATTERN;

  66.     /** Pattern for data lines. */
  67.     private static final Pattern DATA_LINE_PATTERN;

  68.     /** Year field. */
  69.     private static final int YEAR_FIELD;

  70.     /** Month field. */
  71.     private static final int MONTH_FIELD;

  72.     /** Day field. */
  73.     private static final int DAY_FIELD;

  74.     /** MJD field. */
  75.     private static final int MJD_FIELD;

  76.     /** X component of pole motion field. */
  77.     private static final int POLE_X_FIELD;

  78.     /** Y component of pole motion field. */
  79.     private static final int POLE_Y_FIELD;

  80.     /** UT1-UTC field. */
  81.     private static final int UT1_UTC_FIELD;

  82.     /** LoD field. */
  83.     private static final int LOD_FIELD;

  84.     /** Correction for nutation first field (either dX or dPsi). */
  85.     private static final int NUT_0_FIELD;

  86.     /** Correction for nutation second field (either dY or dEps). */
  87.     private static final int NUT_1_FIELD;

  88.     static {
  89.         // Header have either the following form:
  90.         //       Date      MJD      x          y        UT1-UTC       LOD         dPsi      dEps       x Err     y Err   UT1-UTC Err  LOD Err    dPsi Err   dEpsilon Err
  91.         //                          "          "           s           s            "         "        "          "          s           s            "         "
  92.         //      (0h UTC)
  93.         // or the following form:
  94.         //       Date      MJD      x          y        UT1-UTC       LOD         dX        dY        x Err     y Err   UT1-UTC Err  LOD Err     dX Err       dY Err
  95.         //                          "          "           s           s          "         "           "          "          s         s            "           "
  96.         //      (0h UTC)
  97.         //
  98.         COLUMNS_HEADER_PATTERN = Pattern.compile("^ *Date +MJD +x +y +UT1-UTC +LOD +((?:dPsi +dEps)|(?:dX +dY)) .*");

  99.         // The data lines in the EOP 08 C04 yearly data files have the following fixed form:
  100.         // year month day MJD ...12 floating values fields in decimal format...
  101.         // 2000   1   1  51544   0.043242   0.377915   0.3554777   ...
  102.         // 2000   1   2  51545   0.043515   0.377753   0.3546065   ...
  103.         // 2000   1   3  51546   0.043623   0.377452   0.3538444   ...
  104.         // the corresponding fortran format is:
  105.         // 3(I4),I7,2(F11.6),2(F12.7),2(F12.6),2(F11.6),2(F12.7),2F12.6
  106.         DATA_LINE_PATTERN = Pattern.compile("^\\d+ +\\d+ +\\d+ +\\d+(?: +-?\\d+\\.\\d+){12}$");

  107.         YEAR_FIELD    = 0;
  108.         MONTH_FIELD   = 1;
  109.         DAY_FIELD     = 2;
  110.         MJD_FIELD     = 3;
  111.         POLE_X_FIELD  = 4;
  112.         POLE_Y_FIELD  = 5;
  113.         UT1_UTC_FIELD = 6;
  114.         LOD_FIELD     = 7;
  115.         NUT_0_FIELD   = 8;
  116.         NUT_1_FIELD   = 9;

  117.     }

  118.     /** Regular expression for supported files names. */
  119.     private final String supportedNames;

  120.     /** Build a loader for IERS EOP 08 C04 files.
  121.      * @param supportedNames regular expression for supported files names
  122.      */
  123.     public EOP08C04FilesLoader(final String supportedNames) {
  124.         this.supportedNames = supportedNames;
  125.     }

  126.     /** {@inheritDoc} */
  127.     public void fillHistory(final IERSConventions.NutationCorrectionConverter converter,
  128.                             final SortedSet<EOPEntry> history)
  129.         throws OrekitException {
  130.         final Parser parser = new Parser(converter);
  131.         DataProvidersManager.getInstance().feed(supportedNames, parser);
  132.         history.addAll(parser.history);
  133.     }

  134.     /** Internal class performing the parsing. */
  135.     private static class Parser implements DataLoader {

  136.         /** Converter for nutation corrections. */
  137.         private final IERSConventions.NutationCorrectionConverter converter;

  138.         /** History entries. */
  139.         private final List<EOPEntry> history;

  140.         /** Current line number. */
  141.         private int lineNumber;

  142.         /** Current line. */
  143.         private String line;

  144.         /** Indicator for header parsing. */
  145.         private boolean inHeader;

  146.         /** Indicator for Non-Rotating Origin. */
  147.         private boolean isNonRotatingOrigin;

  148.         /** Simple constructor.
  149.          * @param converter converter to use
  150.          */
  151.         public Parser(final IERSConventions.NutationCorrectionConverter converter) {
  152.             this.converter           = converter;
  153.             this.history             = new ArrayList<EOPEntry>();
  154.             this.lineNumber          = 0;
  155.             this.inHeader            = true;
  156.             this.isNonRotatingOrigin = false;
  157.         }

  158.         /** {@inheritDoc} */
  159.         public boolean stillAcceptsData() {
  160.             return true;
  161.         }

  162.         /** {@inheritDoc} */
  163.         public void loadData(final InputStream input, final String name)
  164.             throws IOException, OrekitException {

  165.             // set up a reader for line-oriented bulletin B files
  166.             final BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));

  167.             // reset parse info to start new file (do not clear history!)
  168.             lineNumber          = 0;
  169.             inHeader            = true;
  170.             isNonRotatingOrigin = false;

  171.             // read all file
  172.             for (line = reader.readLine(); line != null; line = reader.readLine()) {
  173.                 ++lineNumber;
  174.                 boolean parsed = false;

  175.                 if (inHeader) {
  176.                     final Matcher matcher = COLUMNS_HEADER_PATTERN.matcher(line);
  177.                     if (matcher.matches()) {
  178.                         if (matcher.group(1).startsWith("dX")) {
  179.                             isNonRotatingOrigin = true;
  180.                         }
  181.                     }
  182.                 }

  183.                 if (DATA_LINE_PATTERN.matcher(line).matches()) {
  184.                     inHeader = false;
  185.                     // this is a data line, build an entry from the extracted fields
  186.                     final String[] fields = line.split(" +");
  187.                     final DateComponents dc = new DateComponents(Integer.parseInt(fields[YEAR_FIELD]),
  188.                                                                  Integer.parseInt(fields[MONTH_FIELD]),
  189.                                                                  Integer.parseInt(fields[DAY_FIELD]));
  190.                     final int    mjd   = Integer.parseInt(fields[MJD_FIELD]);
  191.                     if (dc.getMJD() != mjd) {
  192.                         throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE,
  193.                                                   name, dc.getYear(), dc.getMonth(), dc.getDay(), mjd);
  194.                     }
  195.                     final AbsoluteDate date = new AbsoluteDate(dc, TimeScalesFactory.getUTC());

  196.                     // the first six fields are consistent with the expected format
  197.                     final double x     = Double.parseDouble(fields[POLE_X_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS;
  198.                     final double y     = Double.parseDouble(fields[POLE_Y_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS;
  199.                     final double dtu1  = Double.parseDouble(fields[UT1_UTC_FIELD]);
  200.                     final double lod   = Double.parseDouble(fields[LOD_FIELD]);
  201.                     final double[] equinox;
  202.                     final double[] nro;
  203.                     if (isNonRotatingOrigin) {
  204.                         nro = new double[] {
  205.                             Double.parseDouble(fields[NUT_0_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS,
  206.                             Double.parseDouble(fields[NUT_1_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS
  207.                         };
  208.                         equinox = converter.toEquinox(date, nro[0], nro[1]);
  209.                     } else {
  210.                         equinox = new double[] {
  211.                             Double.parseDouble(fields[NUT_0_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS,
  212.                             Double.parseDouble(fields[NUT_1_FIELD]) * Constants.ARC_SECONDS_TO_RADIANS
  213.                         };
  214.                         nro = converter.toNonRotating(date, equinox[0], equinox[1]);
  215.                     }
  216.                     history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1]));
  217.                     parsed = true;

  218.                 }
  219.                 if (!(inHeader || parsed)) {
  220.                     throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  221.                                               lineNumber, name, line);
  222.                 }
  223.             }

  224.             // check if we have read something
  225.             if (inHeader) {
  226.                 throw new OrekitException(OrekitMessages.NOT_A_SUPPORTED_IERS_DATA_FILE, name);
  227.             }

  228.         }

  229.     }

  230. }