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 }