CcsdsSegmentedTimeCode.java

  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. import org.orekit.errors.OrekitException;
  19. import org.orekit.errors.OrekitMessages;

  20. /** This class represents a CCSDS segmented time code.
  21.  * @author Luc Maisonobe
  22.  * @since 12.1
  23.  * @see AbsoluteDate
  24.  * @see FieldAbsoluteDate
  25.  */
  26. class CcsdsSegmentedTimeCode extends AbstractCcsdsTimeCode {

  27.     /** Date part. */
  28.     private final DateComponents date;

  29.     /** Time part (down to second only). */
  30.     private final TimeComponents time;

  31.     /** Sub-second part. */
  32.     private final double subSecond;

  33.     /** Create an instance CCSDS Day Segmented Time Code (CDS).
  34.      * <p>
  35.      * CCSDS Day Segmented Time Code is defined in the blue book:
  36.      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
  37.      * </p>
  38.      * @param preambleField field specifying the format, often not transmitted in
  39.      * data interfaces, as it is constant for a given data interface
  40.      * @param timeField byte array containing the time code
  41.      * @param agencyDefinedEpoch reference epoch, ignored if the preamble field
  42.      * specifies the {@link DateComponents#CCSDS_EPOCH CCSDS reference epoch} is used
  43.      * (and hence may be null in this case)
  44.      */
  45.     CcsdsSegmentedTimeCode(final byte preambleField, final byte[] timeField,
  46.                            final DateComponents agencyDefinedEpoch) {

  47.         // time code identification
  48.         if ((preambleField & 0xF0) != 0x40) {
  49.             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
  50.                                       formatByte(preambleField));
  51.         }

  52.         // reference epoch
  53.         final DateComponents epoch;
  54.         if ((preambleField & 0x08) == 0x00) {
  55.             // the reference epoch is CCSDS epoch 1958-01-01T00:00:00 TAI
  56.             epoch = DateComponents.CCSDS_EPOCH;
  57.         } else {
  58.             // the reference epoch is agency defined
  59.             if (agencyDefinedEpoch == null) {
  60.                 throw new OrekitException(OrekitMessages.CCSDS_DATE_MISSING_AGENCY_EPOCH);
  61.             }
  62.             epoch = agencyDefinedEpoch;
  63.         }

  64.         // time field lengths
  65.         final int daySegmentLength = ((preambleField & 0x04) == 0x0) ? 2 : 3;
  66.         final int subMillisecondLength = (preambleField & 0x03) << 1;
  67.         if (subMillisecondLength == 6) {
  68.             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
  69.                                       formatByte(preambleField));
  70.         }
  71.         if (timeField.length != daySegmentLength + 4 + subMillisecondLength) {
  72.             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
  73.                                       timeField.length, daySegmentLength + 4 + subMillisecondLength);
  74.         }


  75.         int i   = 0;
  76.         int day = 0;
  77.         while (i < daySegmentLength) {
  78.             day = day * 256 + toUnsigned(timeField[i++]);
  79.         }

  80.         long milliInDay = 0L;
  81.         while (i < daySegmentLength + 4) {
  82.             milliInDay = milliInDay * 256 + toUnsigned(timeField[i++]);
  83.         }
  84.         final int milli   = (int) (milliInDay % 1000L);
  85.         final int seconds = (int) ((milliInDay - milli) / 1000L);

  86.         double subMilli = 0;
  87.         double divisor  = 1;
  88.         while (i < timeField.length) {
  89.             subMilli = subMilli * 256 + toUnsigned(timeField[i++]);
  90.             divisor *= 1000;
  91.         }

  92.         this.date      = new DateComponents(epoch, day);
  93.         this.time      = new TimeComponents(seconds);
  94.         this.subSecond = milli * 1.0e-3 + subMilli / divisor;

  95.     }

  96.     /** Build an instance from a CCSDS Calendar Segmented Time Code (CCS).
  97.      * <p>
  98.      * CCSDS Calendar Segmented Time Code is defined in the blue book:
  99.      * CCSDS Time Code Format (CCSDS 301.0-B-4) published in November 2010
  100.      * </p>
  101.      * @param preambleField field specifying the format, often not transmitted in
  102.      * data interfaces, as it is constant for a given data interface
  103.      * @param timeField byte array containing the time code
  104.      */
  105.     CcsdsSegmentedTimeCode(final byte preambleField, final byte[] timeField) {

  106.         // time code identification
  107.         if ((preambleField & 0xF0) != 0x50) {
  108.             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
  109.                                       formatByte(preambleField));
  110.         }

  111.         // time field length
  112.         final int length = 7 + (preambleField & 0x07);
  113.         if (length == 14) {
  114.             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_PREAMBLE_FIELD,
  115.                                       formatByte(preambleField));
  116.         }
  117.         if (timeField.length != length) {
  118.             throw new OrekitException(OrekitMessages.CCSDS_DATE_INVALID_LENGTH_TIME_FIELD,
  119.                                       timeField.length, length);
  120.         }

  121.         // date part in the first four bytes
  122.         if ((preambleField & 0x08) == 0x00) {
  123.             // month of year and day of month variation
  124.             this.date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
  125.                                            toUnsigned(timeField[2]),
  126.                                            toUnsigned(timeField[3]));
  127.         } else {
  128.             // day of year variation
  129.             this.date = new DateComponents(toUnsigned(timeField[0]) * 256 + toUnsigned(timeField[1]),
  130.                                            toUnsigned(timeField[2]) * 256 + toUnsigned(timeField[3]));
  131.         }

  132.         // time part from bytes 5 to last (between 7 and 13 depending on precision)
  133.         this.time = new TimeComponents(toUnsigned(timeField[4]),
  134.                                        toUnsigned(timeField[5]),
  135.                                        toUnsigned(timeField[6]));

  136.         double sub = 0;
  137.         double divisor   = 1;
  138.         for (int i = 7; i < length; ++i) {
  139.             sub = sub * 100 + toUnsigned(timeField[i]);
  140.             divisor *= 100;
  141.         }

  142.         this.subSecond = sub / divisor;

  143.     }

  144.     /** Get the date part.
  145.      * @return date part
  146.      */
  147.     public DateComponents getDate() {
  148.         return date;
  149.     }

  150.     /** Get the time part.
  151.      * @return time part
  152.      */
  153.     public TimeComponents getTime() {
  154.         return time;
  155.     }

  156.     /** Get the sub-second part.
  157.      * @return sub-second part
  158.      */
  159.     public double getSubSecond() {
  160.         return subSecond;
  161.     }

  162. }