ParseUtils.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (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.util.Arrays;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;

  22. import org.orekit.errors.OrekitException;
  23. import org.orekit.errors.OrekitMessages;

  24. /** Utility class for TLE parsing, including alpha-5 TLE satellites IDs handling.
  25.  * <p>
  26.  * Alpha-5 extends the range of existing 5 digits TLE satellite numbers
  27.  * by allowing the first digit to be an upper case letter, ignoring 'I'
  28.  * and 'O' to avoid confusion with numbers '1' and '0'.
  29.  * </p>
  30.  * @see <a href="https://www.space-track.org/documentation#tle-alpha5>TLE-alpha5</a>
  31.  * @author Mark rutten
  32.  */
  33. class ParseUtils  {

  34.     /** Letter-number map for satellite number. */
  35.     private static final int MAX_NUMERIC_SATNUM = 99999;

  36.     /** Letter-number map for satellite number. */
  37.     private static final Map<Character, Integer> ALPHA5_NUMBERS;

  38.     /** Number-letter map for satellite number. */
  39.     private static final Map<Integer, Character> ALPHA5_LETTERS;

  40.     /** Scaling factor for alpha5 numbers. */
  41.     private static final int ALPHA5_SCALING = 10000;

  42.     static {
  43.         // Generate maps between TLE satellite alphabetic characters and integers.
  44.         final List<Character> alpha5Letters =
  45.                         Arrays.asList('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J',
  46.                                       'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T',
  47.                                       'U', 'V', 'W', 'X', 'Y', 'Z');
  48.         ALPHA5_NUMBERS = new HashMap<>(alpha5Letters.size());
  49.         ALPHA5_LETTERS = new HashMap<>(alpha5Letters.size());
  50.         for (int i = 0; i < alpha5Letters.size(); ++i) {
  51.             ALPHA5_NUMBERS.put(alpha5Letters.get(i), i + 10);
  52.             ALPHA5_LETTERS.put(i + 10, alpha5Letters.get(i));
  53.         }
  54.     }

  55.     /** Private constructor for a utility class. */
  56.     private ParseUtils() {
  57.         // nothing to do
  58.     }

  59.     /** Build an alpha5 satellite number.
  60.      * @param satelliteNumber satellite number, that may exceed the 99999 limit
  61.      * @param name parameter name
  62.      * @return satellite number in alpha5 representation
  63.      */
  64.     public static String buildSatelliteNumber(final int satelliteNumber, final String name) {
  65.         if (satelliteNumber > MAX_NUMERIC_SATNUM) {
  66.             final int highDigits = satelliteNumber / ALPHA5_SCALING;
  67.             final int lowDigits = satelliteNumber - highDigits * ALPHA5_SCALING;

  68.             final Character alpha = ALPHA5_LETTERS.get(highDigits);
  69.             if (alpha == null) {
  70.                 throw new OrekitException(OrekitMessages.TLE_INVALID_PARAMETER,
  71.                                           satelliteNumber, name, "null");
  72.             }
  73.             return alpha + addPadding(name, lowDigits, '0', 4, true, satelliteNumber);
  74.         } else {
  75.             return addPadding(name, satelliteNumber, '0', 5, true, satelliteNumber);
  76.         }
  77.     }

  78.     /** Add padding characters before an integer.
  79.      * @param name parameter name
  80.      * @param k integer to pad
  81.      * @param c padding character
  82.      * @param size desired size
  83.      * @param rightJustified if true, the resulting string is
  84.      * right justified (i.e. space are added to the left)
  85.      * @param satelliteNumber satellite number
  86.      * @return padded string
  87.      */
  88.     public static String addPadding(final String name, final int k, final char c,
  89.                                     final int size, final boolean rightJustified,
  90.                                     final int satelliteNumber) {
  91.         return addPadding(name, Integer.toString(k), c, size, rightJustified, satelliteNumber);
  92.     }

  93.     /** Add padding characters to a string.
  94.      * @param name parameter name
  95.      * @param string string to pad
  96.      * @param c padding character
  97.      * @param size desired size
  98.      * @param rightJustified if true, the resulting string is
  99.      * right justified (i.e. space are added to the left)
  100.      * @param satelliteNumber satellite number
  101.      * @return padded string
  102.      */
  103.     public static String addPadding(final String name, final String string, final char c,
  104.                                     final int size, final boolean rightJustified,
  105.                                     final int satelliteNumber) {

  106.         if (string.length() > size) {
  107.             throw new OrekitException(OrekitMessages.TLE_INVALID_PARAMETER,
  108.                                       satelliteNumber, name, string);
  109.         }

  110.         final StringBuilder padding = new StringBuilder();
  111.         for (int i = 0; i < size; ++i) {
  112.             padding.append(c);
  113.         }

  114.         if (rightJustified) {
  115.             final String concatenated = padding + string;
  116.             final int l = concatenated.length();
  117.             return concatenated.substring(l - size, l);
  118.         }

  119.         return (string + padding).substring(0, size);

  120.     }

  121.     /** Parse a double.
  122.      * @param line line to parse
  123.      * @param start start index of the first character
  124.      * @param length length of the string
  125.      * @return value of the double
  126.      */
  127.     public static double parseDouble(final String line, final int start, final int length) {
  128.         final String string = line.substring(start, start + length).trim();
  129.         return string.length() > 0 ? Double.parseDouble(string.replace(' ', '0')) : 0;
  130.     }

  131.     /** Parse an integer.
  132.      * @param line line to parse
  133.      * @param start start index of the first character
  134.      * @param length length of the string
  135.      * @return value of the integer
  136.      */
  137.     public static int parseInteger(final String line, final int start, final int length) {
  138.         final String field = line.substring(start, start + length).trim();
  139.         return field.length() > 0 ? Integer.parseInt(field.replace(' ', '0')) : 0;
  140.     }

  141.     /** Parse a satellite number.
  142.      * @param line line to parse
  143.      * @param start start index of the first character
  144.      * @param length length of the string
  145.      * @return value of the integer
  146.      */
  147.     public static int parseSatelliteNumber(final String line, final int start, final int length) {
  148.         String field = line.substring(start, start + length);
  149.         int satelliteNumber;

  150.         final Integer alpha = ALPHA5_NUMBERS.get(field.charAt(0));
  151.         if (alpha != null) {
  152.             satelliteNumber = Integer.parseInt(field.substring(1));
  153.             satelliteNumber += alpha * ALPHA5_SCALING;
  154.         } else {
  155.             field = field.trim();
  156.             satelliteNumber = field.length() > 0 ? Integer.parseInt(field.replace(' ', '0')) : 0;
  157.         }
  158.         return satelliteNumber;
  159.     }

  160.     /** Parse a year written on 2 digits.
  161.      * @param line line to parse
  162.      * @param start start index of the first character
  163.      * @return value of the year
  164.      */
  165.     public static int parseYear(final String line, final int start) {
  166.         final int year = 2000 + parseInteger(line, start, 2);
  167.         return (year > 2056) ? (year - 100) : year;
  168.     }

  169. }