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.aem;
18
19 import org.hipparchus.geometry.euclidean.threed.RotationOrder;
20 import org.orekit.files.ccsds.ndm.adm.AdmMetadata;
21 import org.orekit.files.ccsds.ndm.adm.AttitudeType;
22 import org.orekit.files.ccsds.ndm.adm.AttitudeEndoints;
23 import org.orekit.time.AbsoluteDate;
24
25 /** This class gathers the meta-data present in the Attitude Data Message (ADM).
26 * @author Bryan Cazabonne
27 * @since 10.2
28 */
29 public class AemMetadata extends AdmMetadata {
30
31 /** Endpoints (i.e. frames A, B and their relationship). */
32 private final AttitudeEndoints endpoints;
33
34 /** Start of total time span covered by attitude data. */
35 private AbsoluteDate startTime;
36
37 /** End of total time span covered by attitude data. */
38 private AbsoluteDate stopTime;
39
40 /** Start of useable time span covered by attitude data. */
41 private AbsoluteDate useableStartTime;
42
43 /** End of useable time span covered by attitude data. */
44 private AbsoluteDate useableStopTime;
45
46 /** The format of the data lines in the message. */
47 private AttitudeType attitudeType;
48
49 /** The placement of the scalar portion of the quaternion (QC) in the attitude data. */
50 private Boolean isFirst;
51
52 /** The rotation sequence of the Euler angles. */
53 private RotationOrder eulerRotSeq;
54
55 /** The frame in which rates are specified. */
56 private Boolean rateFrameIsA;
57
58 /** The interpolation method to be used. */
59 private String interpolationMethod;
60
61 /** The interpolation degree. */
62 private int interpolationDegree;
63
64 /** Simple constructor.
65 * @param defaultInterpolationDegree default interpolation degree
66 */
67 public AemMetadata(final int defaultInterpolationDegree) {
68 endpoints = new AttitudeEndoints();
69 interpolationDegree = defaultInterpolationDegree;
70 }
71
72 /** {@inheritDoc} */
73 @Override
74 public void validate(final double version) {
75
76 checkMandatoryEntriesExceptDatesAndExternalFrame(version);
77 endpoints.checkExternalFrame(AemMetadataKey.REF_FRAME_A, AemMetadataKey.REF_FRAME_B);
78
79 checkNotNull(startTime, AemMetadataKey.START_TIME);
80 checkNotNull(stopTime, AemMetadataKey.STOP_TIME);
81
82 }
83
84 /** Check is mandatory entries EXCEPT DATES AND EXTERNAL FRAME have been initialized.
85 * <p>
86 * Either frame A or frame B must be initialized with a {@link
87 * org.orekit.files.ccsds.definitions.SpacecraftBodyFrame spacecraft body frame}.
88 * </p>
89 * <p>
90 * This method should throw an exception if some mandatory entry is missing
91 * </p>
92 * @param version format version
93 */
94 void checkMandatoryEntriesExceptDatesAndExternalFrame(final double version) {
95
96 super.validate(version);
97
98 endpoints.checkMandatoryEntriesExceptExternalFrame(AemMetadataKey.REF_FRAME_A,
99 AemMetadataKey.REF_FRAME_B,
100 AemMetadataKey.ATTITUDE_DIR);
101
102 checkNotNull(attitudeType, AemMetadataKey.ATTITUDE_TYPE);
103 if (attitudeType == AttitudeType.QUATERNION ||
104 attitudeType == AttitudeType.QUATERNION_DERIVATIVE) {
105 checkNotNull(isFirst, AemMetadataKey.QUATERNION_TYPE);
106 }
107 if (attitudeType == AttitudeType.QUATERNION_RATE ||
108 attitudeType == AttitudeType.EULER_ANGLE ||
109 attitudeType == AttitudeType.EULER_ANGLE_RATE) {
110 checkNotNull(eulerRotSeq, AemMetadataKey.EULER_ROT_SEQ);
111 }
112 if (attitudeType == AttitudeType.QUATERNION_RATE ||
113 attitudeType == AttitudeType.EULER_ANGLE_RATE) {
114 checkNotNull(rateFrameIsA, AemMetadataKey.RATE_FRAME);
115 }
116
117 }
118
119 /** Get the endpoints (i.e. frames A, B and their relationship).
120 * @return endpoints
121 */
122 public AttitudeEndoints getEndpoints() {
123 return endpoints;
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 format of the data lines in the message.
155 *
156 * @return the format of the data lines in the message
157 */
158 public AttitudeType getAttitudeType() {
159 return attitudeType;
160 }
161
162 /**
163 * Set the format of the data lines in the message.
164 * @param type format to be set
165 */
166 public void setAttitudeType(final AttitudeType type) {
167 refuseFurtherComments();
168 this.attitudeType = type;
169 }
170
171 /**
172 * Get the flag for the placement of the quaternion QC in the attitude data.
173 *
174 * @return true if QC is the first element in the attitude data,
175 * null if not initialized
176 */
177 public Boolean isFirst() {
178 return isFirst == null ? Boolean.TRUE : isFirst;
179 }
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 /**
191 * Get the rotation order of Euler angles.
192 * @return rotation order
193 */
194 public RotationOrder getEulerRotSeq() {
195 return eulerRotSeq;
196 }
197
198 /**
199 * Set the rotation order for Euler angles.
200 * @param eulerRotSeq order to be set
201 */
202 public void setEulerRotSeq(final RotationOrder eulerRotSeq) {
203 refuseFurtherComments();
204 this.eulerRotSeq = eulerRotSeq;
205 }
206
207 /**
208 * Get start of total time span covered by attitude data.
209 * @return the start time
210 */
211 public AbsoluteDate getStartTime() {
212 return startTime;
213 }
214
215 /**
216 * Set start of total time span covered by attitude data.
217 * @param startTime the time to be set
218 */
219 public void setStartTime(final AbsoluteDate startTime) {
220 refuseFurtherComments();
221 this.startTime = startTime;
222 }
223
224 /**
225 * Get end of total time span covered by attitude data.
226 * @return the stop time
227 */
228 public AbsoluteDate getStopTime() {
229 return stopTime;
230 }
231
232 /**
233 * Set end of total time span covered by attitude data.
234 * @param stopTime the time to be set
235 */
236 public void setStopTime(final AbsoluteDate stopTime) {
237 refuseFurtherComments();
238 this.stopTime = stopTime;
239 }
240
241 /**
242 * Get start of useable time span covered by attitude data.
243 * @return the useable start time
244 */
245 public AbsoluteDate getUseableStartTime() {
246 return useableStartTime;
247 }
248
249 /**
250 * Set start of useable time span covered by attitude data.
251 * @param useableStartTime the time to be set
252 */
253 public void setUseableStartTime(final AbsoluteDate useableStartTime) {
254 refuseFurtherComments();
255 this.useableStartTime = useableStartTime;
256 }
257
258 /**
259 * Get end of useable time span covered by ephemerides data.
260 * @return the useable stop time
261 */
262 public AbsoluteDate getUseableStopTime() {
263 return useableStopTime;
264 }
265
266 /**
267 * Set end of useable time span covered by ephemerides data.
268 * @param useableStopTime the time to be set
269 */
270 public void setUseableStopTime(final AbsoluteDate useableStopTime) {
271 refuseFurtherComments();
272 this.useableStopTime = useableStopTime;
273 }
274
275 /**
276 * Get the start date of this ephemeris segment.
277 *
278 * @return ephemeris segment start date.
279 */
280 public AbsoluteDate getStart() {
281 // usable start time overrides start time if it is set
282 final AbsoluteDate start = this.getUseableStartTime();
283 if (start != null) {
284 return start;
285 } else {
286 return this.getStartTime();
287 }
288 }
289
290 /**
291 * Get the end date of this ephemeris segment.
292 *
293 * @return ephemeris segment end date.
294 */
295 public AbsoluteDate getStop() {
296 // useable stop time overrides stop time if it is set
297 final AbsoluteDate stop = this.getUseableStopTime();
298 if (stop != null) {
299 return stop;
300 } else {
301 return this.getStopTime();
302 }
303 }
304
305 /**
306 * Get the interpolation method to be used.
307 *
308 * @return the interpolation method
309 */
310 public String getInterpolationMethod() {
311 return interpolationMethod;
312 }
313
314 /**
315 * Set the interpolation method to be used.
316 * @param interpolationMethod the interpolation method to be set
317 */
318 public void setInterpolationMethod(final String interpolationMethod) {
319 refuseFurtherComments();
320 this.interpolationMethod = interpolationMethod;
321 }
322
323 /**
324 * Get the interpolation degree.
325 * @return the interpolation degree
326 */
327 public int getInterpolationDegree() {
328 return interpolationDegree;
329 }
330
331 /**
332 * Set the interpolation degree.
333 * @param interpolationDegree the interpolation degree to be set
334 */
335 public void setInterpolationDegree(final int interpolationDegree) {
336 refuseFurtherComments();
337 this.interpolationDegree = interpolationDegree;
338 }
339
340 /**
341 * Get the number of samples to use in interpolation.
342 *
343 * @return the number of points to use for interpolation.
344 */
345 public int getInterpolationSamples() {
346 // From the standard it is not entirely clear how to interpret the degree.
347 return getInterpolationDegree() + 1;
348 }
349
350 /** Copy the instance, making sure mandatory fields have been initialized.
351 * @param version format version
352 * @return a new copy
353 */
354 AemMetadata copy(final double version) {
355
356 checkMandatoryEntriesExceptDatesAndExternalFrame(version);
357
358 // allocate new instance
359 final AemMetadata copy = new AemMetadata(getInterpolationDegree());
360
361 // copy comments
362 for (String comment : getComments()) {
363 copy.addComment(comment);
364 }
365
366 // copy object
367 copy.setObjectName(getObjectName());
368 copy.setObjectID(getObjectID());
369 if (getCenter() != null) {
370 copy.setCenter(getCenter());
371 }
372
373 // copy frames (we may copy null references here)
374 copy.getEndpoints().setFrameA(getEndpoints().getFrameA());
375 copy.getEndpoints().setFrameB(getEndpoints().getFrameB());
376 copy.getEndpoints().setA2b(getEndpoints().isA2b());
377 copy.setRateFrameIsA(rateFrameIsA());
378
379 // copy time system only (ignore times themselves)
380 copy.setTimeSystem(getTimeSystem());
381
382 // copy attitude definitions
383 copy.setAttitudeType(getAttitudeType());
384 if (isFirst() != null) {
385 copy.setIsFirst(isFirst());
386 }
387 if (getEulerRotSeq() != null) {
388 copy.setEulerRotSeq(getEulerRotSeq());
389 }
390
391 // copy interpolation (degree has already been set up at construction)
392 if (getInterpolationMethod() != null) {
393 copy.setInterpolationMethod(getInterpolationMethod());
394 }
395
396 return copy;
397
398 }
399
400 }