1   /* Copyright 2024-2025 The Johns Hopkins University Applied Physics Laboratory
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.files.iirv.terms;
18  
19  import org.orekit.files.iirv.terms.base.DoubleValuedIIRVTerm;
20  import org.orekit.files.iirv.terms.base.IIRVVectorTerm;
21  import org.orekit.time.AbsoluteDate;
22  import org.orekit.time.TimeComponents;
23  import org.orekit.time.UTCScale;
24  
25  import java.text.DecimalFormat;
26  import java.text.DecimalFormatSymbols;
27  import java.util.Locale;
28  
29  /**
30   * Vector epoch in UTC with resolution to nearest millisecond.
31   * <p>
32   * Valid values:
33   * <p>
34   * hhmmsssss where:
35   * <ul>
36   * <li> hh = 00 to 23
37   * <li> mm = 00 to 59
38   * <li> sssss = 00000 to 59999 (milliseconds, implied decimal point three places from right)
39   * </ul>
40   *
41   * @author Nick LaFarge
42   * @since 13.0
43   */
44  public class VectorEpochTerm extends IIRVVectorTerm<TimeComponents> {
45  
46      /** The length of the IIRV term within the message. */
47      public static final int VECTOR_EPOCH_TERM_LENGTH = 9;
48  
49      /**
50       * Regular expression that ensures the validity of string values for this term.
51       * <p>
52       * String in the form "hhmmsssss":
53       * <ul>
54       * <li> hh is 00 to 23: (0[0-9]|1[0-9]|2[0-3])
55       * <li> mm is 00 to 59: ([0-5][0-9])
56       * <li> sssss is 00000 to 599999: ([0-5][0-9]{4})
57       * </ul>
58       */
59      public static final String VECTOR_EPOCH_TERM_PATTERN = "(0[0-9]|1[0-9]|2[0-3])([0-5][0-9])([0-5][0-9]{4})";
60  
61      /**
62       * Constructs from a String value.
63       *
64       * @param stringValue Day of the year (001-366)
65       */
66      public VectorEpochTerm(final String stringValue) {
67          super(VECTOR_EPOCH_TERM_PATTERN, VectorEpochTerm.fromString(stringValue), VECTOR_EPOCH_TERM_LENGTH);
68      }
69  
70      /**
71       * Constructs from a {@link TimeComponents} value.
72       *
73       * @param timeComponents TimeComponents value to extract vector epoch information from
74       */
75      public VectorEpochTerm(final TimeComponents timeComponents) {
76          super(VECTOR_EPOCH_TERM_PATTERN, timeComponents, VECTOR_EPOCH_TERM_LENGTH);
77      }
78  
79      /**
80       * Constructs from a {@link AbsoluteDate} value.
81       *
82       * @param absoluteDate AbsoluteDate value to extract vector epoch information from (in UTC)
83       * @param utc          UTC time scale
84       */
85      public VectorEpochTerm(final AbsoluteDate absoluteDate, final UTCScale utc) {
86          super(VECTOR_EPOCH_TERM_PATTERN,
87              absoluteDate.getComponents(utc).getTime(),
88              VECTOR_EPOCH_TERM_LENGTH);
89      }
90  
91      /**
92       * Parses an IIRV string in  format to a {@link TimeComponents} instance.
93       * <p>
94       * Format is "hhmmsssss" where the implied decimal place is three from the left (hh mm ss.sss)
95       *
96       * @param iirvString IIRV-formatted string to parse
97       * @return time components contained in the input string
98       */
99      private static TimeComponents fromString(final String iirvString) {
100         final int hour = Integer.parseInt(iirvString.substring(0, 2));
101         final int minute = Integer.parseInt(iirvString.substring(2, 4));
102 
103         // Convert {fullSeconds}.{fractionalSeconds} to seconds
104         final double second = DoubleValuedIIRVTerm.computeValueFromString(iirvString.substring(4, 9), 3);
105 
106         return new TimeComponents(hour, minute, second);
107     }
108 
109     /**
110      * Gets the two-character hour of the vector epoch.
111      *
112      * @return hh: hour of the vector epoch
113      */
114     public String hh() {
115         return toEncodedString().substring(0, 2);
116     }
117 
118     /**
119      * Gets the two-character minute of the vector epoch.
120      *
121      * @return mm: minute of the vector epoch
122      */
123     public String mm() {
124         return toEncodedString().substring(2, 4);
125     }
126 
127     /**
128      * Gets the two-character second of the vector epoch.
129      *
130      * @return ss: second of the vector epoch
131      */
132     public String ss() {
133         return toEncodedString().substring(4, 6);
134     }
135 
136 
137     /** {@inheritDoc} */
138     @Override
139     public String toEncodedString(final TimeComponents value) {
140 
141         final DecimalFormat secondsFormat = new DecimalFormat("00.000", new DecimalFormatSymbols(Locale.US));
142         final String ss_sss = secondsFormat.format(value.getSecond());
143 
144         // Edge case: 60th second doesn't make sense... Round up the hour instead
145         if (ss_sss.charAt(0) == '6') {
146             final int nextSecond = 0;
147             int nextMinute = value.getMinute() + 1;
148             int nextHour = value.getHour();
149             if (nextMinute == 60) {
150                 nextMinute = 0;
151                 nextHour++;
152                 if (nextHour == 24) {
153                     nextHour = 0;
154                 }
155             }
156             return toEncodedString(new TimeComponents(nextHour, nextMinute, nextSecond));
157         }
158 
159         return String.format("%02d%02d%s",
160             value.getHour(),
161             value.getMinute(),
162             ss_sss.replace(".", "")
163         );
164     }
165 }