RapidDataAndPredictionColumnsLoader.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.IERSConventions;

  35. /** Loader for IERS rapid data and prediction files in columns format (finals file).
  36.  * <p>Rapid data and prediction files contain {@link EOPEntry
  37.  * Earth Orientation Parameters} for several years periods, in one file
  38.  * only that is updated regularly.</p>
  39.  * <p>
  40.  * These files contain both the data from IERS Bulletin A and IERS bulletin B.
  41.  * This class parses only the part from Bulletin A.
  42.  * </p>
  43.  * <p>The rapid data and prediction file is recognized thanks to its base name,
  44.  * which must match one of the the patterns <code>finals.*</code> or
  45.  * <code>finals2000A.*</code> (or the same ending with <code>.gz</code>
  46.  * for gzip-compressed files) where * stands for a word like "all", "daily",
  47.  * or "data". The file with 2000A in their name correspond to the
  48.  * IAU-2000 precession-nutation model whereas the files without any identifier
  49.  * correspond to the IAU-1980 precession-nutation model. The files with the all
  50.  * suffix start from 1973-01-01, the file with the data suffix start
  51.  * from 1992-01-01 and the files with the daily suffix.</p>
  52.  * <p>
  53.  * This class is immutable and hence thread-safe
  54.  * </p>
  55.  * @author Romain Di Costanzo
  56.  * @see <a href="http://maia.usno.navy.mil/ser7/readme.finals2000A">finals2000A file format description at USNO</a>
  57.  * @see <a href="http://maia.usno.navy.mil/ser7/readme.finals">finals file format description at USNO</a>
  58.  */
  59. class RapidDataAndPredictionColumnsLoader implements EOPHistoryLoader {

  60.     /** Conversion factor. */
  61.     private static final double  ARC_SECONDS_TO_RADIANS       = 2 * Math.PI / 1296000;

  62.     /** Conversion factor. */
  63.     private static final double  MILLI_ARC_SECONDS_TO_RADIANS = ARC_SECONDS_TO_RADIANS / 1000;

  64.     /** Conversion factor. */
  65.     private static final double  MILLI_SECONDS_TO_SECONDS     = 1.0e-3;

  66.     /** Field for year, month and day parsing. */
  67.     private static final String  INTEGER2_FIELD               = "((?:\\p{Blank}|\\p{Digit})\\p{Digit})";

  68.     /** Field for modified Julian day parsing. */
  69.     private static final String  MJD_FIELD                    = "\\p{Blank}+(\\p{Digit}+)(?:\\.00*)";

  70.     /** Field for separator parsing. */
  71.     private static final String  SEPARATOR                    = "\\p{Blank}*[IP]";

  72.     /** Field for real parsing. */
  73.     private static final String  REAL_FIELD                   = "\\p{Blank}*(-?\\p{Digit}*\\.\\p{Digit}*)";

  74.     /** Start index of the date part of the line. */
  75.     private static int DATE_START = 0;

  76.     /** end index of the date part of the line. */
  77.     private static int DATE_END   = 15;

  78.     /** Pattern to match the date part of the line (always present). */
  79.     private static final Pattern DATE_PATTERN = Pattern.compile(INTEGER2_FIELD + INTEGER2_FIELD + INTEGER2_FIELD + MJD_FIELD);

  80.     /** Start index of the pole part of the line. */
  81.     private static int POLE_START = 16;

  82.     /** end index of the pole part of the line. */
  83.     private static int POLE_END   = 55;

  84.     /** Pattern to match the pole part of the line. */
  85.     private static final Pattern POLE_PATTERN = Pattern.compile(SEPARATOR + REAL_FIELD + REAL_FIELD + REAL_FIELD + REAL_FIELD);

  86.     /** Start index of the UT1-UTC part of the line. */
  87.     private static int UT1_UTC_START = 57;

  88.     /** end index of the UT1-UTC part of the line. */
  89.     private static int UT1_UTC_END   = 78;

  90.     /** Pattern to match the UT1-UTC part of the line. */
  91.     private static final Pattern UT1_UTC_PATTERN = Pattern.compile(SEPARATOR + REAL_FIELD + REAL_FIELD);

  92.     /** Start index of the LOD part of the line. */
  93.     private static int LOD_START = 79;

  94.     /** end index of the LOD part of the line. */
  95.     private static int LOD_END   = 93;

  96.     /** Pattern to match the LOD part of the line. */
  97.     private static final Pattern LOD_PATTERN = Pattern.compile(REAL_FIELD + REAL_FIELD);

  98.     /** Start index of the nutation part of the line. */
  99.     private static int NUTATION_START = 95;

  100.     /** end index of the nutation part of the line. */
  101.     private static int NUTATION_END   = 134;

  102.     /** Pattern to match the nutation part of the line. */
  103.     private static final Pattern NUTATION_PATTERN = Pattern.compile(SEPARATOR + REAL_FIELD + REAL_FIELD + REAL_FIELD + REAL_FIELD);

  104.     /** Type of nutation corrections. */
  105.     private final boolean isNonRotatingOrigin;

  106.     /** File supported name. */
  107.     private final String  supportedNames;

  108.     /** Build a loader for IERS bulletins B files.
  109.      * @param isNonRotatingOrigin if true the supported files <em>must</em>
  110.      * contain &delta;X/&delta;Y nutation corrections, otherwise they
  111.      * <em>must</em> contain &delta;&Delta;&psi;/&delta;&Delta;&epsilon; nutation
  112.      * corrections
  113.      * @param supportedNames regular expression for supported files names
  114.      */
  115.     public RapidDataAndPredictionColumnsLoader(final boolean isNonRotatingOrigin,
  116.                                                final String supportedNames) {
  117.         this.isNonRotatingOrigin = isNonRotatingOrigin;
  118.         this.supportedNames      = supportedNames;
  119.     }

  120.     /** {@inheritDoc} */
  121.     public void fillHistory(final IERSConventions.NutationCorrectionConverter converter,
  122.                             final SortedSet<EOPEntry> history)
  123.         throws OrekitException {
  124.         final Parser parser = new Parser(converter, isNonRotatingOrigin);
  125.         DataProvidersManager.getInstance().feed(supportedNames, parser);
  126.         history.addAll(parser.history);
  127.     }

  128.     /** Internal class performing the parsing. */
  129.     private static class Parser implements DataLoader {

  130.         /** Converter for nutation corrections. */
  131.         private final IERSConventions.NutationCorrectionConverter converter;

  132.         /** Indicator for Non-Rotating Origin. */
  133.         private final boolean isNonRotatingOrigin;

  134.         /** History entries. */
  135.         private final List<EOPEntry> history;

  136.         /** Current line number. */
  137.         private int lineNumber;

  138.         /** Current line. */
  139.         private String line;

  140.         /** Simple constructor.
  141.          * @param converter converter to use
  142.          * @param isNonRotatingOrigin type of nutation correction
  143.          */
  144.         public Parser(final IERSConventions.NutationCorrectionConverter converter,
  145.                       final boolean isNonRotatingOrigin) {
  146.             this.converter           = converter;
  147.             this.isNonRotatingOrigin = isNonRotatingOrigin;
  148.             this.history             = new ArrayList<EOPEntry>();
  149.             this.lineNumber          = 0;
  150.         }

  151.         /** {@inheritDoc} */
  152.         public boolean stillAcceptsData() {
  153.             return true;
  154.         }

  155.         /** {@inheritDoc} */
  156.         public void loadData(final InputStream input, final String name)
  157.             throws OrekitException, IOException {

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

  160.             // reset parse info to start new file (do not clear history!)
  161.             lineNumber = 0;

  162.             for (line = reader.readLine(); line != null; line = reader.readLine()) {

  163.                 lineNumber++;

  164.                 // split the lines in its various columns (some of them can be blank)
  165.                 final String datePart      = (line.length() >= DATE_END)     ? line.substring(DATE_START,       DATE_END)     : "";
  166.                 final String polePart      = (line.length() >= POLE_END)     ? line.substring(POLE_START,       POLE_END)     : "";
  167.                 final String ut1utcPart    = (line.length() >= UT1_UTC_END ) ? line.substring(UT1_UTC_START,    UT1_UTC_END)  : "";
  168.                 final String lodPart       = (line.length() >= LOD_END)      ? line.substring(LOD_START,        LOD_END)      : "";
  169.                 final String nutationPart  = (line.length() >= NUTATION_END) ? line.substring(NUTATION_START,   NUTATION_END) : "";

  170.                 // parse the date part
  171.                 final Matcher dateMatcher = DATE_PATTERN.matcher(datePart);
  172.                 final int mjd;
  173.                 if (dateMatcher.matches()) {
  174.                     final int yy = Integer.parseInt(dateMatcher.group(1).trim());
  175.                     final int mm = Integer.parseInt(dateMatcher.group(2).trim());
  176.                     final int dd = Integer.parseInt(dateMatcher.group(3).trim());
  177.                     final int yyyy = (yy > 70) ? yy + 1900 : yy + 2000;
  178.                     mjd = Integer.parseInt(dateMatcher.group(4).trim());
  179.                     final int reconstructedDate = new DateComponents(yyyy, mm, dd).getMJD();
  180.                     if (reconstructedDate != mjd) {
  181.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  182.                                                   lineNumber, name, line);
  183.                     }
  184.                 } else {
  185.                     throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  186.                                               lineNumber, name, line);
  187.                 }

  188.                 // parse the pole part
  189.                 final double x;
  190.                 final double y;
  191.                 if (polePart.trim().length() == 0) {
  192.                     // pole part is blank
  193.                     x = 0;
  194.                     y = 0;
  195.                 } else {
  196.                     final Matcher poleMatcher = POLE_PATTERN.matcher(polePart);
  197.                     if (poleMatcher.matches()) {
  198.                         x = ARC_SECONDS_TO_RADIANS * Double.parseDouble(poleMatcher.group(1));
  199.                         y = ARC_SECONDS_TO_RADIANS * Double.parseDouble(poleMatcher.group(3));
  200.                     } else {
  201.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  202.                                                   lineNumber, name, line);
  203.                     }
  204.                 }

  205.                 // parse the UT1-UTC part
  206.                 final double dtu1;
  207.                 if (ut1utcPart.trim().length() == 0) {
  208.                     // UT1-UTC part is blank
  209.                     dtu1 = 0;
  210.                 } else {
  211.                     final Matcher ut1utcMatcher = UT1_UTC_PATTERN.matcher(ut1utcPart);
  212.                     if (ut1utcMatcher.matches()) {
  213.                         dtu1 = Double.parseDouble(ut1utcMatcher.group(1));
  214.                     } else {
  215.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  216.                                                   lineNumber, name, line);
  217.                     }
  218.                 }

  219.                 // parse the lod part
  220.                 final double lod;
  221.                 if (lodPart.trim().length() == 0) {
  222.                     // lod part is blank
  223.                     lod = 0;
  224.                 } else {
  225.                     final Matcher lodMatcher = LOD_PATTERN.matcher(lodPart);
  226.                     if (lodMatcher.matches()) {
  227.                         lod = MILLI_SECONDS_TO_SECONDS * Double.parseDouble(lodMatcher.group(1));
  228.                     } else {
  229.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  230.                                                   lineNumber, name, line);
  231.                     }
  232.                 }

  233.                 // parse the nutation part
  234.                 final double[] nro;
  235.                 final double[] equinox;
  236.                 if (nutationPart.trim().length() == 0) {
  237.                     // nutation part is blank
  238.                     nro     = new double[2];
  239.                     equinox = new double[2];
  240.                 } else {
  241.                     final Matcher nutationMatcher = NUTATION_PATTERN.matcher(nutationPart);
  242.                     final AbsoluteDate mjdDate =
  243.                             new AbsoluteDate(new DateComponents(DateComponents.MODIFIED_JULIAN_EPOCH, mjd),
  244.                                              TimeScalesFactory.getUTC());
  245.                     if (nutationMatcher.matches()) {
  246.                         if (isNonRotatingOrigin) {
  247.                             nro = new double[] {
  248.                                 MILLI_ARC_SECONDS_TO_RADIANS * Double.parseDouble(nutationMatcher.group(1)),
  249.                                 MILLI_ARC_SECONDS_TO_RADIANS * Double.parseDouble(nutationMatcher.group(3))
  250.                             };
  251.                             equinox = converter.toEquinox(mjdDate, nro[0], nro[1]);
  252.                         } else {
  253.                             equinox = new double[] {
  254.                                 MILLI_ARC_SECONDS_TO_RADIANS * Double.parseDouble(nutationMatcher.group(1)),
  255.                                 MILLI_ARC_SECONDS_TO_RADIANS * Double.parseDouble(nutationMatcher.group(3))
  256.                             };
  257.                             nro = converter.toNonRotating(mjdDate, equinox[0], equinox[1]);
  258.                         }
  259.                     } else {
  260.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  261.                                                   lineNumber, name, line);
  262.                     }
  263.                 }

  264.                 history.add(new EOPEntry(mjd, dtu1, lod, x, y, equinox[0], equinox[1], nro[0], nro[1]));

  265.             }

  266.         }

  267.     }

  268. }