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.time;
18  
19  import org.orekit.errors.OrekitException;
20  import org.orekit.errors.OrekitMessages;
21  
22  /** This class represents a CCSDS segmented time code.
23   * @author Luc Maisonobe
24   * @since 12.1
25   * @see AbsoluteDate
26   * @see FieldAbsoluteDate
27   */
28  class CcsdsSegmentedTimeCode extends AbstractCcsdsTimeCode {
29  
30      /** Date part. */
31      private final DateComponents date;
32  
33      /** Time part. */
34      private final TimeComponents time;
35  
36      /** Create an instance CCSDS Day Segmented Time Code (CDS).
37       * <p>
38       * CCSDS Day Segmented Time Code is defined in the blue book:
39       * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
40       * </p>
41       * @param preambleField field specifying the format, often not transmitted in
42       * data interfaces, as it is constant for a given data interface
43       * @param timeField byte array containing the time code
44       * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
45       * specifies the {@link DateComponents#CCSDS_EPOCH CCSDS reference epoch} is used
46       * (and hence may be null in this case)
47       */
48      CcsdsSegmentedTimeCode(final byte preambleField, final byte[] timeField,
49                             final DateComponents agencyDefinedEpoch) {
50  
51          // time code identification
52          if ((preambleField & 0xF0) != 0x40) {
53              throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
54                                        formatByte(preambleField));
55          }
56  
57          // reference epoch
58          final DateComponents epoch;
59          if ((preambleField & 0x08) == 0x00) {
60              // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
61              epoch = DateComponents.CCSDS_EPOCH;
62          } else {
63              // the reference epoch is agency defined
64              if (agencyDefinedEpoch == null) {
65                  throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
66              }
67              epoch = agencyDefinedEpoch;
68          }
69  
70          // time field lengths
71          final int daySegmentLength = ((preambleField & 0x04) == 0x0) ? 2 : 3;
72          final int subMillisecondLength = (preambleField & 0x03) << 1;
73          if (subMillisecondLength == 6) {
74              throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
75                                        formatByte(preambleField));
76          }
77          if (timeField.length != daySegmentLength + 4 + subMillisecondLength) {
78              throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
79                                        timeField.length, daySegmentLength + 4 + subMillisecondLength);
80          }
81  
82  
83          int i   = 0;
84          int day = 0;
85          while (i < daySegmentLength) {
86              day = day * 256 + toUnsigned(timeField[i++]);
87          }
88  
89          long milliInDay = 0L;
90          while (i < daySegmentLength + 4) {
91              milliInDay = milliInDay * 256 + toUnsigned(timeField[i++]);
92          }
93          final int milli   = (int) (milliInDay % 1000L);
94          final int seconds = (int) ((milliInDay - milli) / 1000L);
95  
96          long subMilli = 0;
97          while (i < timeField.length) {
98              subMilli = subMilli * 256 + toUnsigned(timeField[i++]);
99          }
100         final TimeOffset timeOffset =
101             new TimeOffset(seconds, TimeOffset.SECOND,
102                            milli, TimeOffset.MILLISECOND,
103                            subMilli, subMillisecondLength == 2 ? TimeOffset.MICROSECOND : TimeOffset.PICOSECOND);
104 
105         this.date = new DateComponents(epoch, day);
106         this.time = new TimeComponents(timeOffset);
107 
108     }
109 
110     /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
111      * <p>
112      * CCSDS Calendar Segmented Time Code is defined in the blue book:
113      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
114      * </p>
115      * @param preambleField field specifying the format, often not transmitted in
116      * data interfaces, as it is constant for a given data interface
117      * @param timeField byte array containing the time code
118      */
119     CcsdsSegmentedTimeCode(final byte preambleField, final byte[] timeField) {
120 
121         // time code identification
122         if ((preambleField & 0xF0) != 0x50) {
123             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
124                                       formatByte(preambleField));
125         }
126 
127         // time field length
128         final int length = 7 + (preambleField & 0x07);
129         if (length == 14) {
130             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
131                                       formatByte(preambleField));
132         }
133         if (timeField.length != length) {
134             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
135                                       timeField.length, length);
136         }
137 
138         // date part in the first four bytes
139         if ((preambleField & 0x08) == 0x00) {
140             // month of year and day of month variation
141             this.date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
142                                            toUnsigned(timeField[2]),
143                                            toUnsigned(timeField[3]));
144         } else {
145             // day of year variation
146             this.date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
147                                            toUnsigned(timeField[2]) * 256 + toUnsigned(timeField[3]));
148         }
149 
150         // time part from bytes 5 to last (between 7 and 13 depending on precision)
151         final int hour        = toUnsigned(timeField[4]);
152         final int minute      = toUnsigned(timeField[5]);
153         final int second      = toUnsigned(timeField[6]);
154         final int secondInDay = 3600 * hour + 60 * minute + second;
155 
156         long sub                  = 0;
157         long attoSecondMultiplier = 1000000000000000000L;
158         for (int i = 7; i < length; ++i) {
159             sub                   = sub * 100L + toUnsigned(timeField[i]);
160             attoSecondMultiplier /= 100L;
161         }
162 
163         this.time = new TimeComponents(new TimeOffset(secondInDay, sub * attoSecondMultiplier));
164 
165     }
166 
167     /** Get the date part.
168      * @return date part
169      */
170     public DateComponents getDate() {
171         return date;
172     }
173 
174     /** Get the time part.
175      * @return time part
176      */
177     public TimeComponents getTime() {
178         return time;
179     }
180 
181 }