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  
19  import java.net.URISyntaxException;
20  import java.util.ArrayList;
21  import java.util.List;
22  
23  import org.hipparchus.analysis.differentiation.UnivariateDerivative1;
24  import org.hipparchus.geometry.euclidean.threed.FieldRotation;
25  import org.hipparchus.geometry.euclidean.threed.Rotation;
26  import org.hipparchus.geometry.euclidean.threed.RotationConvention;
27  import org.hipparchus.geometry.euclidean.threed.RotationOrder;
28  import org.hipparchus.geometry.euclidean.threed.Vector3D;
29  import org.hipparchus.util.Binary64;
30  import org.hipparchus.util.Binary64Field;
31  import org.hipparchus.util.FastMath;
32  import org.hipparchus.util.MathUtils;
33  import org.junit.jupiter.api.Assertions;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  import org.orekit.Utils;
37  import org.orekit.attitudes.Attitude;
38  import org.orekit.attitudes.BoundedAttitudeProvider;
39  import org.orekit.attitudes.FieldAttitude;
40  import org.orekit.bodies.CelestialBodyFactory;
41  import org.orekit.data.DataContext;
42  import org.orekit.data.DataSource;
43  import org.orekit.errors.OrekitException;
44  import org.orekit.errors.OrekitMessages;
45  import org.orekit.files.ccsds.definitions.CelestialBodyFrame;
46  import org.orekit.files.ccsds.definitions.OrbitRelativeFrame;
47  import org.orekit.files.ccsds.definitions.SpacecraftBodyFrame;
48  import org.orekit.files.ccsds.definitions.TimeSystem;
49  import org.orekit.files.ccsds.ndm.ParserBuilder;
50  import org.orekit.files.ccsds.ndm.adm.AttitudeType;
51  import org.orekit.files.ccsds.section.Segment;
52  import org.orekit.frames.FramesFactory;
53  import org.orekit.frames.ITRFVersion;
54  import org.orekit.orbits.CircularOrbit;
55  import org.orekit.orbits.FieldCircularOrbit;
56  import org.orekit.orbits.PositionAngleType;
57  import org.orekit.time.AbsoluteDate;
58  import org.orekit.time.FieldAbsoluteDate;
59  import org.orekit.time.TimeOffset;
60  import org.orekit.time.TimeScale;
61  import org.orekit.time.TimeScalesFactory;
62  import org.orekit.utils.AngularDerivativesFilter;
63  import org.orekit.utils.Constants;
64  import org.orekit.utils.IERSConventions;
65  import org.orekit.utils.TimeStampedAngularCoordinates;
66  
67  public class AEMParserTest {
68  
69      @BeforeEach
70      public void setUp()
71          throws Exception {
72          Utils.setDataRoot("regular-data");
73      }
74  
75      @Test
76      public void testParseAEM01() {
77          final String ex = "/ccsds/adm/aem/AEMExample01.txt";
78          final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
79          final Aem file = new ParserBuilder().buildAemParser().parseMessage(source);
80          final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
81          final Segment<AemMetadata, AemData> segment1 = file.getSegments().get(1);
82          final AbsoluteDate start = new AbsoluteDate("1996-11-28T22:08:02.5555", TimeScalesFactory.getUTC());
83          Assertions.assertEquals(0.0, start.durationFrom(file.getSatellites().get("1996-062A").getStart()), Double.MIN_VALUE);
84          final AbsoluteDate end = new AbsoluteDate("1996-12-28T21:23:00.5555", TimeScalesFactory.getUTC());
85          Assertions.assertEquals(0.0, end.durationFrom(file.getSatellites().get("1996-062A").getStop()), Double.MIN_VALUE);
86          Assertions.assertEquals("1996-062A", file.getSatellites().get("1996-062A").getId());
87          Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), Double.MIN_VALUE);
88          Assertions.assertEquals(new AbsoluteDate(2002, 11, 4, 17, 22, 31.0, TimeScalesFactory.getUTC()),
89                              file.getHeader().getCreationDate());
90          Assertions.assertEquals("NASA/JPL", file.getHeader().getOriginator());
91          Assertions.assertEquals("UTC",     segment0.getMetadata().getTimeSystem().name());
92          Assertions.assertEquals("MARS GLOBAL SURVEYOR", segment0.getMetadata().getObjectName());
93          Assertions.assertEquals("1996-062A",            segment0.getMetadata().getObjectID());
94          Assertions.assertEquals("MARS BARYCENTER",      segment0.getMetadata().getCenter().getName());
95          Assertions.assertEquals(1996,                   segment0.getMetadata().getLaunchYear());
96          Assertions.assertEquals(62,                     segment0.getMetadata().getLaunchNumber());
97          Assertions.assertEquals("A",                    segment0.getMetadata().getLaunchPiece());
98          Assertions.assertFalse(segment0.getMetadata().getHasCreatableBody());
99          Assertions.assertNull(segment0.getMetadata().getCenter().getBody());
100         Assertions.assertEquals(new AbsoluteDate(1996, 11, 28, 21, 29, new TimeOffset(7, TimeOffset.SECOND,
101                                                                                       255500, TimeOffset.MICROSECOND),
102                                                  TimeScalesFactory.getUTC()),
103                             segment0.getMetadata().getStartTime());
104         Assertions.assertEquals(new AbsoluteDate(1996, 11, 30, 1, 28, new TimeOffset(2, TimeOffset.SECOND,
105                                                                                      555500, TimeOffset.MICROSECOND),
106                                                  TimeScalesFactory.getUTC()),
107                             segment0.getMetadata().getStopTime());
108         Assertions.assertEquals(new AbsoluteDate(1996, 11, 28, 22, 8, new TimeOffset(2, TimeOffset.SECOND,
109                                                                                      555500, TimeOffset.MICROSECOND),
110                                                  TimeScalesFactory.getUTC()),
111                             segment0.getMetadata().getUseableStartTime());
112         Assertions.assertEquals(new AbsoluteDate(1996, 11, 30, 1, 18, new TimeOffset(2, TimeOffset.SECOND,
113                                                                                      555500, TimeOffset.MICROSECOND),
114                                                  TimeScalesFactory.getUTC()),
115                             segment0.getMetadata().getUseableStopTime());
116         Assertions.assertEquals("HERMITE", segment0.getMetadata().getInterpolationMethod());
117         Assertions.assertEquals(7,         segment0.getMetadata().getInterpolationDegree());
118         Assertions.assertFalse(segment0.getMetadata().isFirst());
119         Assertions.assertEquals("EME2000",       segment0.getMetadata().getEndpoints().getFrameA().getName());
120         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
121                             segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
122         Assertions.assertEquals("1",
123                             segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
124         Assertions.assertTrue(segment0.getMetadata().getEndpoints().isA2b());
125         Assertions.assertEquals(AttitudeType.QUATERNION, segment1.getMetadata().getAttitudeType());
126         Assertions.assertEquals(AngularDerivativesFilter.USE_R, segment0.getMetadata().getAttitudeType().getAngularDerivativesFilter());
127         verifyAngularCoordinates(new TimeStampedAngularCoordinates(new AbsoluteDate(1996, 11, 28, 21, 29,
128                                                                                     new TimeOffset(7, TimeOffset.SECOND,
129                                                                                                    255500, TimeOffset.MICROSECOND),
130                                                                                     TimeScalesFactory.getUTC()),
131                                                                    new Rotation(0.68427, 0.56748, 0.03146, 0.45689, false),
132                                                                    Vector3D.ZERO,
133                                                                    Vector3D.ZERO),
134                                  segment0.getData().getAngularCoordinates().get(0), 1.0e-5);
135         verifyAngularCoordinates(new TimeStampedAngularCoordinates(new AbsoluteDate(1996, 11, 28, 22, 8,
136                                                                                     new TimeOffset(3, TimeOffset.SECOND,
137                                                                                                    555500, TimeOffset.MICROSECOND),
138                                                                                     TimeScalesFactory.getUTC()),
139                                                                    new Rotation(0.74533, 0.42319, -0.45697, 0.23784, false),
140                                                                    Vector3D.ZERO,
141                                                                    Vector3D.ZERO),
142                                  segment0.getData().getAngularCoordinates().get(1), 1.0e-5);
143         verifyAngularCoordinates(new TimeStampedAngularCoordinates(new AbsoluteDate(1996, 11, 28, 22, 8,
144                                                                                     new TimeOffset(4, TimeOffset.SECOND,
145                                                                                                    555500, TimeOffset.MICROSECOND),
146                                                                                     TimeScalesFactory.getUTC()),
147                                                                    new Rotation(0.45652, -0.84532, 0.26974, -0.06532, false),
148                                                                    Vector3D.ZERO,
149                                                                    Vector3D.ZERO),
150                                  segment0.getData().getAngularCoordinates().get(2), 1.0e-5);
151         ArrayList<String> ephemeridesDataLinesComment = new ArrayList<>();
152         ephemeridesDataLinesComment.add("This file was produced by M.R. Somebody, MSOO NAV/JPL, 2002 OCT 04.");
153         ephemeridesDataLinesComment.add("It is to be used for attitude reconstruction only. The relative accuracy of these");
154         ephemeridesDataLinesComment.add("attitudes is 0.1 degrees per axis.");
155         Assertions.assertEquals(ephemeridesDataLinesComment, segment0.getMetadata().getComments());
156 
157         Assertions.assertEquals("UTC",                      segment1.getMetadata().getTimeSystem().name());
158         Assertions.assertEquals("MARS GLOBAL SURVEYOR",     segment1.getMetadata().getObjectName());
159         Assertions.assertEquals("1996-062A",                segment1.getMetadata().getObjectID());
160         Assertions.assertEquals("MARS BARYCENTER",          segment1.getMetadata().getCenter().getName());
161         Assertions.assertEquals(1996,                       segment1.getMetadata().getLaunchYear());
162         Assertions.assertEquals(62,                         segment1.getMetadata().getLaunchNumber());
163         Assertions.assertEquals("A",                        segment1.getMetadata().getLaunchPiece());
164         Assertions.assertFalse(segment1.getMetadata().getHasCreatableBody());
165         Assertions.assertNull(segment1.getMetadata().getCenter().getBody());
166         Assertions.assertEquals(new AbsoluteDate(1996, 12, 18, 12, 5, new TimeOffset(555500, TimeOffset.MICROSECOND), TimeScalesFactory.getUTC()),
167                             segment1.getMetadata().getStartTime());
168         Assertions.assertEquals(new AbsoluteDate(1996, 12, 28, 21, 28, new TimeOffset(555500, TimeOffset.MICROSECOND), TimeScalesFactory.getUTC()),
169                             segment1.getMetadata().getStopTime());
170         Assertions.assertEquals(new AbsoluteDate(1996, 12, 18, 12, 10, new TimeOffset(555500, TimeOffset.MICROSECOND), TimeScalesFactory.getUTC()),
171                             segment1.getMetadata().getUseableStartTime());
172         Assertions.assertEquals(new AbsoluteDate(1996, 12, 28, 21, 23, new TimeOffset(555500, TimeOffset.MICROSECOND), TimeScalesFactory.getUTC()),
173                             segment1.getMetadata().getUseableStopTime());
174         Assertions.assertFalse(segment1.getMetadata().isFirst());
175         Assertions.assertEquals("EME2000",       segment0.getMetadata().getEndpoints().getFrameA().getName());
176         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
177                             segment0.getMetadata().getEndpoints().getSpacecraftBodyFrame().asSpacecraftBodyFrame().getBaseEquipment());
178         Assertions.assertEquals("1",
179                             segment0.getMetadata().getEndpoints().getSpacecraftBodyFrame().asSpacecraftBodyFrame().getLabel());
180         Assertions.assertTrue(segment0.getMetadata().getEndpoints().isA2b());
181         Assertions.assertEquals(AttitudeType.QUATERNION, segment1.getMetadata().getAttitudeType());
182         Assertions.assertEquals(AngularDerivativesFilter.USE_R, segment0.getMetadata().getAttitudeType().getAngularDerivativesFilter());
183         verifyAngularCoordinates(new TimeStampedAngularCoordinates(new AbsoluteDate(1996, 12, 18, 12, 5, new TimeOffset(555500, TimeOffset.MICROSECOND),
184                                                                                     TimeScalesFactory.getUTC()),
185                                                                    new Rotation(0.72501, -0.64585, 0.018542, -0.23854, false),
186                                                                    Vector3D.ZERO,
187                                                                    Vector3D.ZERO),
188                                  segment1.getData().getAngularCoordinates().get(0), 1.0e-5);
189         verifyAngularCoordinates(new TimeStampedAngularCoordinates(new AbsoluteDate(1996, 12, 18, 12, 10, new TimeOffset(5, TimeOffset.SECOND,
190                                                                                                                          555500,
191                                                                                                                          TimeOffset.MICROSECOND),
192                                                                                     TimeScalesFactory.getUTC()),
193                                                                    new Rotation(-0.16767, 0.87451, -0.43475, 0.13458, false),
194                                                                    Vector3D.ZERO,
195                                                                    Vector3D.ZERO),
196                                  segment1.getData().getAngularCoordinates().get(1), 1.0e-5);
197         verifyAngularCoordinates(new TimeStampedAngularCoordinates(new AbsoluteDate(1996, 12, 18, 12, 10, new TimeOffset(10, TimeOffset.SECOND,
198                                                                                                                          555500, TimeOffset.MICROSECOND),
199                                                                                     TimeScalesFactory.getUTC()),
200                                                                    new Rotation(-0.71418, 0.03125, -0.65874, 0.23458, false),
201                                                                    Vector3D.ZERO,
202                                                                    Vector3D.ZERO),
203                                  segment1.getData().getAngularCoordinates().get(2), 1.0e-5);
204         ArrayList<String> ephemeridesDataLinesComment2 = new ArrayList<>();
205         ephemeridesDataLinesComment2.add("This block begins after trajectory correction maneuver TCM-3.");
206         Assertions.assertEquals(ephemeridesDataLinesComment2, segment1.getMetadata().getComments());
207     }
208 
209     @Test
210     public void testParseAEM02() {
211         final String name = "/ccsds/adm/aem/AEMExample02.txt";
212         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
213         AemParser parser = new ParserBuilder().
214                            withMissionReferenceDate(new AbsoluteDate("1996-12-17T00:00:00.000",
215                                                                      TimeScalesFactory.getUTC())).
216                            buildAemParser();
217 
218         final Aem file = parser.parse(source); // using generic API here
219         final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
220         final List<String> headerComment = new ArrayList<>();
221         headerComment.add("comment");
222         Assertions.assertEquals(headerComment, file.getHeader().getComments());
223         final List<String> metadataComment = new ArrayList<>();
224         metadataComment.add("This file was produced by M.R. Somebody, MSOO NAV/JPL, 2002 OCT 04.");
225         metadataComment.add("It is to be used for attitude reconstruction only. The relative accuracy of these");
226         metadataComment.add("attitudes is 0.1 degrees per axis.");
227         Assertions.assertEquals(metadataComment, segment0.getMetadata().getComments());
228         Assertions.assertEquals("EME2000",       segment0.getMetadata().getEndpoints().getFrameA().getName());
229         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY,
230                             segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
231         Assertions.assertEquals("1",
232                             segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
233         List<AemSegment> blocks = file.getSegments();
234         Assertions.assertEquals(1, blocks.size());
235         Assertions.assertEquals(IERSConventions.IERS_2010, parser.getConventions());
236         Assertions.assertTrue(parser.isSimpleEOP());
237         Assertions.assertEquals(0.0, parser.getMissionReferenceDate().durationFrom(new AbsoluteDate(1996, 12, 17, 0, 0, 0.0,
238                                                                                                     TimeScalesFactory.getUTC())), 1.0e-5);
239         Assertions.assertEquals(DataContext.getDefault(), parser.getDataContext());
240         Assertions.assertEquals((new AbsoluteDate("1996-12-17T00:00:00.000",
241                                               TimeScalesFactory.getUTC())),
242                             parser.getMissionReferenceDate());
243     }
244 
245     @Test
246     public void testParseKvnAEM03() {
247         final String ex = "/ccsds/adm/aem/AEMExample03.txt";
248         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
249         validateAEM03(new ParserBuilder().buildAemParser().parseMessage(source));
250     }
251 
252     @Test
253     public void testParseXmlAEM03() {
254         final String ex = "/ccsds/adm/aem/AEMExample03.xml";
255         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
256         final AemParser parser  = new ParserBuilder().buildAemParser();
257         validateAEM03(parser.parse(source));
258     }
259 
260     private void validateAEM03(final Aem file) {
261 
262         final TimeScale utc = TimeScalesFactory.getUTC();
263         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-15);
264         Assertions.assertEquals(0,
265                             file.getHeader().getCreationDate().durationFrom(new AbsoluteDate("2008-071T17:09:49", utc)),
266                             1.0e-12);
267         Assertions.assertEquals("GSFC FDF", file.getHeader().getOriginator());
268         final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
269         Assertions.assertEquals("ST5-224", segment0.getMetadata().getObjectName());
270         Assertions.assertEquals("2006224", segment0.getMetadata().getObjectID());
271         Assertions.assertEquals("J2000",   segment0.getMetadata().getEndpoints().getFrameA().getName());
272         Assertions.assertEquals("SC_BODY_1", segment0.getMetadata().getEndpoints().getFrameB().getName());
273         Assertions.assertTrue(segment0.getMetadata().getEndpoints().isA2b());
274         Assertions.assertEquals(TimeSystem.UTC, segment0.getMetadata().getTimeSystem());
275         Assertions.assertEquals(0,
276                             segment0.getMetadata().getStartTime().durationFrom(new AbsoluteDate("2006-090T05:00:00.071", utc)),
277                             1.0e-12);
278         Assertions.assertEquals(0,
279                             segment0.getMetadata().getUseableStartTime().durationFrom(new AbsoluteDate("2006-090T05:00:00.071", utc)),
280                             1.0e-12);
281         Assertions.assertEquals(0,
282                             segment0.getMetadata().getUseableStopTime().durationFrom(new AbsoluteDate("2006-090T05:00:00.946", utc)),
283                             1.0e-12);
284         Assertions.assertEquals(0,
285                             segment0.getMetadata().getStopTime().durationFrom(new AbsoluteDate("2006-090T05:00:00.946", utc)),
286                             1.0e-12);
287         Assertions.assertEquals(AttitudeType.SPIN, segment0.getMetadata().getAttitudeType());
288         Assertions.assertEquals(1, segment0.getData().getComments().size());
289         Assertions.assertEquals("Spin KF ground solution, SPINKF rates", segment0.getData().getComments().get(0));
290         Assertions.assertEquals(8, segment0.getData().getAngularCoordinates().size());
291         TimeStampedAngularCoordinates prev = null;
292         for (TimeStampedAngularCoordinates tac : segment0.getData().getAngularCoordinates()) {
293             if (prev != null) {
294                 double dt = tac.getDate().durationFrom(prev.getDate());
295                 double dR = Rotation.distance(tac.getRotation(), prev.getRotation());
296                 double meanRate = 0.5 * (prev.getRotationRate().getNorm() + tac.getRotationRate().getNorm());
297                 Assertions.assertEquals(dR, dt * meanRate, 1.3e-3);
298             }
299             prev = tac;
300         }
301 
302     }
303 
304     @Test
305     public void testParseAEM04() {
306         final TimeScale utc = TimeScalesFactory.getUTC();
307         final String ex = "/ccsds/adm/aem/AEMExample04.txt";
308         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
309         final AemParser parser  = new ParserBuilder().buildAemParser();
310         final Aem file = parser.parseMessage(source);
311         Assertions.assertEquals(1.0, file.getHeader().getFormatVersion(), 1.0e-15);
312         Assertions.assertEquals(0,
313                             file.getHeader().getCreationDate().durationFrom(new AbsoluteDate("2021-04-13T08:41:42", utc)),
314                             1.0e-12);
315         Assertions.assertEquals("CS GROUP", file.getHeader().getOriginator());
316         final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
317         Assertions.assertEquals("COPIHUE",   segment0.getMetadata().getObjectName());
318         Assertions.assertEquals("2100-017F", segment0.getMetadata().getObjectID());
319         Assertions.assertEquals(2100,        segment0.getMetadata().getLaunchYear());
320         Assertions.assertEquals(17,          segment0.getMetadata().getLaunchNumber());
321         Assertions.assertEquals("F",         segment0.getMetadata().getLaunchPiece());
322         Assertions.assertEquals("EME2000",   segment0.getMetadata().getEndpoints().getFrameA().getName());
323         Assertions.assertEquals("SC_BODY_1", segment0.getMetadata().getEndpoints().getFrameB().getName());
324         Assertions.assertTrue(segment0.getMetadata().getEndpoints().isA2b());
325         Assertions.assertEquals(TimeSystem.UTC, segment0.getMetadata().getTimeSystem());
326         Assertions.assertEquals(0,
327                             segment0.getMetadata().getStartTime().durationFrom(new AbsoluteDate("2021-12-31T00:00:00.000", utc)),
328                             1.0e-12);
329         Assertions.assertEquals(0,
330                             segment0.getMetadata().getUseableStartTime().durationFrom(new AbsoluteDate("2021-12-31T00:00:00.500", utc)),
331                             1.0e-12);
332         Assertions.assertEquals(0,
333                             segment0.getMetadata().getUseableStopTime().durationFrom(new AbsoluteDate("2021-12-31T00:00:05.500", utc)),
334                             1.0e-12);
335         Assertions.assertEquals(0,
336                             segment0.getMetadata().getStopTime().durationFrom(new AbsoluteDate("2021-12-31T00:00:06.000", utc)),
337                             1.0e-12);
338         Assertions.assertEquals(AttitudeType.QUATERNION_DERIVATIVE, segment0.getMetadata().getAttitudeType());
339         Assertions.assertEquals("HERMITE", segment0.getMetadata().getInterpolationMethod());
340         Assertions.assertEquals(3, segment0.getMetadata().getInterpolationDegree());
341         Assertions.assertEquals(1, segment0.getData().getComments().size());
342         Assertions.assertEquals(13, segment0.getData().getAngularCoordinates().size());
343         TimeStampedAngularCoordinates prev = null;
344         for (TimeStampedAngularCoordinates tac : segment0.getData().getAngularCoordinates()) {
345             if (prev != null) {
346                 double dt = tac.getDate().durationFrom(prev.getDate());
347                 double dR = Rotation.distance(tac.getRotation(), prev.getRotation());
348                 double meanRate = 0.5 * (prev.getRotationRate().getNorm() + tac.getRotationRate().getNorm());
349                 Assertions.assertEquals(dR, dt * meanRate, 1.5e-6);
350             }
351             prev = tac;
352         }
353 
354     }
355 
356     @Test
357     public void testParseAEM05() {
358         final String ex = "/ccsds/adm/aem/AEMExample05.txt";
359         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
360         final AemParser parser  = new ParserBuilder().buildAemParser();
361         final Aem file = parser.parseMessage(source);
362         final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
363         final List<String> headerComment = new ArrayList<>();
364         headerComment.add("comment");
365         Assertions.assertEquals(headerComment, file.getHeader().getComments());
366         final List<String> metadataComment = new ArrayList<>();
367         metadataComment.add("This file was produced by M.R. Somebody, MSOO NAV/JPL, 2002 OCT 04.");
368         metadataComment.add("It is to be used for attitude reconstruction only. The relative accuracy of these");
369         metadataComment.add("attitudes is 0.1 degrees per axis.");
370         Assertions.assertEquals(metadataComment,        segment0.getMetadata().getComments());
371         Assertions.assertEquals("UTC",                  segment0.getMetadata().getTimeSystem().name());
372         Assertions.assertEquals("MARS GLOBAL SURVEYOR", segment0.getMetadata().getObjectName());
373         Assertions.assertEquals("1996-062A",            segment0.getMetadata().getObjectID());
374         Assertions.assertEquals("MARS BARYCENTER",      segment0.getMetadata().getCenter().getName());
375         Assertions.assertEquals(1996,                   segment0.getMetadata().getLaunchYear());
376         Assertions.assertEquals(62,                     segment0.getMetadata().getLaunchNumber());
377         Assertions.assertEquals("A",                    segment0.getMetadata().getLaunchPiece());
378         Assertions.assertEquals(RotationOrder.ZXY,      segment0.getMetadata().getEulerRotSeq());
379         Assertions.assertTrue(segment0.getMetadata().rateFrameIsA());
380         Assertions.assertFalse(segment0.getMetadata().getHasCreatableBody());
381         Assertions.assertNull(segment0.getMetadata().getCenter().getBody());
382 
383         // Reference values
384         final AbsoluteDate refDate = new AbsoluteDate(1996, 11, 28, 21, 29, 7.2555, TimeScalesFactory.getUTC());
385 
386         // Computed angular coordinates
387         final TimeStampedAngularCoordinates ac = segment0.getData().getAngularCoordinates().get(0);
388         final FieldRotation<UnivariateDerivative1> r = ac.toUnivariateDerivative1Rotation();
389         final UnivariateDerivative1[] angles = r.getAngles(segment0.getMetadata().getEulerRotSeq(),
390                                                            RotationConvention.FRAME_TRANSFORM);
391         Assertions.assertEquals(0.0,     refDate.durationFrom(ac.getDate()),                 1.0e-5);
392         Assertions.assertEquals(0.0,     ac.getRotationAcceleration().getNorm(),             1.0e-5);
393         Assertions.assertEquals(-26.78,  FastMath.toDegrees(angles[0].getValue()),           1.0e-2);
394         Assertions.assertEquals(46.26,   FastMath.toDegrees(angles[1].getValue()),           1.0e-2);
395         Assertions.assertEquals(144.10,  FastMath.toDegrees(angles[2].getValue()),           1.0e-2);
396         Assertions.assertEquals(0.10450, FastMath.toDegrees(angles[0].getFirstDerivative()), 1.0e-5);
397         Assertions.assertEquals(0.03214, FastMath.toDegrees(angles[1].getFirstDerivative()), 1.0e-5);
398         Assertions.assertEquals(0.02156, FastMath.toDegrees(angles[2].getFirstDerivative()), 1.0e-5);
399     }
400 
401     @Test
402     public void testParseAEM06a() {
403         final String ex = "/ccsds/adm/aem/AEMExample06a.txt";
404         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
405         final AemParser parser  = new ParserBuilder().buildAemParser();
406 
407         final Aem file = parser.parseMessage(source);
408         final TimeStampedAngularCoordinates ac = file.getSegments().get(0).getAngularCoordinates().get(7);
409         final Vector3D lastSpin = ac.getRotation().applyInverseTo(Vector3D.PLUS_K);
410         Assertions.assertEquals(268.45119, FastMath.toDegrees(MathUtils.normalizeAngle(lastSpin.getAlpha(), FastMath.PI)), 1.0e-5);
411         Assertions.assertEquals(68.317275, FastMath.toDegrees(lastSpin.getDelta()), 1.0e-5);
412     }
413 
414     @Test
415     public void testParseAEM06b() {
416         final String ex = "/ccsds/adm/aem/AEMExample06b.txt";
417         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
418         final AemParser parser  = new ParserBuilder().buildAemParser();
419 
420         final Aem file = parser.parseMessage(source);
421         final TimeStampedAngularCoordinates ac = file.getSegments().get(0).getAngularCoordinates().get(7);
422         final Vector3D lastSpin = ac.getRotation().applyInverseTo(Vector3D.PLUS_K);
423         Assertions.assertEquals(268.45119, FastMath.toDegrees(MathUtils.normalizeAngle(lastSpin.getAlpha(), FastMath.PI)), 1.0e-5);
424         Assertions.assertEquals(68.317275, FastMath.toDegrees(lastSpin.getDelta()), 1.0e-5);
425     }
426 
427     @Test
428     public void testParseAEM07() {
429         final String ex = "/ccsds/adm/aem/AEMExample07.txt";
430         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
431         final AemParser parser  = new ParserBuilder().buildAemParser();
432         final Aem file = parser.parseMessage(source);
433         final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
434         final List<String> headerComment = new ArrayList<>();
435         headerComment.add("comment");
436         Assertions.assertEquals(headerComment, file.getHeader().getComments());
437         final List<String> metadataComment = new ArrayList<>();
438         metadataComment.add("This file was produced by M.R. Somebody, MSOO NAV/JPL, 2002 OCT 04.");
439         metadataComment.add("It is to be used for attitude reconstruction only. The relative accuracy of these");
440         metadataComment.add("attitudes is 0.1 degrees per axis.");
441         Assertions.assertEquals(metadataComment,        segment0.getMetadata().getComments());
442         Assertions.assertEquals(TimeSystem.UTC,         segment0.getMetadata().getTimeSystem());
443         Assertions.assertEquals("MARS GLOBAL SURVEYOR", segment0.getMetadata().getObjectName());
444         Assertions.assertEquals("1996-062A",            segment0.getMetadata().getObjectID());
445         Assertions.assertEquals("MARS BARYCENTER",      segment0.getMetadata().getCenter().getName());
446         Assertions.assertEquals(1996,                   segment0.getMetadata().getLaunchYear());
447         Assertions.assertEquals(62,                     segment0.getMetadata().getLaunchNumber());
448         Assertions.assertEquals("A",                    segment0.getMetadata().getLaunchPiece());
449         Assertions.assertFalse(segment0.getMetadata().getHasCreatableBody());
450         Assertions.assertNull(segment0.getMetadata().getCenter().getBody());
451         Assertions.assertEquals(CelestialBodyFrame.EME2000, segment0.getMetadata().getEndpoints().getFrameA().asCelestialBodyFrame());
452         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY, segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
453         Assertions.assertEquals("1", segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
454         Assertions.assertTrue(segment0.getMetadata().getEndpoints().isA2b());
455         Assertions.assertEquals(new AbsoluteDate("2002-12-18T12:00:00.331", TimeScalesFactory.getUTC()),
456                             segment0.getMetadata().getStartTime());
457         Assertions.assertEquals(new AbsoluteDate("2002-12-18T12:00:00.331", TimeScalesFactory.getUTC()),
458                             segment0.getMetadata().getUseableStartTime());
459         Assertions.assertEquals(new AbsoluteDate("2002-12-18T12:02:00.331", TimeScalesFactory.getUTC()),
460                             segment0.getMetadata().getUseableStopTime());
461         Assertions.assertEquals(new AbsoluteDate("2002-12-18T12:02:00.331", TimeScalesFactory.getUTC()),
462                             segment0.getMetadata().getStopTime());
463         Assertions.assertEquals(AttitudeType.QUATERNION, segment0.getMetadata().getAttitudeType());
464         Assertions.assertFalse(segment0.getMetadata().isFirst());
465         Assertions.assertEquals("HERMITE", segment0.getMetadata().getInterpolationMethod());
466         Assertions.assertEquals(7, segment0.getMetadata().getInterpolationDegree());
467 
468         final AbsoluteDate refDate = new AbsoluteDate("2002-12-18T12:00:00.331", TimeScalesFactory.getUTC());
469 
470         Assertions.assertEquals(3, segment0.getData().getAngularCoordinates().size());
471         final TimeStampedAngularCoordinates ac0 = segment0.getData().getAngularCoordinates().get(0);
472         Assertions.assertEquals(0.0, ac0.getDate().durationFrom(refDate), 1.0e-5);
473         Assertions.assertEquals(0.0,
474                             Rotation.distance(new Rotation(0.68427, 0.56748, 0.03146, 0.45689, true),
475                                               ac0.getRotation()),
476                             1.0e-10);
477         final TimeStampedAngularCoordinates ac1 = segment0.getData().getAngularCoordinates().get(1);
478         Assertions.assertEquals(60.0, ac1.getDate().durationFrom(refDate), 1.0e-5);
479         Assertions.assertEquals(0.0,
480                             Rotation.distance(new Rotation(0.74533, 0.42319, -0.45697, 0.23784, true),
481                                               ac1.getRotation()),
482                             1.0e-10);
483         final TimeStampedAngularCoordinates ac2 = segment0.getData().getAngularCoordinates().get(2);
484         Assertions.assertEquals(120.0, ac2.getDate().durationFrom(refDate), 1.0e-5);
485         Assertions.assertEquals(0.0,
486                             Rotation.distance(new Rotation(0.45652, -0.84532, 0.26974, -0.06532, true),
487                                               ac2.getRotation()),
488                             1.0e-10);
489 
490     }
491 
492     @Test
493     public void testParseAEM11() {
494         final String ex = "/ccsds/adm/aem/AEMExample11.xml";
495         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
496         final AemParser parser  = new ParserBuilder().buildAemParser();
497         final Aem file = parser.parseMessage(source);
498         final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
499         final List<String> headerComment = new ArrayList<>();
500         headerComment.add("This example shows an AEM with a rotation");
501         Assertions.assertEquals(headerComment, file.getHeader().getComments());
502         final List<String> metadataComment = new ArrayList<>();
503         metadataComment.add("The relative accuracy of these");
504         metadataComment.add("attitudes is 0.1 degrees per axis.");
505         Assertions.assertEquals(metadataComment, segment0.getMetadata().getComments());
506         Assertions.assertEquals(TimeSystem.UTC,  segment0.getMetadata().getTimeSystem());
507         Assertions.assertEquals("FICTITIOUS",    segment0.getMetadata().getObjectName());
508         Assertions.assertEquals("2020-224A",     segment0.getMetadata().getObjectID());
509         Assertions.assertEquals("EARTH",         segment0.getMetadata().getCenter().getName());
510         Assertions.assertEquals(CelestialBodyFrame.J2000, segment0.getMetadata().getEndpoints().getFrameA().asCelestialBodyFrame());
511         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY, segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
512         Assertions.assertEquals("1", segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
513         Assertions.assertTrue(segment0.getMetadata().getEndpoints().isA2b());
514         Assertions.assertEquals(new AbsoluteDate("2020-090T05:00:00.071", TimeScalesFactory.getUTC()),
515                             segment0.getMetadata().getStartTime());
516         Assertions.assertEquals(new AbsoluteDate("2020-090T05:00:00.946", TimeScalesFactory.getUTC()),
517                             segment0.getMetadata().getStopTime());
518         Assertions.assertEquals(AttitudeType.EULER_ANGLE_DERIVATIVE, segment0.getMetadata().getAttitudeType());
519 
520         final AbsoluteDate refDate = new AbsoluteDate("2020-090T05:00:00.071", TimeScalesFactory.getUTC());
521 
522         Assertions.assertEquals(2, segment0.getData().getAngularCoordinates().size());
523         final TimeStampedAngularCoordinates ac0 = segment0.getData().getAngularCoordinates().get(0);
524         Assertions.assertEquals(0.0, ac0.getDate().durationFrom(refDate), 1.0e-5);
525         Assertions.assertEquals(0.0,
526                             Rotation.distance(new Rotation(RotationOrder.XYZ, RotationConvention.FRAME_TRANSFORM,
527                                                            FastMath.toRadians(45),
528                                                            FastMath.toRadians(0.9),
529                                                            FastMath.toRadians(15)),
530                                               ac0.getRotation()),
531                             1.0e-10);
532         final TimeStampedAngularCoordinates ac1 = segment0.getData().getAngularCoordinates().get(1);
533         Assertions.assertEquals(0.875, ac1.getDate().durationFrom(refDate), 1.0e-5);
534         Assertions.assertEquals(0.0,
535                             Rotation.distance(new Rotation(RotationOrder.XYZ, RotationConvention.FRAME_TRANSFORM,
536                                                            FastMath.toRadians(50),
537                                                            FastMath.toRadians(1.9),
538                                                            FastMath.toRadians(1.5)),
539                                               ac1.getRotation()),
540                             1.0e-10);
541 
542     }
543 
544     @Test
545     public void testParseAEM13() {
546         final TimeScale tai = TimeScalesFactory.getTAI();
547         final String ex = "/ccsds/adm/aem/AEMExample13.xml";
548         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
549         final AemParser parser  = new ParserBuilder().buildAemParser();
550         final Aem file = parser.parseMessage(source);
551         final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
552         Assertions.assertEquals(TimeSystem.TAI,          segment0.getMetadata().getTimeSystem());
553         Assertions.assertEquals("OREKIT SAT",            segment0.getMetadata().getObjectName());
554         Assertions.assertEquals("2020-012A",             segment0.getMetadata().getObjectID());
555         Assertions.assertEquals(OrbitRelativeFrame.LVLH, segment0.getMetadata().getEndpoints().getFrameA().asOrbitRelativeFrame());
556         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.IMU_FRAME, segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
557         Assertions.assertEquals("1", segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
558         Assertions.assertFalse(segment0.getMetadata().getEndpoints().isA2b());
559         Assertions.assertEquals(new AbsoluteDate("2021-04-15T13:31:20.000", tai), segment0.getMetadata().getStartTime());
560         Assertions.assertEquals(new AbsoluteDate("2021-04-15T13:31:23.000", tai), segment0.getMetadata().getStopTime());
561         Assertions.assertEquals(AttitudeType.QUATERNION_DERIVATIVE, segment0.getMetadata().getAttitudeType());
562         Assertions.assertTrue(segment0.getMetadata().isFirst());
563 
564         final AbsoluteDate refDate = new AbsoluteDate("2021-04-15T13:31:20.000", tai);
565 
566         Assertions.assertEquals(7, segment0.getData().getAngularCoordinates().size());
567         final TimeStampedAngularCoordinates ac0 = segment0.getData().getAngularCoordinates().get(0);
568         Assertions.assertEquals(0.0, ac0.getDate().durationFrom(refDate), 1.0e-5);
569         Assertions.assertEquals(0.0,
570                             Rotation.distance(new Rotation(-0.488615, -0.402157,  0.581628,  0.511111, true),
571                                               ac0.getRotation()),
572                             1.0e-10);
573         final TimeStampedAngularCoordinates ac1 = segment0.getData().getAngularCoordinates().get(1);
574         Assertions.assertEquals(0.5, ac1.getDate().durationFrom(refDate), 1.0e-5);
575         Assertions.assertEquals(0.0,
576                             Rotation.distance(new Rotation(-0.488765, -0.402027,  0.581486,  0.511231, true),
577                                               ac1.getRotation()),
578                             1.0e-10);
579         final TimeStampedAngularCoordinates ac2 = segment0.getData().getAngularCoordinates().get(2);
580         Assertions.assertEquals(1.0, ac2.getDate().durationFrom(refDate), 1.0e-5);
581         Assertions.assertEquals(0.0,
582                             Rotation.distance(new Rotation(-0.488916, -0.401898,  0.581344,  0.511350, true),
583                                               ac2.getRotation()),
584                             1.0e-10);
585 
586         final CircularOrbit o = new CircularOrbit(6992992, -5e-04, 1.2e-03,
587                                                   FastMath.toRadians(97.83), FastMath.toRadians(80.95),
588                                                   FastMath.toRadians(179.86), PositionAngleType.MEAN,
589                                                   FramesFactory.getEME2000(),
590                                                   new AbsoluteDate("2021-04-15T13:31:22.000", tai),
591                                                   Constants.EIGEN5C_EARTH_MU);
592         final FieldCircularOrbit<Binary64> fo =
593                         new FieldCircularOrbit<>(new Binary64(o.getA()),
594                                                  new Binary64(o.getCircularEx()), new Binary64(o.getCircularEy()),
595                                                  new Binary64(o.getI()), new Binary64(o.getRightAscensionOfAscendingNode()),
596                                                  new Binary64(o.getAlphaM()), PositionAngleType.MEAN,
597                                                  o.getFrame(), new FieldAbsoluteDate<>(Binary64Field.getInstance(), o.getDate()),
598                                                  new Binary64(o.getMu()));
599         final AemSatelliteEphemeris ephemeris = file.getSatellites().get("2020-012A");
600         final BoundedAttitudeProvider provider = ephemeris.getAttitudeProvider();
601         Attitude                  a = provider.getAttitude(o, o.getDate(), o.getFrame());
602         FieldAttitude<Binary64> fa = provider.getAttitude(fo, fo.getDate(), fo.getFrame());
603         Assertions.assertEquals(a.getRotation().getQ0(), fa.getRotation().getQ0().getReal(), 0.00001);
604         Assertions.assertEquals(a.getRotation().getQ1(), fa.getRotation().getQ1().getReal(), 0.00001);
605         Assertions.assertEquals(a.getRotation().getQ2(), fa.getRotation().getQ2().getReal(), 0.00001);
606         Assertions.assertEquals(a.getRotation().getQ3(), fa.getRotation().getQ3().getReal(), 0.00001);
607 
608     }
609 
610     @Test
611     public void testParseAEM14() {
612         final TimeScale tai = TimeScalesFactory.getTAI();
613         final String ex = "/ccsds/adm/aem/AEMExample14.txt";
614         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
615         final AemParser parser  = new ParserBuilder().buildAemParser();
616         final Aem file = parser.parseMessage(source);
617 
618         final Segment<AemMetadata, AemData> segment0 = file.getSegments().get(0);
619         Assertions.assertEquals(TimeSystem.TAI,          segment0.getMetadata().getTimeSystem());
620         Assertions.assertEquals("MMS",                   segment0.getMetadata().getObjectName());
621         Assertions.assertEquals("2015-011A",             segment0.getMetadata().getObjectID());
622         Assertions.assertEquals("EME2000",               segment0.getMetadata().getEndpoints().getFrameA().getName());
623         Assertions.assertEquals(SpacecraftBodyFrame.BaseEquipment.SC_BODY, segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getBaseEquipment());
624         Assertions.assertEquals("1", segment0.getMetadata().getEndpoints().getFrameB().asSpacecraftBodyFrame().getLabel());
625         Assertions.assertEquals(new AbsoluteDate("2023-01-01T00:00:00.000", tai), segment0.getMetadata().getStartTime());
626         Assertions.assertEquals(new AbsoluteDate("2023-01-01T00:04:30.000", tai), segment0.getMetadata().getStopTime());
627         Assertions.assertEquals(AttitudeType.EULER_ANGLE_DERIVATIVE, segment0.getMetadata().getAttitudeType());
628         Assertions.assertEquals(RotationOrder.ZXZ, segment0.getMetadata().getEulerRotSeq());
629         Assertions.assertEquals(10, segment0.getData().getAngularCoordinates().size());
630 
631         Assertions.assertEquals(AttitudeType.SPIN_NUTATION_MOMENTUM,
632                                 file.getSegments().get(1).getMetadata().getAttitudeType());
633         Assertions.assertEquals(AttitudeType.QUATERNION,
634                                 file.getSegments().get(2).getMetadata().getAttitudeType());
635         Assertions.assertEquals(AttitudeType.QUATERNION_ANGVEL,
636                                 file.getSegments().get(3).getMetadata().getAttitudeType());
637         Assertions.assertEquals(AttitudeType.EULER_ANGLE_ANGVEL,
638                                 file.getSegments().get(4).getMetadata().getAttitudeType());
639 
640     }
641 
642     @Test
643     public void testWrongNDMType() {
644         final String name = "/ccsds/odm/opm/OPMExample1.txt";
645         try {
646             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
647             new ParserBuilder().buildAemParser().parseMessage(source);
648             Assertions.fail("an exception should have been thrown");
649         } catch (OrekitException oe) {
650             Assertions.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT, oe.getSpecifier());
651             Assertions.assertEquals(name, oe.getParts()[0]);
652         }
653     }
654 
655     @Test
656     public void testNonExistentFile() throws URISyntaxException {
657         final String realName = getClass().getResource("/ccsds/adm/aem/AEMExample01.txt").toURI().getPath();
658         final String wrongName = realName + "xxxxx";
659         final DataSource source =  new DataSource(wrongName, () -> getClass().getResourceAsStream(wrongName));
660         try {
661             new ParserBuilder().buildAemParser().parseMessage(source);
662             Assertions.fail("an exception should have been thrown");
663         } catch (OrekitException oe) {
664             Assertions.assertEquals(OrekitMessages.UNABLE_TO_FIND_FILE, oe.getSpecifier());
665             Assertions.assertEquals(wrongName, oe.getParts()[0]);
666         }
667     }
668 
669     @Test
670     public void testMissingAttitudeType() {
671         try {
672             final String name = "/ccsds/adm/aem/AEM-missing-attitude-type.txt";
673             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
674             new ParserBuilder().buildAemParser().parseMessage(source);
675             Assertions.fail("an exception should have been thrown");
676         } catch (OrekitException oe) {
677             Assertions.assertEquals(OrekitMessages.UNINITIALIZED_VALUE_FOR_KEY, oe.getSpecifier());
678             Assertions.assertEquals(AemMetadataKey.ATTITUDE_TYPE.name(), oe.getParts()[0]);
679         }
680     }
681 
682     @Test
683     public void testInconsistentDirection() {
684         try {
685             final String name = "/ccsds/adm/aem/AEM-inconsistent-direction.txt";
686             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
687             new ParserBuilder().buildAemParser().parseMessage(source);
688             Assertions.fail("an exception should have been thrown");
689         } catch (OrekitException oe) {
690             Assertions.assertEquals(OrekitMessages.CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION, oe.getSpecifier());
691             Assertions.assertEquals(AemMetadataKey.ATTITUDE_DIR, oe.getParts()[0]);
692             Assertions.assertEquals(2.0, (Double) oe.getParts()[1], 1.0e-15);
693         }
694     }
695 
696     @Test
697     public void testInconsistentQuaternionType() {
698         try {
699             final String name = "/ccsds/adm/aem/AEM-inconsistent-quaternion-type.txt";
700             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
701             new ParserBuilder().buildAemParser().parseMessage(source);
702             Assertions.fail("an exception should have been thrown");
703         } catch (OrekitException oe) {
704             Assertions.assertEquals(OrekitMessages.CCSDS_KEYWORD_NOT_ALLOWED_IN_VERSION, oe.getSpecifier());
705             Assertions.assertEquals(AemMetadataKey.QUATERNION_TYPE, oe.getParts()[0]);
706             Assertions.assertEquals(2.0, (Double) oe.getParts()[1], 1.0e-15);
707         }
708     }
709 
710     @Test
711     public void testLowerCaseValue() {
712         //setup
713         String file = "/ccsds/adm/aem/aemLowerCaseValue.aem";
714         final DataSource source = new DataSource(file, () -> getClass().getResourceAsStream(file));
715 
716         //action
717         Aem actual = new ParserBuilder().buildAemParser().parseMessage(source);
718 
719         //verify
720         Assertions.assertEquals(
721                 CelestialBodyFactory.getEarth(),
722                 actual.getSegments().get(0).getMetadata().getCenter().getBody());
723     }
724 
725     @Test
726     public void testWrongFile() {
727         final String name = "/ccsds/odm/opm/OPMExample1.txt";
728         try {
729             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
730             new ParserBuilder().buildAemParser().parseMessage(source);
731             Assertions.fail("an exception should have been thrown");
732         } catch (OrekitException oe) {
733             Assertions.assertEquals(OrekitMessages.UNSUPPORTED_FILE_FORMAT, oe.getSpecifier());
734             Assertions.assertEquals(name, oe.getParts()[0]);
735         }
736     }
737 
738     @Test
739     public void testWrongKeyword() {
740         // simple test for AEM file, contains a wrong keyword in the metadata.
741         final String name = "/ccsds/adm/aem/AEM-wrong-keyword.txt";
742         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
743         try {
744             new ParserBuilder().buildAemParser().parseMessage(source);
745             Assertions.fail("an exception should have been thrown");
746         } catch (OrekitException oe) {
747             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
748             Assertions.assertEquals(24, ((Integer) oe.getParts()[0]).intValue());
749             Assertions.assertTrue(((String) oe.getParts()[2]).startsWith("WRONG_KEYWORD"));
750         }
751     }
752 
753     @Test
754     public void testEphemerisNumberFormatErrorType() {
755         final String name = "/ccsds/adm/aem/AEM-ephemeris-number-format-error.txt";
756         try {
757             final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
758             new ParserBuilder().buildAemParser().parseMessage(source);
759             Assertions.fail("an exception should have been thrown");
760         } catch (OrekitException oe) {
761             Assertions.assertEquals(OrekitMessages.UNABLE_TO_PARSE_LINE_IN_FILE, oe.getSpecifier());
762             Assertions.assertEquals(28, oe.getParts()[0]);
763             Assertions.assertEquals(name, oe.getParts()[1]);
764             Assertions.assertEquals("1996-11-28T22:08:03.5555 0.42319   this-is-not-a-number  0.23784   0.74533", oe.getParts()[2]);
765         }
766     }
767 
768     @Test
769     public void testKeywordWithinEphemeris() {
770         // simple test for AEM file, contains p/v entries and other mandatory data.
771         final String name = "/ccsds/adm/aem/AEM-keyword-within-ephemeris.txt";
772         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
773         try {
774             new ParserBuilder().buildAemParser().parseMessage(source);
775             Assertions.fail("an exception should have been thrown");
776         } catch (OrekitException oe) {
777             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
778             Assertions.assertEquals(29, ((Integer) oe.getParts()[0]).intValue());
779             Assertions.assertTrue(((String) oe.getParts()[2]).startsWith("USER_DEFINED_TEST_KEY"));
780         }
781     }
782 
783     @Test
784     public void testWrongRotationSequence() {
785         // simple test for AEM file, contains a wrong keyword in the metadata.
786         final String name = "/ccsds/adm/aem/AEM-inconsistent-rotation-sequence.txt";
787         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
788         try {
789             new ParserBuilder().buildAemParser().parseMessage(source);
790             Assertions.fail("an exception should have been thrown");
791         } catch (OrekitException oe) {
792             Assertions.assertEquals(OrekitMessages.CCSDS_INVALID_ROTATION_SEQUENCE, oe.getSpecifier());
793             Assertions.assertEquals("7051995", oe.getParts()[0]);
794             Assertions.assertEquals(22, ((Integer) oe.getParts()[1]).intValue());
795             Assertions.assertEquals(name, oe.getParts()[2]);
796         }
797     }
798 
799     @Test
800     public void testSpuriousMetaDataSection() {
801         final String name = "/ccsds/adm/aem/spurious-metadata.txt";
802         final DataSource source = new DataSource(name, () -> getClass().getResourceAsStream(name));
803         try {
804             new ParserBuilder().buildAemParser().parseMessage(source);
805             Assertions.fail("an exception should have been thrown");
806         } catch (OrekitException oe) {
807             Assertions.assertEquals(OrekitMessages.CCSDS_UNEXPECTED_KEYWORD, oe.getSpecifier());
808             Assertions.assertEquals(26, ((Integer) oe.getParts()[0]).intValue());
809             Assertions.assertEquals("META", oe.getParts()[2]);
810         }
811     }
812 
813     @Test
814     public void testMissingConvention() {
815         final String ex = "/ccsds/adm/aem/AEMExample01.txt";
816         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
817         final Aem file = new ParserBuilder().buildAemParser().parseMessage(source);
818         try {
819             file.getConventions();
820         } catch (OrekitException oe) {
821             Assertions.assertEquals(OrekitMessages.CCSDS_UNKNOWN_CONVENTIONS, oe.getSpecifier());
822         }
823     }
824 
825     private void verifyAngularCoordinates(final TimeStampedAngularCoordinates expected,
826                                           final TimeStampedAngularCoordinates actual,
827                                           final double threshold) {
828         // Verify date
829         Assertions.assertEquals(0.0, expected.getDate().durationFrom(actual.getDate()), threshold);
830 
831         // Verify Angular elements
832         Assertions.assertEquals(expected.getRotation().getQ0(), actual.getRotation().getQ0(), threshold);
833         Assertions.assertEquals(expected.getRotation().getQ1(), actual.getRotation().getQ1(), threshold);
834         Assertions.assertEquals(expected.getRotation().getQ2(), actual.getRotation().getQ2(), threshold);
835         Assertions.assertEquals(expected.getRotation().getQ3(), actual.getRotation().getQ3(), threshold);
836 
837         Assertions.assertEquals(0.0, expected.getRotationRate().distance(actual.getRotationRate()), threshold);
838     }
839 
840     /**
841      * Check if the parser enters the correct interpolation degree
842      * (the parsed one or the default if there is none)
843      */
844     @Test
845     public void testDefaultInterpolationDegree() {
846 
847         final String name = "/ccsds/adm/aem/AEMExample01.txt";
848         ParserBuilder builder = new ParserBuilder();
849 
850         final DataSource source1 = new DataSource(name, () -> getClass().getResourceAsStream(name));
851         final Aem file = builder.buildAemParser().parseMessage(source1);
852         Assertions.assertEquals(7, file.getSegments().get(0).getMetadata().getInterpolationDegree());
853         Assertions.assertEquals(1, file.getSegments().get(1).getMetadata().getInterpolationDegree());
854 
855         final DataSource source2 = new DataSource(name, () -> getClass().getResourceAsStream(name));
856         final Aem file2 = builder.withDefaultInterpolationDegree(5).buildAemParser().parseMessage(source2);
857         Assertions.assertEquals(7, file2.getSegments().get(0).getMetadata().getInterpolationDegree());
858         Assertions.assertEquals(5, file2.getSegments().get(1).getMetadata().getInterpolationDegree());
859     }
860 
861     @Test
862     public void testIssue739() {
863         final String ex = "/ccsds/adm/aem/AEMExample08.txt";
864         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
865         final Aem file = new ParserBuilder().buildAemParser().parseMessage(source);
866         final AemSegment segment0 = file.getSegments().get(0);
867         Assertions.assertEquals(CelestialBodyFrame.GTOD, segment0.getMetadata().getEndpoints().getFrameB().asCelestialBodyFrame());
868 
869         final BoundedAttitudeProvider provider = segment0.getAttitudeProvider();
870         Attitude attitude = provider.getAttitude(null, new AbsoluteDate("1996-11-28T22:08:03.555", TimeScalesFactory.getUTC()), null);
871         Rotation rotation = attitude.getRotation();
872         Assertions.assertEquals(0.42319,  rotation.getQ1(), 0.0001);
873         Assertions.assertEquals(-0.45697, rotation.getQ2(), 0.0001);
874         Assertions.assertEquals(0.23784,  rotation.getQ3(), 0.0001);
875         Assertions.assertEquals(0.74533,  rotation.getQ0(), 0.0001);
876         Assertions.assertEquals(0.0, provider.getMinDate().durationFrom(segment0.getMetadata().getStart()), 0.0001);
877         Assertions.assertEquals(0.0, provider.getMaxDate().durationFrom(segment0.getMetadata().getStop()), 0.0001);
878 
879     }
880 
881     @Test
882     public void testIssue739_2() {
883         final String ex = "/ccsds/adm/aem/AEMExample09.txt";
884         final DataSource source = new DataSource(ex, () -> getClass().getResourceAsStream(ex));
885         final Aem file = new ParserBuilder().buildAemParser().parseMessage(source);
886         final AemSegment segment0 = file.getSegments().get(0);
887         Assertions.assertEquals(FramesFactory.getITRF(ITRFVersion.ITRF_1993, IERSConventions.IERS_2010, true),
888                             segment0.getMetadata().getEndpoints().getFrameA().asFrame());
889 
890         final BoundedAttitudeProvider provider = segment0.getAttitudeProvider();
891         Attitude attitude = provider.getAttitude(null, new AbsoluteDate("1996-11-28T22:08:03.555", TimeScalesFactory.getUTC()), null);
892         Rotation rotation = attitude.getRotation();
893         Assertions.assertEquals(0.42319,  rotation.getQ1(), 0.0001);
894         Assertions.assertEquals(-0.45697, rotation.getQ2(), 0.0001);
895         Assertions.assertEquals(0.23784,  rotation.getQ3(), 0.0001);
896         Assertions.assertEquals(0.74533,  rotation.getQ0(), 0.0001);
897         Assertions.assertEquals(0.0, provider.getMinDate().durationFrom(segment0.getMetadata().getStart()), 0.0001);
898         Assertions.assertEquals(0.0, provider.getMaxDate().durationFrom(segment0.getMetadata().getStop()), 0.0001);
899 
900     }
901 
902 }