CcsdsSegmentedTimeCode.java
/* Copyright 2002-2024 CS GROUP
* Licensed to CS GROUP (CS) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* CS licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.orekit.time;
import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
/** This class represents a CCSDS segmented time code.
* @author Luc Maisonobe
* @since 12.1
* @see AbsoluteDate
* @see FieldAbsoluteDate
*/
class CcsdsSegmentedTimeCode extends AbstractCcsdsTimeCode {
/** Date part. */
private final DateComponents date;
/** Time part (down to second only). */
private final TimeComponents time;
/** Sub-second part. */
private final double subSecond;
/** Create an instance CCSDS Day Segmented Time Code (CDS).
* <p>
* CCSDS Day Segmented Time Code is defined in the blue book:
* CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
* </p>
* @param preambleField field specifying the format, often not transmitted in
* data interfaces, as it is constant for a given data interface
* @param timeField byte array containing the time code
* @param agencyDefinedEpoch reference epoch, ignored if the preamble field
* specifies the {@link DateComponents#CCSDS_EPOCH CCSDS reference epoch} is used
* (and hence may be null in this case)
*/
CcsdsSegmentedTimeCode(final byte preambleField, final byte[] timeField,
final DateComponents agencyDefinedEpoch) {
// time code identification
if ((preambleField & 0xF0) != 0x40) {
throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
formatByte(preambleField));
}
// reference epoch
final DateComponents epoch;
if ((preambleField & 0x08) == 0x00) {
// the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
epoch = DateComponents.CCSDS_EPOCH;
} else {
// the reference epoch is agency defined
if (agencyDefinedEpoch == null) {
throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
}
epoch = agencyDefinedEpoch;
}
// time field lengths
final int daySegmentLength = ((preambleField & 0x04) == 0x0) ? 2 : 3;
final int subMillisecondLength = (preambleField & 0x03) << 1;
if (subMillisecondLength == 6) {
throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
formatByte(preambleField));
}
if (timeField.length != daySegmentLength + 4 + subMillisecondLength) {
throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
timeField.length, daySegmentLength + 4 + subMillisecondLength);
}
int i = 0;
int day = 0;
while (i < daySegmentLength) {
day = day * 256 + toUnsigned(timeField[i++]);
}
long milliInDay = 0L;
while (i < daySegmentLength + 4) {
milliInDay = milliInDay * 256 + toUnsigned(timeField[i++]);
}
final int milli = (int) (milliInDay % 1000L);
final int seconds = (int) ((milliInDay - milli) / 1000L);
double subMilli = 0;
double divisor = 1;
while (i < timeField.length) {
subMilli = subMilli * 256 + toUnsigned(timeField[i++]);
divisor *= 1000;
}
this.date = new DateComponents(epoch, day);
this.time = new TimeComponents(seconds);
this.subSecond = milli * 1.0e-3 + subMilli / divisor;
}
/** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
* <p>
* CCSDS Calendar Segmented Time Code is defined in the blue book:
* CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
* </p>
* @param preambleField field specifying the format, often not transmitted in
* data interfaces, as it is constant for a given data interface
* @param timeField byte array containing the time code
*/
CcsdsSegmentedTimeCode(final byte preambleField, final byte[] timeField) {
// time code identification
if ((preambleField & 0xF0) != 0x50) {
throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
formatByte(preambleField));
}
// time field length
final int length = 7 + (preambleField & 0x07);
if (length == 14) {
throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
formatByte(preambleField));
}
if (timeField.length != length) {
throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
timeField.length, length);
}
// date part in the first four bytes
if ((preambleField & 0x08) == 0x00) {
// month of year and day of month variation
this.date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
toUnsigned(timeField[2]),
toUnsigned(timeField[3]));
} else {
// day of year variation
this.date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
toUnsigned(timeField[2]) * 256 + toUnsigned(timeField[3]));
}
// time part from bytes 5 to last (between 7 and 13 depending on precision)
this.time = new TimeComponents(toUnsigned(timeField[4]),
toUnsigned(timeField[5]),
toUnsigned(timeField[6]));
double sub = 0;
double divisor = 1;
for (int i = 7; i < length; ++i) {
sub = sub * 100 + toUnsigned(timeField[i]);
divisor *= 100;
}
this.subSecond = sub / divisor;
}
/** Get the date part.
* @return date part
*/
public DateComponents getDate() {
return date;
}
/** Get the time part.
* @return time part
*/
public TimeComponents getTime() {
return time;
}
/** Get the sub-second part.
* @return sub-second part
*/
public double getSubSecond() {
return subSecond;
}
}