CcsdsUnsegmentedTimeCode.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 unsegmented time code.
 * @param <T> type of the date
 * @author Luc Maisonobe
 * @since 12.1
 * @see AbsoluteDate
 * @see FieldAbsoluteDate
 */
class CcsdsUnsegmentedTimeCode<T> extends AbstractCcsdsTimeCode {

    /** Numerator of scale of the sub-second part (10¹⁸/256ⁿ).
     * @since 13.0
     */
    private static final long[] SUB_SCALE_NUM = new long[] {
        3906250000000000L, 15258789062500L, 3814697265625L, 3814697265625L, 3814697265625L, 3814697265625L, 3814697265625L
    };

    /** Denominator of scale of the sub-second part (10¹⁸/256ⁿ).
     * @since 13.0
     */
    private static final long[] SUB_SCALE_DEN = new long[] {
        1L, 1L, 64L, 16384L, 4194304L, 1073741824L, 274877906944L
    };

    /** Epoch part. */
    private final T epoch;

    /** Time part.
     * @since 13.0
     */
    private final TimeOffset time;

    /** Create an instance CCSDS Day Unegmented Time Code (CDS).
     * <p>
     * CCSDS Unsegmented Time Code is defined in the blue book: CCSDS Time Code Format
     * (CCSDS 301.0-B-4) published in November 2010
     * </p>
     * <p>
     * If the date to be parsed is formatted using version 3 of the standard (CCSDS
     * 301.0-B-3 published in 2002) or if the extension of the preamble field introduced
     * in version 4 of the standard is not used, then the {@code preambleField2} parameter
     * can be set to 0.
     * </p>
     * @param preambleField1     first byte of the field specifying the format, often not
     *                           transmitted in data interfaces, as it is constant for a
     *                           given data interface
     * @param preambleField2     second byte of the field specifying the format (added in
     *                           revision 4 of the CCSDS standard in 2010), often not
     *                           transmitted in data interfaces, as it is constant for a
     *                           given data interface (value ignored if presence not
     *                           signaled in {@code preambleField1})
     * @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, but then {@code ccsdsEpoch} must be non-null)
     * @param ccsdsEpoch         reference epoch, ignored if the preamble field specifies
     *                           the agency epoch is used (and hence may be null in this case,
     *                           but then {@code agencyDefinedEpoch} must be non-null).
     */
    CcsdsUnsegmentedTimeCode(final byte preambleField1,
                             final byte preambleField2,
                             final byte[] timeField,
                             final T agencyDefinedEpoch,
                             final T ccsdsEpoch) {

        // time code identification and reference epoch
        switch (preambleField1 & 0x70) {
            case 0x10:
                // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
                epoch = ccsdsEpoch;
                break;
            case 0x20:
                // the reference epoch is agency defined
                if (agencyDefinedEpoch == null) {
                    throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
                }
                epoch = agencyDefinedEpoch;
                break;
            default :
                throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
                                          formatByte(preambleField1));
        }

        // time field lengths
        int coarseTimeLength = 1 + ((preambleField1 & 0x0C) >>> 2);
        int fineTimeLength   = preambleField1 & 0x03;

        if ((preambleField1 & 0x80) != 0x0) {
            // there is an additional octet in preamble field
            coarseTimeLength += (preambleField2 & 0x60) >>> 5;
            fineTimeLength   += (preambleField2 & 0x1C) >>> 2;
        }

        if (timeField.length != coarseTimeLength + fineTimeLength) {
            throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
                                      timeField.length, coarseTimeLength + fineTimeLength);
        }

        long seconds = 0L;
        for (int i = 0; i < coarseTimeLength; ++i) {
            seconds = seconds * 256L + toUnsigned(timeField[i]);
        }

        long attoSeconds = 0L;
        for (int i = coarseTimeLength; i < timeField.length; ++i) {
            attoSeconds += (toUnsigned(timeField[i]) * SUB_SCALE_NUM[i - coarseTimeLength]) /
                           SUB_SCALE_DEN[i - coarseTimeLength];
        }

        time = new TimeOffset(seconds, attoSeconds);

    }

    /** Get the epoch part.
     * @return epoch part
     */
    public T getEpoch() {
        return epoch;
    }

    /** Get the time part.
     * @return time part
     * @since 13.0
     */
    public TimeOffset getTime() {
        return time;
    }

}