TAIUTCDatFilesLoader.java

  1. /* Copyright 2002-2018 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.time;

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

  27. import org.hipparchus.util.FastMath;
  28. import org.orekit.data.DataLoader;
  29. import org.orekit.data.DataProvidersManager;
  30. import org.orekit.errors.OrekitException;
  31. import org.orekit.errors.OrekitMessages;

  32. /** Loader for UTC-TAI extracted from tai-utc.dat file from USNO.
  33.  * <p>
  34.  * This class is immutable and hence thread-safe
  35.  * </p>
  36.  * @author Luc Maisonobe
  37.  * @since 7.1
  38.  */
  39. public class TAIUTCDatFilesLoader implements UTCTAIOffsetsLoader {

  40.     /** Default supported files name pattern. */
  41.     public static final String DEFAULT_SUPPORTED_NAMES = "^tai-utc\\.dat$";

  42.     /** Regular expression for supported files names. */
  43.     private final String supportedNames;

  44.     /** Build a loader for tai-utc.dat file from USNO.
  45.      * @param supportedNames regular expression for supported files names
  46.      */
  47.     public TAIUTCDatFilesLoader(final String supportedNames) {
  48.         this.supportedNames = supportedNames;
  49.     }

  50.     /** {@inheritDoc} */
  51.     @Override
  52.     public List<OffsetModel> loadOffsets() throws OrekitException {
  53.         final Parser parser = new Parser();
  54.         DataProvidersManager.getInstance().feed(supportedNames, parser);
  55.         return parser.getOffsets();
  56.     }

  57.     /** Internal class performing the parsing. */
  58.     private static class Parser implements DataLoader {

  59.         /** Regular expression for optional blanks. */
  60.         private static final String BLANKS               = "\\p{Blank}*";

  61.         /** Regular expression for storage start. */
  62.         private static final String STORAGE_START        = "(";

  63.         /** Regular expression for storage end. */
  64.         private static final String STORAGE_END          = ")";

  65.         /** Regular expression for alternative. */
  66.         private static final String ALTERNATIVE          = "|";

  67.         /** Regular expression matching blanks at start of line. */
  68.         private static final String LINE_START_REGEXP     = "^" + BLANKS;

  69.         /** Regular expression matching blanks at end of line. */
  70.         private static final String LINE_END_REGEXP       = BLANKS + "$";

  71.         /** Regular expression matching integers. */
  72.         private static final String INTEGER_REGEXP        = "[-+]?\\p{Digit}+";

  73.         /** Regular expression matching real numbers. */
  74.         private static final String REAL_REGEXP           = "[-+]?(?:(?:\\p{Digit}+(?:\\.\\p{Digit}*)?)|(?:\\.\\p{Digit}+))(?:[eE][-+]?\\p{Digit}+)?";

  75.         /** Regular expression matching an integer field to store. */
  76.         private static final String STORED_INTEGER_FIELD  = BLANKS + STORAGE_START + INTEGER_REGEXP + STORAGE_END;

  77.         /** Regular expression matching a real field to store. */
  78.         private static final String STORED_REAL_FIELD     = BLANKS + STORAGE_START + REAL_REGEXP + STORAGE_END;

  79.         /** Data lines pattern. */
  80.         private Pattern dataPattern;

  81.         /** UTC-TAI offsets. */
  82.         private List<OffsetModel> offsets;

  83.         /** Simple constructor.
  84.          */
  85.         Parser() {

  86.             // data lines read:
  87.             // 1965 SEP  1 =JD 2439004.5  TAI-UTC=   3.8401300 S + (MJD - 38761.) X 0.001296 S
  88.             // 1966 JAN  1 =JD 2439126.5  TAI-UTC=   4.3131700 S + (MJD - 39126.) X 0.002592 S
  89.             // 1968 FEB  1 =JD 2439887.5  TAI-UTC=   4.2131700 S + (MJD - 39126.) X 0.002592 S
  90.             // 1972 JAN  1 =JD 2441317.5  TAI-UTC=  10.0       S + (MJD - 41317.) X 0.0      S
  91.             // 1972 JUL  1 =JD 2441499.5  TAI-UTC=  11.0       S + (MJD - 41317.) X 0.0      S
  92.             // 1973 JAN  1 =JD 2441683.5  TAI-UTC=  12.0       S + (MJD - 41317.) X 0.0      S
  93.             // 1974 JAN  1 =JD 2442048.5  TAI-UTC=  13.0       S + (MJD - 41317.) X 0.0      S

  94.             // month as a three letters upper case abbreviation
  95.             final StringBuilder builder = new StringBuilder(BLANKS + STORAGE_START);
  96.             for (final Month month : Month.values()) {
  97.                 builder.append(month.getUpperCaseAbbreviation());
  98.                 builder.append(ALTERNATIVE);
  99.             }
  100.             builder.delete(builder.length() - 1, builder.length());
  101.             builder.append(STORAGE_END);
  102.             final String monthField = builder.toString();

  103.             dataPattern = Pattern.compile(LINE_START_REGEXP +
  104.                                           STORED_INTEGER_FIELD + monthField + STORED_INTEGER_FIELD +
  105.                                           "\\p{Blank}+=JD" + STORED_REAL_FIELD +
  106.                                           "\\p{Blank}+TAI-UTC=" + STORED_REAL_FIELD +
  107.                                           "\\p{Blank}+S\\p{Blank}+\\+\\p{Blank}+\\(MJD\\p{Blank}+-" + STORED_REAL_FIELD +
  108.                                           "\\p{Blank}*\\)\\p{Blank}+X" + STORED_REAL_FIELD +
  109.                                           "\\p{Blank}*S" + LINE_END_REGEXP);

  110.             offsets = new ArrayList<OffsetModel>();

  111.         }

  112.         /** Get the parsed offsets.
  113.          * @return parsed offsets
  114.          */
  115.         public List<OffsetModel> getOffsets() {
  116.             return offsets;
  117.         }

  118.         /** {@inheritDoc} */
  119.         public boolean stillAcceptsData() {
  120.             return offsets.isEmpty();
  121.         }

  122.         /** Load UTC-TAI offsets entries read from some file.
  123.          * <p>The time steps are extracted from some {@code tai-utc.dat} file.
  124.          * Since entries are stored in a {@link java.util.SortedMap SortedMap},
  125.          * they are chronologically sorted and only one entry remains for a given date.</p>
  126.          * @param input data input stream
  127.          * @param name name of the file (or zip entry)
  128.          * @exception IOException if data can't be read
  129.          * @exception ParseException if data can't be parsed
  130.          * @exception OrekitException if some data is missing
  131.          * or if some loader specific error occurs
  132.          */
  133.         public void loadData(final InputStream input, final String name)
  134.             throws OrekitException, IOException, ParseException {

  135.             offsets.clear();

  136.             // set up a reader for line-oriented file
  137.             final BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));

  138.             // read all file, ignoring not recognized lines
  139.             int lineNumber = 0;
  140.             DateComponents lastDate = null;
  141.             for (String line = reader.readLine(); line != null; line = reader.readLine()) {
  142.                 ++lineNumber;

  143.                 // check matching for data lines
  144.                 final Matcher matcher = dataPattern.matcher(line);
  145.                 if (matcher.matches()) {

  146.                     try {
  147.                         // build an entry from the extracted fields
  148.                         final DateComponents dc1 = new DateComponents(Integer.parseInt(matcher.group(1)),
  149.                                                                       Month.parseMonth(matcher.group(2)),
  150.                                                                       Integer.parseInt(matcher.group(3)));
  151.                         final DateComponents dc2 = new DateComponents(DateComponents.JULIAN_EPOCH,
  152.                                                                       (int) FastMath.ceil(Double.parseDouble(matcher.group(4))));
  153.                         if (!dc1.equals(dc2)) {
  154.                             throw new OrekitException(OrekitMessages.INCONSISTENT_DATES_IN_IERS_FILE,
  155.                                                       name, dc1.getYear(), dc1.getMonth(), dc1.getDay(), dc2.getMJD());
  156.                         }

  157.                         if ((lastDate != null) && dc1.compareTo(lastDate) <= 0) {
  158.                             throw new OrekitException(OrekitMessages.NON_CHRONOLOGICAL_DATES_IN_FILE,
  159.                                                       name, lineNumber);
  160.                         }
  161.                         lastDate = dc1;

  162.                         final double offset = Double.parseDouble(matcher.group(5));
  163.                         final double mjdRef = Double.parseDouble(matcher.group(6));
  164.                         final double slope  = Double.parseDouble(matcher.group(7));
  165.                         offsets.add(new OffsetModel(dc1, (int) FastMath.rint(mjdRef), offset, slope));

  166.                     } catch (NumberFormatException nfe) {
  167.                         throw new OrekitException(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE,
  168.                                                   lineNumber, name, line);
  169.                     }
  170.                 }
  171.             }

  172.             if (offsets.isEmpty()) {
  173.                 throw new OrekitException(OrekitMessages.NO_ENTRIES_IN_IERS_UTC_TAI_HISTORY_FILE, name);
  174.             }

  175.         }

  176.     }

  177. }