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 }