1 /* Copyright 2002-2021 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
19 import java.util.Arrays;
20
21 import org.hipparchus.geometry.euclidean.threed.RotationOrder;
22 import org.orekit.errors.OrekitException;
23 import org.orekit.errors.OrekitMessages;
24 import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints;
25 import org.orekit.files.ccsds.section.CommentsContainer;
26
27 /**
28 * Container for {@link Euler Euler rotations} entries.
29 * @author Bryan Cazabonne
30 * @since 10.2
31 */
32 public class Euler extends CommentsContainer {
33
34 /** Endpoints (i.e. frames A, B and their relationship). */
35 private final AttitudeEndoints endpoints;
36
37 /** Rotation order of the Euler angles. */
38 private RotationOrder eulerRotSeq;
39
40 /** The frame in which rates are specified. */
41 private Boolean rateFrameIsA;
42
43 /** Euler angles [rad]. */
44 private double[] rotationAngles;
45
46 /** Rotation rate [rad/s]. */
47 private double[] rotationRates;
48
49 /** Indicator for rotation angles. */
50 private boolean inRotationAngles;
51
52 /** Simple constructor.
53 */
54 public Euler() {
55 this.endpoints = new AttitudeEndoints();
56 this.rotationAngles = new double[3];
57 this.rotationRates = new double[3];
58 this.inRotationAngles = false;
59 Arrays.fill(rotationAngles, Double.NaN);
60 Arrays.fill(rotationRates, Double.NaN);
61 }
62
63 /** {@inheritDoc} */
64 @Override
65 public void validate(final double version) {
66
67 super.validate(version);
68 endpoints.checkMandatoryEntriesExceptExternalFrame(EulerKey.EULER_FRAME_A,
69 EulerKey.EULER_FRAME_B,
70 EulerKey.EULER_DIR);
71 endpoints.checkExternalFrame(EulerKey.EULER_FRAME_A, EulerKey.EULER_FRAME_B);
72 checkNotNull(eulerRotSeq, EulerKey.EULER_ROT_SEQ);
73
74 final boolean missingAngle = Double.isNaN(rotationAngles[0] + rotationAngles[1] + rotationAngles[2]);
75 if (missingAngle) {
76 // if at least one is NaN, all must be NaN (i.e. not initialized)
77 for (final double ra : rotationAngles) {
78 if (!Double.isNaN(ra)) {
79 throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "{X|Y|Z}_ANGLE");
80 }
81 }
82 }
83
84 final boolean missingRate = Double.isNaN(rotationRates[0] + rotationRates[1] + rotationRates[2]);
85 if (missingRate) {
86 // if at least one is NaN, all must be NaN (i.e. not initialized)
87 for (final double rr : rotationRates) {
88 if (!Double.isNaN(rr)) {
89 throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "{X|Y|Z}_RATE");
90 }
91 }
92 }
93
94 // either angles or rates must be specified
95 // (angles may be missing in the quaternion/Euler rate case)
96 if (missingAngle && missingRate) {
97 throw new OrekitException(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, "{X|Y|Z}_{ANGLE|RATE}");
98 }
99
100 }
101
102 /** Get the endpoints (i.e. frames A, B and their relationship).
103 * @return endpoints
104 */
105 public AttitudeEndoints getEndpoints() {
106 return endpoints;
107 }
108
109 /**
110 * Get the rotation order of Euler angles.
111 * @return rotation order
112 */
113 public RotationOrder getEulerRotSeq() {
114 return eulerRotSeq;
115 }
116
117 /**
118 * Set the rotation order for Euler angles.
119 * @param eulerRotSeq order to be set
120 */
121 public void setEulerRotSeq(final RotationOrder eulerRotSeq) {
122 refuseFurtherComments();
123 this.eulerRotSeq = eulerRotSeq;
124 }
125
126 /** Check if rates are specified in {@link AttitudeEndoints#getFrameA() frame A}.
127 * @return true if rates are specified in {@link AttitudeEndoints#getFrameA() frame A}
128 */
129 public boolean rateFrameIsA() {
130 return rateFrameIsA == null ? false : rateFrameIsA;
131 }
132
133 /** Set the frame in which rates are specified.
134 * @param rateFrameIsA if true, rates are specified in {@link AttitudeEndoints#getFrameA() frame A}
135 */
136 public void setRateFrameIsA(final boolean rateFrameIsA) {
137 refuseFurtherComments();
138 this.rateFrameIsA = rateFrameIsA;
139 }
140
141 /** Check if rates are specified in spacecraft body frame.
142 * <p>
143 * {@link #validate(double) Mandatory entries} must have been
144 * initialized properly to non-null values before this method is called,
145 * otherwise {@code NullPointerException} will be thrown.
146 * </p>
147 * @return true if rates are specified in spacecraft body frame
148 */
149 public boolean isSpacecraftBodyRate() {
150 return rateFrameIsA ^ endpoints.getFrameA().asSpacecraftBodyFrame() == null;
151 }
152
153 /**
154 * Get the coordinates of the Euler angles (rad).
155 * @return rotation angles
156 */
157 public double[] getRotationAngles() {
158 return rotationAngles.clone();
159 }
160
161 /**
162 * Set the Euler angle about (rad).
163 * @param axis rotation axis
164 * @param angle angle to set
165 */
166 public void setRotationAngle(final char axis, final double angle) {
167 refuseFurtherComments();
168 setAngleOrRate(rotationAngles, axis, angle);
169 }
170
171 /**
172 * Get the rates of the Euler angles (rad/s).
173 * @return rotation rates
174 */
175 public double[] getRotationRates() {
176 return rotationRates.clone();
177 }
178
179 /**
180 * Set the rate of Euler angle (rad/s).
181 * @param axis rotation axis
182 * @param rate angle rate to set
183 */
184 public void setRotationRate(final char axis, final double rate) {
185 refuseFurtherComments();
186 setAngleOrRate(rotationRates, axis, rate);
187 }
188
189 /** Check if we are in the rotationAngles part of XML files.
190 * @return true if we are in the rotationAngles part of XML files
191 */
192 boolean inRotationAngles() {
193 return inRotationAngles;
194 }
195
196 /** Set flag for rotation angle parsing.
197 * @param inRotationAngles if true, we are in the rotationAngles part of XML files
198 */
199 public void setInRotationAngles(final boolean inRotationAngles) {
200 refuseFurtherComments();
201 this.inRotationAngles = inRotationAngles;
202 }
203
204 /** Check if the logical block includes rates.
205 * @return true if logical block includes rates
206 */
207 public boolean hasRates() {
208 return !Double.isNaN(rotationRates[0] + rotationRates[1] + rotationRates[2]);
209 }
210
211 /** Set an angle or rate in an array.
212 * @param array angle or rate array
213 * @param axis axis name
214 * @param value angle or rate to set
215 */
216 private void setAngleOrRate(final double[] array, final char axis, final double value) {
217 refuseFurtherComments();
218 if (eulerRotSeq != null) {
219 if (eulerRotSeq.name().charAt(0) == axis && Double.isNaN(array[0])) {
220 array[0] = value;
221 } else if (eulerRotSeq.name().charAt(1) == axis && Double.isNaN(array[1])) {
222 array[1] = value;
223 } else if (eulerRotSeq.name().charAt(2) == axis && Double.isNaN(array[2])) {
224 array[2] = value;
225 }
226 }
227 }
228 }