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