TLESeries.java

  1. /* Copyright 2002-2017 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.propagation.analytical.tle;

  18. import java.io.BufferedReader;
  19. import java.io.IOException;
  20. import java.io.InputStream;
  21. import java.io.InputStreamReader;
  22. import java.util.Set;
  23. import java.util.SortedSet;
  24. import java.util.TreeSet;

  25. import org.orekit.data.DataLoader;
  26. import org.orekit.data.DataProvidersManager;
  27. import org.orekit.errors.OrekitException;
  28. import org.orekit.errors.OrekitMessages;
  29. import org.orekit.time.AbsoluteDate;
  30. import org.orekit.time.ChronologicalComparator;
  31. import org.orekit.time.TimeStamped;
  32. import org.orekit.utils.PVCoordinates;

  33. /** This class reads and handles series of TLEs for one space object.
  34.  *  <p>
  35.  *  TLE data is read using the standard Orekit mechanism based on a configured
  36.  *  {@link DataProvidersManager DataProvidersManager}. This means TLE data may
  37.  *  be retrieved from many different storage media (local disk files, remote servers,
  38.  *  database ...).
  39.  *  </p>
  40.  *  <p>
  41.  *  This class provides bounded ephemerides by finding the best initial TLE to
  42.  *  propagate and then handling the propagation.
  43.  *  </p>
  44.  *
  45.  * @see TLE
  46.  * @see DataProvidersManager
  47.  * @author Fabien Maussion
  48.  * @author Luc Maisonobe
  49.  * @deprecated as of 9.0, this class is deprecated without replacement. The file format
  50.  * used was considered to be too specific and the API not really well designed. Users are
  51.  * encouraged to use their own parser for series of TLE
  52.  */
  53. @Deprecated
  54. public class TLESeries implements DataLoader {

  55.     /** Default supported files name pattern. */
  56.     private static final String DEFAULT_SUPPORTED_NAMES = ".*\\.tle$";

  57.     /** Regular expression for supported files names. */
  58.     private final String supportedNames;

  59.     /** Available satellite numbers. */
  60.     private final Set<Integer> availableSatNums;

  61.     /** Set containing all TLE entries. */
  62.     private final SortedSet<TimeStamped> tles;

  63.     /** Satellite number used for filtering. */
  64.     private int filterSatelliteNumber;

  65.     /** Launch year used for filtering (all digits). */
  66.     private int filterLaunchYear;

  67.     /** Launch number used for filtering. */
  68.     private int filterLaunchNumber;

  69.     /** Launch piece used for filtering. */
  70.     private String filterLaunchPiece;

  71.     /** Previous TLE in the cached selection. */
  72.     private TLE previous;

  73.     /** Next TLE in the cached selection. */
  74.     private TLE next;

  75.     /** Last used TLE. */
  76.     private TLE lastTLE;

  77.     /** Associated propagator. */
  78.     private TLEPropagator lastPropagator;

  79.     /** Date of the first TLE. */
  80.     private AbsoluteDate firstDate;

  81.     /** Date of the last TLE. */
  82.     private AbsoluteDate lastDate;

  83.     /** Indicator for non-TLE extra lines. */
  84.     private final boolean ignoreNonTLELines;

  85.     /** Simple constructor with a TLE file.
  86.      * <p>This constructor does not load any data by itself. Data must be
  87.      * loaded later on by calling one of the {@link #loadTLEData()
  88.      * loadTLEData()} method, the {@link #loadTLEData(int)
  89.      * loadTLEData(filterSatelliteNumber)} method or the {@link #loadTLEData(int,
  90.      * int, String) loadTLEData(filterLaunchYear, filterLaunchNumber, filterLaunchPiece)} method.<p>
  91.      * @param supportedNames regular expression for supported files names
  92.      * (if null, a default pattern matching files with a ".tle" extension will be used)
  93.      * @param ignoreNonTLELines if true, extra non-TLE lines are silently ignored,
  94.      * if false an exception will be generated when such lines are encountered
  95.      * @see #loadTLEData()
  96.      * @see #loadTLEData(int)
  97.      * @see #loadTLEData(int, int, String)
  98.      */
  99.     public TLESeries(final String supportedNames, final boolean ignoreNonTLELines) {

  100.         this.supportedNames    = (supportedNames == null) ? DEFAULT_SUPPORTED_NAMES : supportedNames;
  101.         availableSatNums       = new TreeSet<Integer>();
  102.         this.ignoreNonTLELines = ignoreNonTLELines;
  103.         filterSatelliteNumber  = -1;
  104.         filterLaunchYear       = -1;
  105.         filterLaunchNumber     = -1;
  106.         filterLaunchPiece      = null;

  107.         tles     = new TreeSet<TimeStamped>(new ChronologicalComparator());
  108.         previous = null;
  109.         next     = null;

  110.     }

  111.     /** Load TLE data for a specified object.
  112.      * <p>The TLE data already loaded in the instance will be discarded
  113.      * and replaced by the newly loaded data.</p>
  114.      * <p>The filtering values will be automatically set to the first loaded
  115.      * satellite. This feature is useful when the satellite selection is
  116.      * already set up by either the instance configuration (supported file
  117.      * names) or by the {@link DataProvidersManager data providers manager}
  118.      * configuration and the local filtering feature provided here can be ignored.</p>
  119.      * @exception OrekitException if some data can't be read, some
  120.      * file content is corrupted or no TLE data is available
  121.      * @see #loadTLEData(int)
  122.      * @see #loadTLEData(int, int, String)
  123.      */
  124.     public void loadTLEData() throws OrekitException {

  125.         availableSatNums.clear();

  126.         // set the filtering parameters
  127.         filterSatelliteNumber = -1;
  128.         filterLaunchYear      = -1;
  129.         filterLaunchNumber    = -1;
  130.         filterLaunchPiece     = null;

  131.         // load the data from the configured data providers
  132.         tles.clear();
  133.         previous = null;
  134.         next     = null;
  135.         DataProvidersManager.getInstance().feed(supportedNames, this);
  136.         if (tles.isEmpty()) {
  137.             throw new OrekitException(OrekitMessages.NO_TLE_DATA_AVAILABLE);
  138.         }

  139.     }

  140.     /** Get the available satellite numbers.
  141.      * @return available satellite numbers
  142.      * @throws OrekitException if some data can't be read, some
  143.      * file content is corrupted or no TLE data is available
  144.      */
  145.     public Set<Integer> getAvailableSatelliteNumbers() throws OrekitException {
  146.         if (availableSatNums.isEmpty()) {
  147.             loadTLEData();
  148.         }
  149.         return availableSatNums;
  150.     }

  151.     /** Load TLE data for a specified object.
  152.      * <p>The TLE data already loaded in the instance will be discarded
  153.      * and replaced by the newly loaded data.</p>
  154.      * <p>Calling this method with the satellite number set to a negative value,
  155.      * is equivalent to call {@link #loadTLEData()}.</p>
  156.      * @param satelliteNumber satellite number
  157.      * @exception OrekitException if some data can't be read, some
  158.      * file content is corrupted or no TLE data is available for the selected object
  159.      * @see #loadTLEData()
  160.      * @see #loadTLEData(int, int, String)
  161.      */
  162.     public void loadTLEData(final int satelliteNumber) throws OrekitException {

  163.         if (satelliteNumber < 0) {
  164.             // no filtering at all
  165.             loadTLEData();
  166.         } else {
  167.             // set the filtering parameters
  168.             filterSatelliteNumber = satelliteNumber;
  169.             filterLaunchYear      = -1;
  170.             filterLaunchNumber    = -1;
  171.             filterLaunchPiece     = null;

  172.             // load the data from the configured data providers
  173.             tles.clear();
  174.             previous = null;
  175.             next     = null;
  176.             DataProvidersManager.getInstance().feed(supportedNames, this);
  177.             if (tles.isEmpty()) {
  178.                 throw new OrekitException(OrekitMessages.NO_TLE_FOR_OBJECT, satelliteNumber);
  179.             }
  180.         }

  181.     }

  182.     /** Load TLE data for a specified object.
  183.      * <p>The TLE data already loaded in the instance will be discarded
  184.      * and replaced by the newly loaded data.</p>
  185.      * <p>Calling this method with either the launch year or the launch number
  186.      * set to a negative value, or the launch piece set to null or an empty
  187.      * string are all equivalent to call {@link #loadTLEData()}.</p>
  188.      * @param launchYear launch year (all digits)
  189.      * @param launchNumber launch number
  190.      * @param launchPiece launch piece
  191.      * @exception OrekitException if some data can't be read, some
  192.      * file content is corrupted or no TLE data is available for the selected object
  193.      * @see #loadTLEData()
  194.      * @see #loadTLEData(int)
  195.      */
  196.     public void loadTLEData(final int launchYear, final int launchNumber,
  197.                             final String launchPiece) throws OrekitException {

  198.         if ((launchYear < 0) || (launchNumber < 0) ||
  199.             (launchPiece == null) || (launchPiece.length() == 0)) {
  200.             // no filtering at all
  201.             loadTLEData();
  202.         } else {
  203.             // set the filtering parameters
  204.             filterSatelliteNumber = -1;
  205.             filterLaunchYear      = launchYear;
  206.             filterLaunchNumber    = launchNumber;
  207.             filterLaunchPiece     = launchPiece;

  208.             // load the data from the configured data providers
  209.             tles.clear();
  210.             previous = null;
  211.             next     = null;
  212.             DataProvidersManager.getInstance().feed(supportedNames, this);
  213.             if (tles.isEmpty()) {
  214.                 throw new OrekitException(OrekitMessages.NO_TLE_FOR_LAUNCH_YEAR_NUMBER_PIECE,
  215.                                           launchYear, launchNumber, launchPiece);
  216.             }
  217.         }

  218.     }

  219.     /** {@inheritDoc} */
  220.     public boolean stillAcceptsData() {
  221.         return tles.isEmpty();
  222.     }

  223.     /** {@inheritDoc} */
  224.     public void loadData(final InputStream input, final String name)
  225.         throws IOException, OrekitException {

  226.         final BufferedReader r = new BufferedReader(new InputStreamReader(input, "UTF-8"));
  227.         try {

  228.             int lineNumber     = 0;
  229.             String pendingLine = null;
  230.             for (String line = r.readLine(); line != null; line = r.readLine()) {

  231.                 ++lineNumber;

  232.                 if (pendingLine == null) {

  233.                     // we must wait for the second line
  234.                     pendingLine = line;

  235.                 } else {

  236.                     // safety checks
  237.                     if (!TLE.isFormatOK(pendingLine, line)) {
  238.                         if (ignoreNonTLELines) {
  239.                             // just shift one line
  240.                             pendingLine = line;
  241.                             continue;
  242.                         } else {
  243.                             throw new OrekitException(OrekitMessages.NOT_TLE_LINES,
  244.                                                       lineNumber - 1, lineNumber, pendingLine, line);
  245.                         }
  246.                     }

  247.                     final TLE tle = new TLE(pendingLine, line);

  248.                     if (filterSatelliteNumber < 0) {
  249.                         if ((filterLaunchYear < 0) ||
  250.                             ((tle.getLaunchYear()   == filterLaunchYear) &&
  251.                              (tle.getLaunchNumber() == filterLaunchNumber) &&
  252.                              tle.getLaunchPiece().equals(filterLaunchPiece))) {
  253.                             // we now know the number of the object to load
  254.                             filterSatelliteNumber = tle.getSatelliteNumber();
  255.                         }
  256.                     }

  257.                     availableSatNums.add(tle.getSatelliteNumber());

  258.                     if (tle.getSatelliteNumber() == filterSatelliteNumber) {
  259.                         // accept this TLE
  260.                         tles.add(tle);
  261.                     }

  262.                     // we need to wait for two new lines
  263.                     pendingLine = null;

  264.                 }

  265.             }

  266.             if ((pendingLine != null) && !ignoreNonTLELines) {
  267.                 // there is an unexpected last line
  268.                 throw new OrekitException(OrekitMessages.MISSING_SECOND_TLE_LINE,
  269.                                           lineNumber, pendingLine);
  270.             }

  271.         } finally {
  272.             r.close();
  273.         }

  274.     }

  275.     /** Get the extrapolated position and velocity from an initial date.
  276.      * For a good precision, this date should not be too far from the range :
  277.      * [{@link #getFirstDate() first date} ; {@link #getLastDate() last date}].
  278.      * @param date the final date
  279.      * @return the final PVCoordinates
  280.      * @exception OrekitException if the underlying propagator cannot be initialized
  281.      */
  282.     public PVCoordinates getPVCoordinates(final AbsoluteDate date)
  283.         throws OrekitException {
  284.         final TLE toExtrapolate = getClosestTLE(date);
  285.         if (toExtrapolate != lastTLE) {
  286.             lastTLE = toExtrapolate;
  287.             lastPropagator = TLEPropagator.selectExtrapolator(lastTLE);
  288.         }
  289.         return lastPropagator.getPVCoordinates(date);
  290.     }

  291.     /** Get the closest TLE to the selected date.
  292.      * @param date the date
  293.      * @return the TLE that will suit the most for propagation.
  294.      */
  295.     public TLE getClosestTLE(final AbsoluteDate date) {

  296.         //  don't search if the cached selection is fine
  297.         if ((previous != null) && (date.durationFrom(previous.getDate()) >= 0) &&
  298.             (next     != null) && (date.durationFrom(next.getDate())     <= 0)) {
  299.             // the current selection is already good
  300.             if (next.getDate().durationFrom(date) > date.durationFrom(previous.getDate())) {
  301.                 return previous;
  302.             } else {
  303.                 return next;
  304.             }
  305.         }
  306.         // reset the selection before the search phase
  307.         previous  = null;
  308.         next      = null;
  309.         final SortedSet<TimeStamped> headSet = tles.headSet(date);
  310.         final SortedSet<TimeStamped> tailSet = tles.tailSet(date);


  311.         if (headSet.isEmpty()) {
  312.             return (TLE) tailSet.first();
  313.         }
  314.         if (tailSet.isEmpty()) {
  315.             return (TLE) headSet.last();
  316.         }
  317.         previous = (TLE) headSet.last();
  318.         next = (TLE) tailSet.first();

  319.         if (next.getDate().durationFrom(date) > date.durationFrom(previous.getDate())) {
  320.             return previous;
  321.         } else {
  322.             return next;
  323.         }
  324.     }

  325.     /** Get the start date of the series.
  326.      * @return the first date
  327.      */
  328.     public AbsoluteDate getFirstDate() {
  329.         if (firstDate == null) {
  330.             firstDate = tles.first().getDate();
  331.         }
  332.         return firstDate;
  333.     }

  334.     /** Get the last date of the series.
  335.      * @return the end date
  336.      */
  337.     public AbsoluteDate getLastDate() {
  338.         if (lastDate == null) {
  339.             lastDate = tles.last().getDate();
  340.         }
  341.         return lastDate;
  342.     }

  343.     /** Get the first TLE.
  344.      * @return first TLE
  345.      */
  346.     public TLE getFirst() {
  347.         return (TLE) tles.first();
  348.     }

  349.     /** Get the last TLE.
  350.      * @return last TLE
  351.      */
  352.     public TLE getLast() {
  353.         return (TLE) tles.last();
  354.     }

  355. }