AemMetadata.java

  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.files.ccsds.ndm.adm.aem;

  18. import org.hipparchus.geometry.euclidean.threed.RotationOrder;
  19. import org.orekit.errors.OrekitException;
  20. import org.orekit.errors.OrekitMessages;
  21. import org.orekit.files.ccsds.definitions.FrameFacade;
  22. import org.orekit.files.ccsds.ndm.adm.AdmMetadata;
  23. import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints;
  24. import org.orekit.files.ccsds.ndm.adm.AttitudeType;
  25. import org.orekit.time.AbsoluteDate;

  26. /** This class gathers the meta-data present in the Attitude Data Message (ADM).
  27.  * @author Bryan Cazabonne
  28.  * @since 10.2
  29.  */
  30. public class AemMetadata extends AdmMetadata {

  31.     /** Endpoints (i.e. frames A, B and their relationship). */
  32.     private final AttitudeEndpoints endpoints;

  33.     /** Start of total time span covered by attitude data. */
  34.     private AbsoluteDate startTime;

  35.     /** End of total time span covered by attitude data. */
  36.     private AbsoluteDate stopTime;

  37.     /** Start of useable time span covered by attitude data. */
  38.     private AbsoluteDate useableStartTime;

  39.     /** End of useable time span covered by attitude data. */
  40.     private AbsoluteDate useableStopTime;

  41.     /** The format of the data lines in the message. */
  42.     private AttitudeType attitudeType;

  43.     /** The placement of the scalar portion of the quaternion (QC) in the attitude data. */
  44.     private Boolean isFirst;

  45.     /** The rotation sequence of the Euler angles. */
  46.     private RotationOrder eulerRotSeq;

  47.     /** The frame in which rates are specified (only for ADM V1). */
  48.     private Boolean rateFrameIsA;

  49.     /** The frame in which angular velocities are specified.
  50.      * @since 12.0
  51.      */
  52.     private FrameFacade angvelFrame;

  53.     /** The interpolation method to be used. */
  54.     private String interpolationMethod;

  55.     /** The interpolation degree. */
  56.     private int interpolationDegree;

  57.     /** Simple constructor.
  58.      * @param defaultInterpolationDegree default interpolation degree
  59.      */
  60.     public AemMetadata(final int defaultInterpolationDegree) {
  61.         endpoints           = new AttitudeEndpoints();
  62.         interpolationDegree = defaultInterpolationDegree;
  63.     }

  64.     /** {@inheritDoc} */
  65.     @Override
  66.     public void validate(final double version) {

  67.         super.validate(version);

  68.         checkMandatoryEntriesExceptDatesAndExternalFrame(version);
  69.         endpoints.checkExternalFrame(AemMetadataKey.REF_FRAME_A, AemMetadataKey.REF_FRAME_B);

  70.         checkNotNull(startTime, AemMetadataKey.START_TIME.name());
  71.         checkNotNull(stopTime,  AemMetadataKey.STOP_TIME.name());

  72.         if (version >= 2.0 && isFirst()) {
  73.             throw new OrekitException(OrekitMessages.CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION,
  74.                                       AemMetadataKey.QUATERNION_TYPE, version);
  75.         }

  76.     }

  77.     /** Check is mandatory entries EXCEPT DATES AND EXTERNAL FRAME have been initialized.
  78.      * <p>
  79.      * Either frame A or frame B must be initialized with a {@link
  80.      * org.orekit.files.ccsds.definitions.SpacecraftBodyFrame spacecraft body frame}.
  81.      * </p>
  82.      * <p>
  83.      * This method should throw an exception if some mandatory entry is missing
  84.      * </p>
  85.      * @param version format version
  86.      */
  87.     void checkMandatoryEntriesExceptDatesAndExternalFrame(final double version) {

  88.         super.validate(version);

  89.         endpoints.checkMandatoryEntriesExceptExternalFrame(version,
  90.                                                            AemMetadataKey.REF_FRAME_A,
  91.                                                            AemMetadataKey.REF_FRAME_B,
  92.                                                            AemMetadataKey.ATTITUDE_DIR);

  93.         checkNotNull(attitudeType, AemMetadataKey.ATTITUDE_TYPE.name());
  94.         if (version < 2.0) {
  95.             if (attitudeType == AttitudeType.QUATERNION ||
  96.                 attitudeType == AttitudeType.QUATERNION_DERIVATIVE) {
  97.                 checkNotNull(isFirst, AemMetadataKey.QUATERNION_TYPE.name());
  98.             }
  99.             if (attitudeType == AttitudeType.EULER_ANGLE_DERIVATIVE) {
  100.                 checkNotNull(rateFrameIsA, AemMetadataKey.RATE_FRAME.name());
  101.             }
  102.         } else {
  103.             if (attitudeType == AttitudeType.QUATERNION_ANGVEL) {
  104.                 checkNotNull(angvelFrame, AemMetadataKey.ANGVEL_FRAME.name());
  105.             }
  106.         }

  107.         if (attitudeType == AttitudeType.EULER_ANGLE ||
  108.             attitudeType == AttitudeType.EULER_ANGLE_DERIVATIVE) {
  109.             checkNotNull(eulerRotSeq, AemMetadataKey.EULER_ROT_SEQ.name());
  110.         }

  111.     }

  112.     /** Get the endpoints (i.e. frames A, B and their relationship).
  113.      * @return endpoints
  114.      */
  115.     public AttitudeEndpoints getEndpoints() {
  116.         return endpoints;
  117.     }

  118.     /** Check if rates are specified in {@link AttitudeEndpoints#getFrameA() frame A}.
  119.      * @return true if rates are specified in {@link AttitudeEndpoints#getFrameA() frame A}
  120.      */
  121.     public boolean rateFrameIsA() {
  122.         return rateFrameIsA == null ? false : rateFrameIsA;
  123.     }

  124.     /** Set the frame in which rates are specified.
  125.      * @param rateFrameIsA if true, rates are specified in {@link AttitudeEndpoints#getFrameA() frame A}
  126.      */
  127.     public void setRateFrameIsA(final boolean rateFrameIsA) {
  128.         refuseFurtherComments();
  129.         this.rateFrameIsA = rateFrameIsA;
  130.     }

  131.     /** Set frame in which angular velocities are specified.
  132.      * @param angvelFrame frame in which angular velocities are specified
  133.      * @since 12.0
  134.      */
  135.     public void setAngvelFrame(final FrameFacade angvelFrame) {
  136.         this.angvelFrame = angvelFrame;
  137.     }

  138.     /** Get frame in which angular velocities are specified.
  139.      * @return frame in which angular velocities are specified
  140.      * @since 12.0
  141.      */
  142.     public FrameFacade getFrameAngvelFrame() {
  143.         return angvelFrame;
  144.     }

  145.     /** Check if rates are specified in spacecraft body frame.
  146.      * <p>
  147.      * {@link #validate(double) Mandatory entries} must have been
  148.      * initialized properly to non-null values before this method is called,
  149.      * otherwise {@code NullPointerException} will be thrown.
  150.      * </p>
  151.      * @return true if rates are specified in spacecraft body frame
  152.      */
  153.     public boolean isSpacecraftBodyRate() {
  154.         return rateFrameIsA() ^ endpoints.getFrameA().asSpacecraftBodyFrame() == null;
  155.     }

  156.     /**
  157.      * Get the format of the data lines in the message.
  158.      *
  159.      * @return the format of the data lines in the message
  160.      */
  161.     public AttitudeType getAttitudeType() {
  162.         return attitudeType;
  163.     }

  164.     /**
  165.      * Set the format of the data lines in the message.
  166.      * @param type format to be set
  167.      */
  168.     public void setAttitudeType(final AttitudeType type) {
  169.         refuseFurtherComments();
  170.         this.attitudeType = type;
  171.     }

  172.     /**
  173.      * Get the flag for the placement of the quaternion QC in the attitude data.
  174.      *
  175.      * @return true if QC is the first element in the attitude data,
  176.      * false if not initialized
  177.      */
  178.     public Boolean isFirst() {
  179.         return isFirst == null ? Boolean.FALSE : isFirst;
  180.     }

  181.     /**
  182.      * Set the flag for the placement of the quaternion QC in the attitude data.
  183.      * @param isFirst true if QC is the first element in the attitude data
  184.      */
  185.     public void setIsFirst(final boolean isFirst) {
  186.         refuseFurtherComments();
  187.         this.isFirst = isFirst;
  188.     }

  189.     /**
  190.      * Get the rotation order of Euler angles.
  191.      * @return rotation order
  192.      */
  193.     public RotationOrder getEulerRotSeq() {
  194.         return eulerRotSeq;
  195.     }

  196.     /**
  197.      * Set the rotation order for Euler angles.
  198.      * @param eulerRotSeq order to be set
  199.      */
  200.     public void setEulerRotSeq(final RotationOrder eulerRotSeq) {
  201.         refuseFurtherComments();
  202.         this.eulerRotSeq = eulerRotSeq;
  203.     }

  204.     /**
  205.      * Get start of total time span covered by attitude data.
  206.      * @return the start time
  207.      */
  208.     public AbsoluteDate getStartTime() {
  209.         return startTime;
  210.     }

  211.     /**
  212.      * Set start of total time span covered by attitude data.
  213.      * @param startTime the time to be set
  214.      */
  215.     public void setStartTime(final AbsoluteDate startTime) {
  216.         refuseFurtherComments();
  217.         this.startTime = startTime;
  218.     }

  219.     /**
  220.      * Get end of total time span covered by attitude data.
  221.      * @return the stop time
  222.      */
  223.     public AbsoluteDate getStopTime() {
  224.         return stopTime;
  225.     }

  226.     /**
  227.      * Set end of total time span covered by attitude data.
  228.      * @param stopTime the time to be set
  229.      */
  230.     public void setStopTime(final AbsoluteDate stopTime) {
  231.         refuseFurtherComments();
  232.         this.stopTime = stopTime;
  233.     }

  234.     /**
  235.      * Get start of useable time span covered by attitude data.
  236.      * @return the useable start time
  237.      */
  238.     public AbsoluteDate getUseableStartTime() {
  239.         return useableStartTime;
  240.     }

  241.     /**
  242.      * Set start of useable time span covered by attitude data.
  243.      * @param useableStartTime the time to be set
  244.      */
  245.     public void setUseableStartTime(final AbsoluteDate useableStartTime) {
  246.         refuseFurtherComments();
  247.         this.useableStartTime = useableStartTime;
  248.     }

  249.     /**
  250.      * Get end of useable time span covered by ephemerides data.
  251.      * @return the useable stop time
  252.      */
  253.     public AbsoluteDate getUseableStopTime() {
  254.         return useableStopTime;
  255.     }

  256.     /**
  257.      * Set end of useable time span covered by ephemerides data.
  258.      * @param useableStopTime the time to be set
  259.      */
  260.     public void setUseableStopTime(final AbsoluteDate useableStopTime) {
  261.         refuseFurtherComments();
  262.         this.useableStopTime = useableStopTime;
  263.     }

  264.     /**
  265.      * Get the start date of this ephemeris segment.
  266.      *
  267.      * @return ephemeris segment start date.
  268.      */
  269.     public AbsoluteDate getStart() {
  270.         // usable start time overrides start time if it is set
  271.         final AbsoluteDate start = this.getUseableStartTime();
  272.         if (start != null) {
  273.             return start;
  274.         } else {
  275.             return this.getStartTime();
  276.         }
  277.     }

  278.     /**
  279.      * Get the end date of this ephemeris segment.
  280.      *
  281.      * @return ephemeris segment end date.
  282.      */
  283.     public AbsoluteDate getStop() {
  284.         // useable stop time overrides stop time if it is set
  285.         final AbsoluteDate stop = this.getUseableStopTime();
  286.         if (stop != null) {
  287.             return stop;
  288.         } else {
  289.             return this.getStopTime();
  290.         }
  291.     }

  292.     /**
  293.      * Get the interpolation method to be used.
  294.      *
  295.      * @return the interpolation method
  296.      */
  297.     public String getInterpolationMethod() {
  298.         return interpolationMethod;
  299.     }

  300.     /**
  301.      * Set the interpolation method to be used.
  302.      * @param interpolationMethod the interpolation method to be set
  303.      */
  304.     public void setInterpolationMethod(final String interpolationMethod) {
  305.         refuseFurtherComments();
  306.         this.interpolationMethod = interpolationMethod;
  307.     }

  308.     /**
  309.      * Get the interpolation degree.
  310.      * @return the interpolation degree
  311.      */
  312.     public int getInterpolationDegree() {
  313.         return interpolationDegree;
  314.     }

  315.     /**
  316.      * Set the interpolation degree.
  317.      * @param interpolationDegree the interpolation degree to be set
  318.      */
  319.     public void setInterpolationDegree(final int interpolationDegree) {
  320.         refuseFurtherComments();
  321.         this.interpolationDegree = interpolationDegree;
  322.     }

  323.     /**
  324.      * Get the number of samples to use in interpolation.
  325.      *
  326.      * @return the number of points to use for interpolation.
  327.      */
  328.     public int getInterpolationSamples() {
  329.         // From the standard it is not entirely clear how to interpret the degree.
  330.         return getInterpolationDegree() + 1;
  331.     }

  332.     /** Copy the instance, making sure mandatory fields have been initialized.
  333.      * @param version format version
  334.      * @return a new copy
  335.      */
  336.     AemMetadata copy(final double version) {

  337.         checkMandatoryEntriesExceptDatesAndExternalFrame(version);

  338.         // allocate new instance
  339.         final AemMetadata copy = new AemMetadata(getInterpolationDegree());

  340.         // copy comments
  341.         for (String comment : getComments()) {
  342.             copy.addComment(comment);
  343.         }

  344.         // copy object
  345.         copy.setObjectName(getObjectName());
  346.         copy.setObjectID(getObjectID());
  347.         if (getCenter() != null) {
  348.             copy.setCenter(getCenter());
  349.         }

  350.         // copy frames (we may copy null references here)
  351.         copy.getEndpoints().setFrameA(getEndpoints().getFrameA());
  352.         copy.getEndpoints().setFrameB(getEndpoints().getFrameB());
  353.         copy.getEndpoints().setA2b(getEndpoints().isA2b());
  354.         copy.setRateFrameIsA(rateFrameIsA());

  355.         // copy time system only (ignore times themselves)
  356.         copy.setTimeSystem(getTimeSystem());

  357.         // copy attitude definitions
  358.         copy.setAttitudeType(getAttitudeType());
  359.         if (isFirst() != null) {
  360.             copy.setIsFirst(isFirst());
  361.         }
  362.         if (getEulerRotSeq() != null) {
  363.             copy.setEulerRotSeq(getEulerRotSeq());
  364.         }

  365.         // copy interpolation (degree has already been set up at construction)
  366.         if (getInterpolationMethod() != null) {
  367.             copy.setInterpolationMethod(getInterpolationMethod());
  368.         }

  369.         return copy;

  370.     }

  371. }