1   /* Copyright 2002-2025 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.time;
18  
19  import org.orekit.errors.OrekitException;
20  import org.orekit.errors.OrekitMessages;
21  
22  /** This class represents a CCSDS unsegmented time code.
23   * @param <T> type of the date
24   * @author Luc Maisonobe
25   * @since 12.1
26   * @see AbsoluteDate
27   * @see FieldAbsoluteDate
28   */
29  class CcsdsUnsegmentedTimeCode<T> extends AbstractCcsdsTimeCode {
30  
31      /** Numerator of scale of the sub-second part (10¹⁸/256ⁿ).
32       * @since 13.0
33       */
34      private static final long[] SUB_SCALE_NUM = new long[] {
35          3906250000000000L, 15258789062500L, 3814697265625L, 3814697265625L, 3814697265625L, 3814697265625L, 3814697265625L
36      };
37  
38      /** Denominator of scale of the sub-second part (10¹⁸/256ⁿ).
39       * @since 13.0
40       */
41      private static final long[] SUB_SCALE_DEN = new long[] {
42          1L, 1L, 64L, 16384L, 4194304L, 1073741824L, 274877906944L
43      };
44  
45      /** Epoch part. */
46      private final T epoch;
47  
48      /** Time part.
49       * @since 13.0
50       */
51      private final TimeOffset time;
52  
53      /** Create an instance CCSDS Day Unegmented Time Code (CDS).
54       * <p>
55       * CCSDS Unsegmented Time Code is defined in the blue book: CCSDS Time Code Format
56       * (CCSDS 301.0-B-4) published in November 2010
57       * </p>
58       * <p>
59       * If the date to be parsed is formatted using version 3 of the standard (CCSDS
60       * 301.0-B-3 published in 2002) or if the extension of the preamble field introduced
61       * in version 4 of the standard is not used, then the {@code preambleField2} parameter
62       * can be set to 0.
63       * </p>
64       * @param preambleField1     first byte of the field specifying the format, often not
65       *                           transmitted in data interfaces, as it is constant for a
66       *                           given data interface
67       * @param preambleField2     second byte of the field specifying the format (added in
68       *                           revision 4 of the CCSDS standard in 2010), often not
69       *                           transmitted in data interfaces, as it is constant for a
70       *                           given data interface (value ignored if presence not
71       *                           signaled in {@code preambleField1})
72       * @param timeField          byte array containing the time code
73       * @param agencyDefinedEpoch reference epoch, ignored if the preamble field specifies
74       *                           the {@link DateComponents#CCSDS_EPOCH CCSDS reference epoch} is used
75       *                           (and hence may be null in this case, but then {@code ccsdsEpoch} must be non-null)
76       * @param ccsdsEpoch         reference epoch, ignored if the preamble field specifies
77       *                           the agency epoch is used (and hence may be null in this case,
78       *                           but then {@code agencyDefinedEpoch} must be non-null).
79       */
80      CcsdsUnsegmentedTimeCode(final byte preambleField1,
81                               final byte preambleField2,
82                               final byte[] timeField,
83                               final T agencyDefinedEpoch,
84                               final T ccsdsEpoch) {
85  
86          // time code identification and reference epoch
87          switch (preambleField1 & 0x70) {
88              case 0x10:
89                  // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
90                  epoch = ccsdsEpoch;
91                  break;
92              case 0x20:
93                  // the reference epoch is agency defined
94                  if (agencyDefinedEpoch == null) {
95                      throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
96                  }
97                  epoch = agencyDefinedEpoch;
98                  break;
99              default :
100                 throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
101                                           formatByte(preambleField1));
102         }
103 
104         // time field lengths
105         int coarseTimeLength = 1 + ((preambleField1 & 0x0C) >>> 2);
106         int fineTimeLength   = preambleField1 & 0x03;
107 
108         if ((preambleField1 & 0x80) != 0x0) {
109             // there is an additional octet in preamble field
110             coarseTimeLength += (preambleField2 & 0x60) >>> 5;
111             fineTimeLength   += (preambleField2 & 0x1C) >>> 2;
112         }
113 
114         if (timeField.length != coarseTimeLength + fineTimeLength) {
115             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
116                                       timeField.length, coarseTimeLength + fineTimeLength);
117         }
118 
119         long seconds = 0L;
120         for (int i = 0; i < coarseTimeLength; ++i) {
121             seconds = seconds * 256L + toUnsigned(timeField[i]);
122         }
123 
124         long attoSeconds = 0L;
125         for (int i = coarseTimeLength; i < timeField.length; ++i) {
126             attoSeconds += (toUnsigned(timeField[i]) * SUB_SCALE_NUM[i - coarseTimeLength]) /
127                            SUB_SCALE_DEN[i - coarseTimeLength];
128         }
129 
130         time = new TimeOffset(seconds, attoSeconds);
131 
132     }
133 
134     /** Get the epoch part.
135      * @return epoch part
136      */
137     public T getEpoch() {
138         return epoch;
139     }
140 
141     /** Get the time part.
142      * @return time part
143      * @since 13.0
144      */
145     public TimeOffset getTime() {
146         return time;
147     }
148 
149 }