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

  18. import java.util.Arrays;

  19. import org.hipparchus.geometry.euclidean.threed.RotationOrder;
  20. import org.orekit.errors.OrekitException;
  21. import org.orekit.errors.OrekitMessages;
  22. import org.orekit.files.ccsds.ndm.adm.AttitudeEndpoints;
  23. import org.orekit.files.ccsds.section.CommentsContainer;

  24. /**
  25.  * Container for {@link Euler Euler rotations} entries.
  26.  * @author Bryan Cazabonne
  27.  * @since 10.2
  28.  */
  29. public class Euler extends CommentsContainer {

  30.     /** Key for angles in ADM V1.
  31.      * @since 12.0
  32.      */
  33.     private static final String KEY_ANGLES_V1 = "{X|Y|Z}_ANGLE";

  34.     /** Key for angles in ADM V2.
  35.      * @since 12.0
  36.      */
  37.     private static final String KEY_ANGLES_V2 = "ANGLE_{1|2|3}";

  38.     /** Key for rates in ADM V1.
  39.      * @since 12.0
  40.      */
  41.     private static final String KEY_RATES_V1 = "{X|Y|Z}_RATE";

  42.     /** Key for rates in ADM V2.
  43.      * @since 12.0
  44.      */
  45.     private static final String KEY_RATES_V2 = "ANGLE_{1|2|3}_DOT";

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

  48.     /** Rotation order of the Euler angles. */
  49.     private RotationOrder eulerRotSeq;

  50.     /** The frame in which rates are specified. */
  51.     private Boolean rateFrameIsA;

  52.     /** Euler angles [rad]. */
  53.     private double[] rotationAngles;

  54.     /** Rotation rate [rad/s]. */
  55.     private double[] rotationRates;

  56.     /** Indicator for rotation angles. */
  57.     private boolean inRotationAngles;

  58.     /** Simple constructor.
  59.      */
  60.     public Euler() {
  61.         this.endpoints        = new AttitudeEndpoints();
  62.         this.rotationAngles   = new double[3];
  63.         this.rotationRates    = new double[3];
  64.         this.inRotationAngles = false;
  65.         Arrays.fill(rotationAngles, Double.NaN);
  66.         Arrays.fill(rotationRates,  Double.NaN);
  67.     }

  68.     /** {@inheritDoc} */
  69.     @Override
  70.     public void validate(final double version) {

  71.         super.validate(version);
  72.         if (version < 2.0) {
  73.             endpoints.checkMandatoryEntriesExceptExternalFrame(version,
  74.                                                                EulerKey.EULER_FRAME_A,
  75.                                                                EulerKey.EULER_FRAME_B,
  76.                                                                EulerKey.EULER_DIR);
  77.             endpoints.checkExternalFrame(EulerKey.EULER_FRAME_A, EulerKey.EULER_FRAME_B);
  78.         } else {
  79.             endpoints.checkMandatoryEntriesExceptExternalFrame(version,
  80.                                                                EulerKey.REF_FRAME_A,
  81.                                                                EulerKey.REF_FRAME_B,
  82.                                                                EulerKey.EULER_DIR);
  83.             endpoints.checkExternalFrame(EulerKey.REF_FRAME_A, EulerKey.REF_FRAME_B);
  84.         }
  85.         checkNotNull(eulerRotSeq, EulerKey.EULER_ROT_SEQ.name());

  86.         if (!hasAngles()) {
  87.             // if at least one angle is missing, all must be NaN (i.e. not initialized)
  88.             for (final double ra : rotationAngles) {
  89.                 if (!Double.isNaN(ra)) {
  90.                     throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY,
  91.                                               version < 2.0 ? KEY_ANGLES_V1 : KEY_ANGLES_V2);
  92.                 }
  93.             }
  94.         }

  95.         if (!hasRates()) {
  96.             // if at least one rate is missing, all must be NaN (i.e. not initialized)
  97.             for (final double rr : rotationRates) {
  98.                 if (!Double.isNaN(rr)) {
  99.                     throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY,
  100.                                               version < 2.0 ? KEY_RATES_V1 : KEY_RATES_V2);
  101.                 }
  102.             }
  103.         }

  104.         if (version < 2.0) {
  105.             // in ADM V1, either angles or rates must be specified
  106.             // (angles may be missing in the quaternion/Euler rate case)
  107.             if (!hasAngles() && !hasRates()) {
  108.                 throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, KEY_ANGLES_V1 + "/" + KEY_RATES_V1);
  109.             }
  110.         } else {
  111.             // in ADM V2, angles are mandatory
  112.             if (!hasAngles()) {
  113.                 throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, KEY_ANGLES_V2);
  114.             }
  115.         }

  116.     }

  117.     /** Get the endpoints (i.e. frames A, B and their relationship).
  118.      * @return endpoints
  119.      */
  120.     public AttitudeEndpoints getEndpoints() {
  121.         return endpoints;
  122.     }

  123.     /**
  124.      * Get the rotation order of Euler angles.
  125.      * @return rotation order
  126.      */
  127.     public RotationOrder getEulerRotSeq() {
  128.         return eulerRotSeq;
  129.     }

  130.     /**
  131.      * Set the rotation order for Euler angles.
  132.      * @param eulerRotSeq order to be set
  133.      */
  134.     public void setEulerRotSeq(final RotationOrder eulerRotSeq) {
  135.         refuseFurtherComments();
  136.         this.eulerRotSeq = eulerRotSeq;
  137.     }

  138.     /** Check if rates are specified in {@link AttitudeEndpoints#getFrameA() frame A}.
  139.      * @return true if rates are specified in {@link AttitudeEndpoints#getFrameA() frame A}
  140.      */
  141.     public boolean rateFrameIsA() {
  142.         return rateFrameIsA == null ? false : rateFrameIsA;
  143.     }

  144.     /** Set the frame in which rates are specified.
  145.      * @param rateFrameIsA if true, rates are specified in {@link AttitudeEndpoints#getFrameA() frame A}
  146.      */
  147.     public void setRateFrameIsA(final boolean rateFrameIsA) {
  148.         refuseFurtherComments();
  149.         this.rateFrameIsA = rateFrameIsA;
  150.     }

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

  162.     /**
  163.      * Get the coordinates of the Euler angles.
  164.      * @return rotation angles (rad)
  165.      */
  166.     public double[] getRotationAngles() {
  167.         return rotationAngles.clone();
  168.     }

  169.     /**
  170.      * Set the Euler angle about axis.
  171.      * @param axis rotation axis
  172.      * @param angle angle to set (rad)
  173.      */
  174.     public void setLabeledRotationAngle(final char axis, final double angle) {
  175.         if (eulerRotSeq != null) {
  176.             for (int i = 0; i < rotationAngles.length; ++i) {
  177.                 if (eulerRotSeq.name().charAt(i) == axis && Double.isNaN(rotationAngles[i])) {
  178.                     setIndexedRotationAngle(i, angle);
  179.                     return;
  180.                 }
  181.             }
  182.         }
  183.     }

  184.     /**
  185.      * Set the Euler angle about axis.
  186.      * @param axis rotation axis
  187.      * @param angle angle to set (rad)
  188.      * @since 12.0
  189.      */
  190.     public void setIndexedRotationAngle(final int axis, final double angle) {
  191.         refuseFurtherComments();
  192.         rotationAngles[axis] = angle;
  193.     }

  194.     /**
  195.      * Get the rates of the Euler angles.
  196.      * @return rotation rates (rad/s)
  197.      */
  198.     public double[] getRotationRates() {
  199.         return rotationRates.clone();
  200.     }

  201.     /**
  202.      * Set the rate of Euler angle about axis.
  203.      * @param axis rotation axis
  204.      * @param rate angle rate to set (rad/s)
  205.      */
  206.     public void setLabeledRotationRate(final char axis, final double rate) {
  207.         if (eulerRotSeq != null) {
  208.             for (int i = 0; i < rotationRates.length; ++i) {
  209.                 if (eulerRotSeq.name().charAt(i) == axis && Double.isNaN(rotationRates[i])) {
  210.                     setIndexedRotationRate(i, rate);
  211.                     return;
  212.                 }
  213.             }
  214.         }
  215.     }

  216.     /**
  217.      * Set the rate of Euler angle about axis.
  218.      * @param axis rotation axis
  219.      * @param rate angle rate to set (rad/s)
  220.      * @since 12.0
  221.      */
  222.     public void setIndexedRotationRate(final int axis, final double rate) {
  223.         refuseFurtherComments();
  224.         rotationRates[axis] = rate;
  225.     }

  226.     /** Check if we are in the rotationAngles part of XML files.
  227.      * @return true if we are in the rotationAngles part of XML files
  228.      */
  229.     boolean inRotationAngles() {
  230.         return inRotationAngles;
  231.     }

  232.     /** Set flag for rotation angle parsing.
  233.      * @param inRotationAngles if true, we are in the rotationAngles part of XML files
  234.      */
  235.     public void setInRotationAngles(final boolean inRotationAngles) {
  236.         refuseFurtherComments();
  237.         this.inRotationAngles = inRotationAngles;
  238.     }

  239.     /** Check if the logical block includes angles.
  240.      * <p>
  241.      * This can be false only for ADM V1, as angles are mandatory since ADM V2.
  242.      * </p>
  243.      * @return true if logical block includes angles
  244.      * @since 12.0
  245.      */
  246.     public boolean hasAngles() {
  247.         return !Double.isNaN(rotationAngles[0] + rotationAngles[1] + rotationAngles[2]);
  248.     }

  249.     /** Check if the logical block includes rates.
  250.      * @return true if logical block includes rates
  251.      */
  252.     public boolean hasRates() {
  253.         return !Double.isNaN(rotationRates[0] + rotationRates[1] + rotationRates[2]);
  254.     }

  255. }